438 lines
13 KiB
Zig
438 lines
13 KiB
Zig
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "main");
|
|
|
|
const GameContext = @This();
|
|
|
|
allocator: std.mem.Allocator,
|
|
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() {
|
|
const self = try allocator.create(@This());
|
|
self.* = .{
|
|
.allocator = allocator,
|
|
.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;
|
|
}
|
|
|
|
pub fn prepare(self: *@This()) !void {
|
|
_ = self;
|
|
core.engine_log("program ready", .{});
|
|
}
|
|
|
|
pub fn tick(self: *@This(), dt: f64) void {
|
|
_ = dt;
|
|
|
|
ig.setNextWindowPos(.{ .x = 200, .y = 200 }, .{}, .{});
|
|
|
|
if (ig.begin("New Project...", null, .{
|
|
.always_auto_resize = true,
|
|
.no_move = true,
|
|
.no_resize = true,
|
|
.no_collapse = true,
|
|
})) {
|
|
ig.setNextItemWidth(600);
|
|
if (ig.inputText(
|
|
"##Path",
|
|
&self.textBuffer,
|
|
self.textBuffer.len,
|
|
.{ .no_blank = true },
|
|
null,
|
|
null,
|
|
)) {
|
|
core.engine_log("huh", .{});
|
|
}
|
|
|
|
ig.sameLine(0, 3);
|
|
|
|
if (ig.button("...", .{})) {
|
|
core.engine_log("opening NFD", .{});
|
|
core.asyncOpenFolder(.{
|
|
.callback = onFolderSelected,
|
|
.callbackContext = self,
|
|
}) catch @panic(" unable to open dialog");
|
|
}
|
|
|
|
if (ig.button("Create", .{})) {
|
|
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();
|
|
|
|
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(
|
|
ctx: ?*anyopaque,
|
|
path: ?[]const u8,
|
|
) void {
|
|
const self: *@This() = @ptrCast(@alignCast(ctx));
|
|
if (path) |p| {
|
|
std.mem.copyForwards(u8, &self.textBuffer, p);
|
|
self.textBuffer[p.len] = 0;
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
if (self.targetDir) |t| {
|
|
self.allocator.free(t);
|
|
}
|
|
self.allocator.destroy(self);
|
|
}
|
|
|
|
pub fn main() anyerror!void {
|
|
var spec = try api.getSpec("New Project Dialogue");
|
|
_ = api.startEngine(&NeonObjectTable, &spec);
|
|
}
|
|
|
|
const std = @import("std");
|
|
const api = @import("backlog");
|
|
const ig = api.imgui.api;
|
|
const core = api.core;
|
|
const sys = api.sys;
|