new project maker

This commit is contained in:
peterino2 2025-06-29 22:15:34 -07:00
parent b269188c72
commit 8458a6e026
15 changed files with 611 additions and 7 deletions

View File

@ -32,6 +32,7 @@ const engineDepList = [_][]const u8{
"imgui", "imgui",
"physics", "physics",
"ui", "ui",
"sys",
}; };
const BuildSystem = @This(); const BuildSystem = @This();
@ -267,6 +268,7 @@ pub const defaultEnabledModules: []const []const u8 = &.{
pub const moduleOrder: []const []const u8 = &.{ pub const moduleOrder: []const []const u8 = &.{
"core", "core",
"sys",
"assets", "assets",
"platform", "platform",
"physics", "physics",

View File

@ -10,6 +10,7 @@
.platform = .{ .path = "engine/platform" }, .platform = .{ .path = "engine/platform" },
.imgui = .{ .path = "engine/imgui" }, .imgui = .{ .path = "engine/imgui" },
.ui = .{ .path = "engine/ui" }, .ui = .{ .path = "engine/ui" },
.sys = .{ .path = "engine/sys" },
.rend = .{.path = "engine/rend" }, .rend = .{.path = "engine/rend" },
.SpirvReflect = .{ .path = "lib/spirv-reflect-zig" }, .SpirvReflect = .{ .path = "lib/spirv-reflect-zig" },

View File

@ -17,6 +17,7 @@ pub const ui = @import("ui").module;
pub const papyrus = @import("papyrus").module; pub const papyrus = @import("papyrus").module;
pub const physics = @import("physics").module; pub const physics = @import("physics").module;
pub const imgui = @import("imgui").module; pub const imgui = @import("imgui").module;
pub const sys = @import("sys").module;
const modulelist = @import("modulelist.zig").list; const modulelist = @import("modulelist.zig").list;

View File

@ -8,6 +8,7 @@ pub const list = [_][]const u8{
"rend", "rend",
"imgui", "imgui",
"ui", "ui",
"sys",
// to be implemented // to be implemented
// "graphics", // "graphics",
// "vkImgui", // "vkImgui",

View File

@ -32,6 +32,7 @@ pub fn build(b: *std.Build) void {
tests.root_module.addImport("sys", mod); tests.root_module.addImport("sys", mod);
const runArtifact = b.addRunArtifact(tests); const runArtifact = b.addRunArtifact(tests);
b.installArtifact(tests);
test_step.dependOn(&runArtifact.step); test_step.dependOn(&runArtifact.step);
b.installArtifact(tests); b.installArtifact(tests);
} }

View File

@ -20,6 +20,11 @@ pub const SystemTaskRunner = struct {
while (true) { while (true) {
// pump events // pump events
// if nothing pumped this frame, wait 100ms // if nothing pumped this frame, wait 100ms
if (self.jobs.items.len > 0) {
for (self.jobs.items) |j| {
_ = j;
}
}
} }
} }
}; };
@ -35,7 +40,9 @@ pub const SystemTaskRunner = struct {
} }
// check completion from the task thread // check completion from the task thread
pub fn checkCompletionTT(self: *@This()) bool { // returns true if
pub fn checkCompletionTT(self: *@This(), task: *SystemTask) bool {
_ = task;
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
_ = self; _ = self;
} }
@ -44,6 +51,8 @@ pub const SystemTaskRunner = struct {
pub fn destroy(self: *@This()) void { pub fn destroy(self: *@This()) void {
self.allocator.destroy(self); self.allocator.destroy(self);
} }
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "SystemTaskRunner");
}; };
pub const SystemTask = struct { pub const SystemTask = struct {
@ -51,6 +60,16 @@ pub const SystemTask = struct {
taskRunner: *SystemTaskRunner, taskRunner: *SystemTaskRunner,
}; };
pub const SystemTaskExecution = struct {
task: *SystemTask,
pub fn deinit(self: *@This()) void {
if (core.getEngineObject(SystemTaskRunner)) |taskRunner| {
try taskRunner.pushJobRemoval(self.task);
}
}
};
pub const ExecutionStatus = enum { pub const ExecutionStatus = enum {
ready, ready,
failed, failed,

View File

@ -1,12 +1,15 @@
const std = @import("std"); const std = @import("std");
pub const core = @import("core"); pub const core = @import("core");
pub const SubprocessTask = @import("systemProc.zig").SubprocessTask;
pub const runCommand = SubprocessTask.runCommand;
// TODO get rid of core.ModuleDescription // TODO get rid of core.ModuleDescription
// it's really not needed anymore with the // it's really not needed anymore with the
// new backlog API // new backlog API
pub const Module: core.ModuleDescription = .{ pub const Module: core.ModuleDescription = .{
.name = "sys", .name = "sys",
.enabledByDefault = true, .enabledByDefault = false,
}; };
// Module: sys // Module: sys

View File

@ -0,0 +1,131 @@
pub const std = @import("std");
pub const core = @import("core");
fn __debugPrint(comptime fmt: []const u8, args: anytype) void {
std.debug.print(fmt ++ "\n", args);
}
fn noprint(comptime fmt: []const u8, args: anytype) void {
_ = fmt;
_ = args;
}
const debugPrint = noprint;
pub const SubprocessTask = struct {
mutex: std.Thread.Mutex = .{},
allocator: std.mem.Allocator,
args: []const []const u8,
child: ?std.process.Child = null,
completed: bool = false,
success: bool = false,
workingDir: ?[]const u8 = null,
stdout: std.ArrayListUnmanaged(u8) = .{},
stderr: std.ArrayListUnmanaged(u8) = .{},
pub fn run(task: *@This()) !void {
const L = struct {
self: *SubprocessTask,
pub fn func(ctx: @This(), _: *core.JobContext) void {
const self = ctx.self;
self.mutex.lock();
debugPrint("running task", .{});
self.child = std.process.Child.init(self.args, self.allocator);
self.child.?.stdout_behavior = .Inherit;
self.child.?.stderr_behavior = .Inherit;
self.child.?.cwd = self.workingDir;
_ = self.child.?.spawn() catch {};
self.mutex.unlock();
// close and cleanup everything
self.waitInner() catch unreachable;
}
};
try core.dispatchJob(L{ .self = task });
}
pub fn create(allocator: std.mem.Allocator, argv: []const []const u8) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.child = null,
.allocator = allocator,
.args = argv,
};
return self;
}
pub fn destroy(self: *@This()) void {
self.mutex.lock();
if (self.child) |*child| {
_ = child;
debugPrint("destroying child process", .{});
}
self.mutex.unlock();
self.stdout.deinit(self.allocator);
self.stderr.deinit(self.allocator);
self.allocator.destroy(self);
}
pub fn checkComplete(self: *@This()) bool {
return self.completed;
}
pub fn wait(self: *@This()) void {
var completed: bool = self.completed;
while (completed == false) {
std.time.sleep(1 * 1000 * 1000);
self.mutex.lock();
completed = self.completed;
self.mutex.unlock();
}
debugPrint("task completed", .{});
}
pub fn waitInner(self: *@This()) !void {
self.mutex.lock();
if (self.child.?.stdout_behavior == .Pipe) {
try self.child.?.collectOutput(self.allocator, &self.stdout, &self.stderr, 150 * 1024 * 1024);
}
debugPrint("task completed stdout: {s}", .{self.stdout.items});
const term = try self.child.?.wait();
debugPrint("wait completed", .{});
self.mutex.unlock();
switch (term) {
.Exited => |m| {
if (m == 0) self.success = true;
},
.Signal => |m| {
core.engine_log("process Signaled {d}", .{m});
},
.Stopped => |m| {
core.engine_log("process Stopped {d}", .{m});
},
.Unknown => |m| {
core.engine_log("process Unknown {d}", .{m});
},
}
self.mutex.lock();
self.child = null;
self.completed = true;
self.mutex.unlock();
}
pub fn runCommand(allocator: std.mem.Allocator, argv: []const []const u8, cwd: ?[]const u8) !*@This() {
const task = try @This().create(allocator, argv);
task.workingDir = cwd;
try task.run();
return task;
}
};

View File

@ -13,10 +13,14 @@ test "testing threadrunner" {
try sys.start_module(&spec, .{}, std.testing.allocator); try sys.start_module(&spec, .{}, std.testing.allocator);
defer sys.shutdown_module(std.testing.allocator); defer sys.shutdown_module(std.testing.allocator);
const command = sys.runSystemCommand(&.{ "timeout", "/t", "3" }); const task = try sys.SubprocessTask.create(std.testing.allocator, &.{ "echo", "this is an echo: hello world\n" });
defer command.destroy(); defer task.destroy();
while (command.isComplete()) {} std.debug.print("beginning destroy\n", .{});
try task.run();
task.wait();
if (command.ensureCompleted()) {} const task2 = try sys.runCommand(std.testing.allocator, &.{ "echo", "sup homies" }, null);
defer task2.destroy();
task2.wait();
} }

View File

@ -8,6 +8,7 @@ const gpu = sdl.gpu;
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
runtime: *papyrus.PapyrusRuntime, runtime: *papyrus.PapyrusRuntime,
screenContext: *papyrus.Context, screenContext: *papyrus.Context,
screenBuffers: ScreenBuffers,
quadMesh: ?rend.IndexedMesh = null, quadMesh: ?rend.IndexedMesh = null,
quadMeshName: core.Name, quadMeshName: core.Name,
@ -19,6 +20,58 @@ first: bool = true,
rectPipeline: *gpu.GPUGraphicsPipeline = undefined, rectPipeline: *gpu.GPUGraphicsPipeline = undefined,
// textPipeline: *gpu.GPUGraphicsPipeline = undefined, // textPipeline: *gpu.GPUGraphicsPipeline = undefined,
tempDrawCommand: std.ArrayListUnmanaged(DrawCommand) = .{},
const DrawCommand = union(enum(u8)) {
rect: struct {
ssboIndex: u32,
},
text: struct {
ssboIndex: u32,
},
};
const SsboBuffer = struct {
buffer: *gpu.GPUBuffer,
staging: *gpu.GPUTransferBuffer,
pub fn init(device: *gpu.GPUDevice, elementSize: u32, count: u32) @This() {
var self: @This() = undefined;
{
const bci = gpu.GPUBufferCreateInfo{
.size = elementSize * count,
.usage = .{
.bufferusageGraphicsStorageRead = true,
},
};
self.buffer = device.createGPUBuffer(&bci);
}
{
const bci = gpu.GPUTransferBufferCreateInfo{
.usage = .transferbufferusageUpload,
.size = elementSize * count,
};
self.staging = device.createGPUTransferBuffer(&bci);
}
return self;
}
};
const ScreenBuffers = struct {
text: SsboBuffer,
rect: SsboBuffer,
pub fn init(device: *gpu.GPUDevice) @This() {
return .{
.rect = SsboBuffer.init(device, @sizeOf(rect_frag.Scene), 4096),
.text = undefined,
};
}
};
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "ui.runtime"); pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "ui.runtime");
pub fn create(allocator: std.mem.Allocator) !*@This() { pub fn create(allocator: std.mem.Allocator) !*@This() {
@ -32,6 +85,7 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
.stringArena = std.heap.ArenaAllocator.init(allocator), .stringArena = std.heap.ArenaAllocator.init(allocator),
.quadMeshName = core.MakeName("m_screenPlane"), .quadMeshName = core.MakeName("m_screenPlane"),
.drawList = papyrus.DrawList.init(allocator), .drawList = papyrus.DrawList.init(allocator),
.screenBuffers = undefined,
}; };
self.screenContext = try self.runtime.addContext(); self.screenContext = try self.runtime.addContext();
@ -83,6 +137,8 @@ pub fn createRectPipeline(self: *@This()) !void {
pci.rasterizer_state.fill_mode = .fillmodeFill; pci.rasterizer_state.fill_mode = .fillmodeFill;
self.rectPipeline = ctx.device.createGPUGraphicsPipeline(&pci); self.rectPipeline = ctx.device.createGPUGraphicsPipeline(&pci);
self.screenBuffers = ScreenBuffers.init(ctx.device);
} }
pub fn postRender(p: *anyopaque, cmd: *gpu.GPUCommandBuffer) void { pub fn postRender(p: *anyopaque, cmd: *gpu.GPUCommandBuffer) void {

View File

@ -23,6 +23,11 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me
// gets the main context // gets the main context
pub const context = core.EngineObject(PapyrusIntegration).get; pub const context = core.EngineObject(PapyrusIntegration).get;
pub fn getScreen() *papyrus.Context {
return context().screenContext;
}
pub fn shutdown_module(allocator: std.mem.Allocator) void { pub fn shutdown_module(allocator: std.mem.Allocator) void {
_ = allocator; _ = allocator;
} }
pub const ModernStyle = papyrus.ModernStyle;

View File

@ -50,6 +50,11 @@ pub fn build(b: *std.Build) void {
"Additionally install 'SDL_build_config.h' when installing SDL (default: false)", "Additionally install 'SDL_build_config.h' when installing SDL (default: false)",
) orelse false; ) orelse false;
if (target.result.abi.isAndroid()) {
// do the android build instead here... we dont use any of the other shit below
return;
}
var windows = false; var windows = false;
var linux = false; var linux = false;
var linux_deps_values: ?LinuxDepsValues = null; var linux_deps_values: ?LinuxDepsValues = null;

View File

@ -20,6 +20,7 @@ pub fn build(b: *std.Build) void {
sampleGame.setModuleEnabled("imgui", true); sampleGame.setModuleEnabled("imgui", true);
sampleGame.setModuleEnabled("audio", true); sampleGame.setModuleEnabled("audio", true);
sampleGame.setModuleEnabled("physics", true); sampleGame.setModuleEnabled("physics", true);
sampleGame.setModuleEnabled("ui", true);
sampleGame.addExtraModule("gameExtras"); sampleGame.addExtraModule("gameExtras");
sampleGame.addExtraModule("videoplayer"); sampleGame.addExtraModule("videoplayer");
sampleGame.addExtraModule("doomplayer"); sampleGame.addExtraModule("doomplayer");
@ -42,6 +43,7 @@ pub fn build(b: *std.Build) void {
newProjectMaker.setModuleEnabled("imgui", true); newProjectMaker.setModuleEnabled("imgui", true);
newProjectMaker.setModuleEnabled("audio", false); newProjectMaker.setModuleEnabled("audio", false);
newProjectMaker.setModuleEnabled("sys", true);
_ = newProjectMaker.compileInstall(); _ = newProjectMaker.compileInstall();
blbuild.addDependencyInstalls(b, .ReleaseFast); //.ReleaseFast); blbuild.addDependencyInstalls(b, .ReleaseFast); //.ReleaseFast);

View File

@ -92,6 +92,24 @@ pub const ExternGameObject = struct {
defer settings.release(); defer settings.release();
try physics.addShape("ph_small_box", settings); try physics.addShape("ph_small_box", settings);
{
const ctx = ui.getScreen();
const panel2 = try ctx.addPanel(.{});
{
ctx.getPanel(panel2).hasTitle = false;
ctx.get(panel2).anchor = .TopRight;
// ctx.get(panel2).fill = .FillY; todo, this is a bug. why.
ctx.get(panel2).pos = .{ .x = -105, .y = 1 };
ctx.get(panel2).size = .{ .x = 100, .y = 1 };
ctx.getPanel(panel2).titleColor = ui.ModernStyle.GreyDark;
// add some text to this panel
const text = try ctx.addText(panel2, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
ctx.get(text).pos = .{ .x = 32, .y = 32 };
ctx.get(text).size = .{ .x = 300, .y = 400 };
}
}
return self; return self;
} }
@ -229,6 +247,7 @@ const imgui = backlog.imgui;
const physics = backlog.physics; const physics = backlog.physics;
const rend = backlog.rend; const rend = backlog.rend;
const ig = imgui.api; const ig = imgui.api;
const ui = backlog.ui;
const platform = backlog.platform; const platform = backlog.platform;
const extras = @import("gameExtras"); const extras = @import("gameExtras");
const bsp = @import("bsp"); const bsp = @import("bsp");

View File

@ -1,13 +1,34 @@
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "main"); pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "main");
const GameContext = @This();
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
textBuffer: [8192]u8, textBuffer: [8192]u8,
commandQueue: core.RingQueue(Task),
activeCommand: ?*sys.SubprocessTask = null,
targetName: ?[]u8 = null,
targetDir: ?[]u8 = null,
engineRelative: ?[]u8 = null,
fingerprint: ?[]const u8 = null,
showInstructions: bool = false,
const Task = union(enum(u8)) {
subprocess: *sys.SubprocessTask,
func: *const fn (*GameContext) void,
};
pub fn init(allocator: std.mem.Allocator) !*@This() { pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This()); const self = try allocator.create(@This());
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
.textBuffer = std.mem.zeroes([8192]u8), .textBuffer = std.mem.zeroes([8192]u8),
.commandQueue = try core.RingQueue(Task).init(self.allocator, 4096),
.targetName = try allocator.dupe(u8, "sample"),
.engineRelative = try allocator.dupe(u8, "Backlog"),
}; };
return self; return self;
} }
@ -51,10 +72,339 @@ pub fn tick(self: *@This(), dt: f64) void {
} }
if (ig.button("Create", .{})) { if (ig.button("Create", .{})) {
core.engine_log("creating project: ", .{}); const asSlice = std.mem.span(@as([*c]const u8, @ptrCast(&self.textBuffer)));
core.engine_log("creating project: {s}", .{self.textBuffer});
self.createProject(asSlice) catch {};
} }
} }
ig.end(); ig.end();
if (self.activeCommand != null or self.commandQueue.count() > 0) {
ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{});
if (ig.begin("tasks", null, .{
.always_auto_resize = true,
.no_title_bar = true,
.no_move = true,
.no_resize = true,
.no_collapse = true,
})) {
ig.textf("outstanding tasks: {d}", .{self.commandQueue.count() + 1});
}
ig.end();
}
if (self.showInstructions) {
ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{});
ig.setNextWindowSize(.{ .x = 800, .y = 500 }, .{});
if (ig.begin("Instructions", null, .{
.always_auto_resize = true,
.no_move = true,
.no_resize = true,
.no_collapse = true,
})) {
ig.textf("project generated and git repo cloned, run zig build install and you might need to update the fingerprint", .{});
if (ig.button("close..", .{})) {
self.showInstructions = false;
}
}
ig.end();
}
self.tickTasks() catch {};
}
fn tickTasks(self: *@This()) !void {
if (self.activeCommand == null) {
if (self.commandQueue.popFromLocked()) |task| {
switch (task) {
.subprocess => |t| {
self.activeCommand = t;
core.engine_log("starting task {any}", .{t.args});
try t.run();
},
.func => |f| {
f(self);
},
}
}
}
if (self.activeCommand) |active| {
if (active.checkComplete()) {
active.destroy();
core.engine_logs("task complete");
self.activeCommand = null;
}
}
}
pub fn createProject(self: *@This(), targetDir: []const u8) !void {
self.targetDir = try self.allocator.dupe(u8, targetDir);
try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand(
self.allocator,
&.{ "zig", "init" },
targetDir,
) });
try self.commandQueue.pushLocked(.{ .func = updateBuildZig });
try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand(
self.allocator,
&.{ "git", "clone", "git@github.com:peterino2/BacklogEngine.git", "Backlog" },
targetDir,
) });
try self.commandQueue.pushLocked(.{ .func = displayInstructions });
}
fn loadFileAllocFromCwd(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) ![]u8 {
var file = try dir.openFile(path, .{});
defer file.close();
const filesize = (try file.stat()).size + 1; // add null byte
const buffer: []u8 = try allocator.alignedAlloc(u8, 8, filesize);
errdefer allocator.free(buffer);
try file.reader().readNoEof(buffer[0 .. buffer.len - 1]);
buffer[buffer.len - 1] = 0;
return buffer;
}
fn displayInstructions(self: *@This()) void {
self.showInstructions = true;
}
fn updateBuildZig(self: *@This()) void {
core.engine_logs("updating build.zig.zon");
var workingDir = std.fs.cwd().openDir(self.targetDir.?, .{}) catch return;
defer workingDir.close();
const buildZigZonPath = std.fmt.allocPrint(self.allocator, "{s}/build.zig.zon", .{self.targetDir.?}) catch return;
defer self.allocator.free(buildZigZonPath);
const buildZigContents = loadFileAllocFromCwd(self.allocator, workingDir, buildZigZonPath) catch return;
defer self.allocator.free(buildZigContents);
// walk forward until we find the fingerprint value
var window: []const u8 = buildZigContents[0..];
var offset: usize = 0;
while (offset < buildZigContents.len) : (offset += 1) {
window = buildZigContents[offset..buildZigContents.len];
if (std.mem.startsWith(u8, window, ".fingerprint")) {
// walk forward until i find the comma
var windowEnd: usize = offset;
while (windowEnd < buildZigContents.len) : (windowEnd += 1) {
if (buildZigContents[windowEnd] == ',') {
self.updateFingerPrint(buildZigContents[offset..windowEnd]);
self.writeBuildZigFiles() catch {};
return;
}
}
}
}
core.engine_logs("unable to update window");
}
fn writeBuildZigFiles(self: *@This()) !void {
// copy over the entire templates directory.
// 1. make a content folder
// 2. create src/main.zig
// 3. build.zig
// 4. build.zig.zon
var workingDir = try std.fs.cwd().openDir(self.targetDir.?, .{});
defer workingDir.close();
core.engine_logs("deliting files...");
try workingDir.deleteFile("src/root.zig");
try workingDir.deleteFile("src/main.zig");
try workingDir.deleteFile("build.zig");
try workingDir.deleteFile("build.zig.zon");
// 1. write src/main.zig
try self.writeMainZig(&workingDir);
try self.writeBuildZig(&workingDir);
try self.writeBuildZigZon(&workingDir);
}
fn writeBuildZig(self: *@This(), workingDir: *std.fs.Dir) !void {
const out_path = "build.zig";
var content = std.ArrayList(u8).init(self.allocator);
defer content.deinit();
var writer = content.writer();
const template =
\\ const std = @import("std");
\\ const Backlog = @import("Backlog");
\\
\\ pub fn build(b: *std.Build) void {{
\\ const target = b.standardTargetOptions(.{{}});
\\ const optimize = b.standardOptimizeOption(.{{}});
\\
\\ var blbuild = Backlog.init(b, .{{
\\ .target = target,
\\ .optimize = optimize,
\\ .backlogRoot = "{s}",
\\ }});
;
try writer.print(template, .{self.engineRelative.?});
const template2 =
\\ const program = blbuild.program(.{{
\\ .name = "{s}",
\\ .desc = "simple program built with backlog",
\\ .root_source_file = b.path("src/main.zig"),
\\ }});
\\
\\ program.setModuleEnabled("imgui", true);
\\
\\ _ = program.compileInstall();
\\ }}
;
try writer.print(template2, .{self.targetName.?});
try writer.writeByte(0);
try self.writeZigFile(workingDir, out_path, content.items);
}
const entries: []const struct { name: []const u8, path: []const u8 } = &.{
.{ .name = "SpirvReflect", .path = "lib/spirv-reflect-zig" },
.{ .name = "zphysics", .path = "lib/zphysics" },
.{ .name = "spng", .path = "lib/spng" },
.{ .name = "ozz", .path = "lib/ozz" },
.{ .name = "sdl3", .path = "lib/sdl3" },
.{ .name = "miniaudio", .path = "lib/miniaudio" },
.{ .name = "lua", .path = "lib/lua" },
};
fn writeBuildZigZon(self: *@This(), workingDir: *std.fs.Dir) !void {
var content = std.ArrayList(u8).init(self.allocator);
defer content.deinit();
var writer = content.writer();
try writer.print(".{{\n", .{});
try writer.print(
\\ .name = .MyProjects,
\\ .version = "0.0.0",
\\ .dependencies = .{{
\\ .Backlog = .{{ .path = "{s}" }},
, .{self.engineRelative.?});
for (entries) |entry| {
try writer.print(" .{s} = .{{ .path = \"{s}/{s}\" }},\n", .{ entry.name, self.engineRelative.?, entry.path });
}
try writer.print(
\\ }},
\\
, .{});
try writer.print(
\\ .paths = .{{ "" }},
\\
, .{});
try writer.print(
\\ .fingerprint = {s},
\\
, .{self.fingerprint.?});
try writer.print("}}", .{});
const file = try workingDir.createFile("build.zig.zon", .{});
defer file.close();
try file.writeAll(content.items);
}
fn writeMainZig(self: *@This(), workingDir: *std.fs.Dir) !void {
var content = std.ArrayList(u8).init(self.allocator);
defer content.deinit();
const template =
\\ pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "main");
\\
\\ allocator: std.mem.Allocator,
\\
\\ pub fn init(allocator: std.mem.Allocator) !*@This() {
\\ const self = try allocator.create(@This());
\\ self.* = .{
\\ .allocator = allocator,
\\ };
\\ return self;
\\ }
\\
\\ pub fn prepare(self: *@This()) !void {
\\ _ = self;
\\ core.engine_log("program ready", .{});
\\ }
\\
\\ pub fn tick(self: *@This(), dt: f64) void {
\\ _ = self;
\\ _ = dt;
\\ ig.showDemoWindow(null);
\\ }
\\
\\ pub fn deinit(self: *@This()) void {
\\ self.allocator.destroy(self);
\\ }
\\
\\ pub fn main() anyerror!void {
\\ var spec = try api.getSpec("
;
const template2 =
\\");
\\ _ = api.startEngine(&NeonObjectTable, &spec);
\\ }
\\
\\ const std = @import("std");
\\ const api = @import("backlog");
\\ const ig = api.imgui.api;
\\ const core = api.core;
\\
;
try content.appendSlice(template);
try content.appendSlice(self.targetName.?);
try content.appendSlice(template2);
try content.append(0);
const out_path = "src/main.zig";
std.debug.print("{s}", .{content.items});
try self.writeZigFile(workingDir, out_path, content.items);
}
fn writeZigFile(self: *@This(), workingDir: *std.fs.Dir, out_path: []const u8, contents: []const u8) !void {
var ast = try std.zig.Ast.parse(self.allocator, @as([:0]const u8, @ptrCast(contents[0 .. contents.len - 1])), .zig);
defer ast.deinit(self.allocator);
const out = try ast.render(self.allocator);
const file = try workingDir.createFile(out_path, .{});
defer file.close();
try file.writeAll(out);
}
fn updateFingerPrint(self: *@This(), zonEntry: []const u8) void {
// find the first number
var left: usize = 0;
while (zonEntry[left] != '0') : (left += 1) {}
core.engine_log(">> FINGERPRINT '{s}'", .{zonEntry[left .. zonEntry.len - 1]});
self.fingerprint = zonEntry[left .. zonEntry.len - 1];
} }
pub fn onFolderSelected( pub fn onFolderSelected(
@ -69,6 +419,9 @@ pub fn onFolderSelected(
} }
pub fn deinit(self: *@This()) void { pub fn deinit(self: *@This()) void {
if (self.targetDir) |t| {
self.allocator.free(t);
}
self.allocator.destroy(self); self.allocator.destroy(self);
} }
@ -81,3 +434,4 @@ const std = @import("std");
const api = @import("backlog"); const api = @import("backlog");
const ig = api.imgui.api; const ig = api.imgui.api;
const core = api.core; const core = api.core;
const sys = api.sys;