archiving a lot of things then moving to yet another redo of the build system

This commit is contained in:
peterino2 2025-11-28 20:22:51 -08:00
parent cc00fb076e
commit 8a30c78260
74 changed files with 753 additions and 744 deletions

662
archive/build.zig Normal file
View File

@ -0,0 +1,662 @@
// MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 -- use this if you're getting that odd crash on mac
b: *std.Build,
nw_builder: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
nw_mod: *std.Build.Module,
gltf2ozz: ozz.GltfToOzz,
options: *std.Build.Step.Options,
cookShaders: bool,
backlogRoot: []const u8,
// list of all shaders discovered under
// content/_shaders/def
reflectShaderPathList: [][]u8 = undefined,
staticBuild: bool = false,
nwdep: *std.Build.Dependency,
apigen: *std.Build.Step.Compile,
shaderEmbedGen: *std.Build.Step.Compile,
loadDynamicsGen: *std.Build.Step.Compile,
rcGen: *std.Build.Step.Compile,
generatedApis: std.StringHashMapUnmanaged(*std.Build.Module) = .{},
const engineDepList = [_][]const u8{
"assets",
"audio",
"core",
"net",
"papyrus",
"platform",
"rend",
"ui",
"imgui",
"physics",
"sys",
};
const BuildSystem = @This();
const std = @import("std");
const Build = std.Build;
const LazyPath = Build.LazyPath;
const ozz = @import("ozz");
pub const InitOptions = struct {
import_name: []const u8 = "Backlog",
backlogRoot: []const u8 = "./BacklogEngine",
staticBuild: bool = false,
target: Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
};
pub fn init(b: *std.Build, opts: InitOptions) BuildSystem {
var buildOpts = declareBuildOptions(b);
if (opts.target.result.os.tag == .linux) {
// using a static build for linux... object loading hell is not fun
std.debug.print("Important! only supporting static builds for linux", .{});
buildOpts.static_build = true;
}
const nwdep = b.dependency(opts.import_name, .{
.target = opts.target,
.optimize = opts.optimize,
.static_build = buildOpts.static_build,
});
var self = BuildSystem{
.b = b,
.nw_builder = nwdep.builder,
.target = opts.target,
.optimize = opts.optimize,
.nw_mod = nwdep.module("Backlog"),
.backlogRoot = opts.backlogRoot,
.options = createGameOptions(b, buildOpts),
.gltf2ozz = ozz.GltfToOzz.init(nwdep.builder, .{}),
.cookShaders = b.option(bool, "cookShaders", "generates shaders and updates .json files before running the build. (needs to be done whenever shaders are updated, this just runs tools/scripts/cook-shaders.py)") orelse false,
.staticBuild = buildOpts.static_build,
.nwdep = nwdep,
.apigen = nwdep.artifact("backlog-apigen"),
.loadDynamicsGen = nwdep.artifact("backlog-generate-loadDynamics"),
.rcGen = nwdep.artifact("backlog-generate-rc"),
.shaderEmbedGen = nwdep.artifact("backlog-shaderEmbedGen"),
};
const exeList = [1]*std.Build.Step.Compile{self.gltf2ozz.exe};
const install_tools = b.step("tools", "installs tools needed to generate outputs for the engine");
for (exeList) |exe| {
const toolsInstall = b.addInstallArtifact(exe, .{
.dest_dir = .{ .override = .{ .custom = "tools" } },
});
install_tools.dependOn(&toolsInstall.step);
}
{
const runArtifact = b.addRunArtifact(self.gltf2ozz.exe);
if (b.args) |args| {
runArtifact.addArgs(args);
}
const run_exe = b.step("gltf2ozz", "runs the gltf animation converter.");
run_exe.dependOn(&runArtifact.step);
}
self.addDependencyInstalls(self.b, .ReleaseFast);
return self;
}
pub const AddProgramOptions = struct {
name: []const u8,
desc: []const u8,
root_source_file: LazyPath,
imports: []const Build.Module.Import = &.{},
};
pub fn addProgram(self: *BuildSystem, opts: AddProgramOptions) *std.Build.Module {
const b = self.b;
const exe = self.nw_builder.addExecutable(.{
.name = opts.name,
.root_module = b.createModule(.{
.target = self.target,
.optimize = self.optimize,
.root_source_file = self.nw_builder.path("engine/main.zig"),
}),
});
b.installArtifact(exe);
const runArtifact = b.addRunArtifact(exe);
if (b.args) |args| {
runArtifact.addArgs(args);
}
const run_exe = b.step(self.b.fmt("run-{s}", .{opts.name}), opts.desc);
run_exe.dependOn(&runArtifact.step);
// main path = name/main.zig
const mod = b.addModule(opts.name, .{
.target = self.target,
.optimize = self.optimize,
.root_source_file = opts.root_source_file,
.imports = opts.imports,
});
exe.root_module.addImport("main", mod);
// todo.. remove this one and see what happens
exe.root_module.addImport("core", self.nwdep.module("core"));
// mod.addImport("Backlog", self.nw_mod);
exe.root_module.addOptions("BacklogOptions", self.options);
if (self.cookShaders) {
const cookShadersScript = b.fmt("{s}/tools/scripts/cookShaders.py", .{self.backlogRoot});
const cookShadersCommand = b.addSystemCommand(&[_][]const u8{"python"});
cookShadersCommand.addArg(cookShadersScript);
run_exe.dependOn(&cookShadersCommand.step);
}
b.getInstallStep().dependOn(self.nw_builder.getInstallStep());
// run_exe.dependOn(b.getInstallStep());
return mod;
}
pub const BuildOptions = struct {
mutex_job_queue: bool,
static_build: bool,
zero_logging: bool,
slow_logging: bool,
force_mailbox: bool,
};
pub fn declareBuildOptions(b: *std.Build) BuildOptions {
return .{
.mutex_job_queue = b.option(bool, "mutex_job_queue", "temporary test, reverts to old mutex based queue behaviour in jobs.zig:JobManager") orelse false,
.static_build = b.option(bool, "static_build", "builds the entire game as a single executable") orelse false,
.zero_logging = b.option(bool, "zero_logging", "disables all logging, only intended for use on job dispatch testing") orelse false,
.slow_logging = b.option(bool, "slow_logging", "Disables buffered logging, takes a hit to performance but gain timing information on logging") orelse false,
.force_mailbox = b.option(bool, "force_mailbox", "forces mailbox mode for present mode. unlocks framerate to irresponsible levels") orelse false,
};
}
pub fn createGameOptions(b: *std.Build, options: BuildOptions) *std.Build.Step.Options {
const opts = b.addOptions();
inline for (@typeInfo(BuildOptions).@"struct".fields) |field| {
opts.addOption(bool, field.name, @field(options, field.name));
}
return opts;
}
pub fn addExtraModule(self: *BuildSystem, mod: *std.Build.Module, moduleName: []const u8) void {
const dep = self.b.dependency(moduleName, .{ .target = self.target, .optimize = self.optimize, .static_build = self.staticBuild });
mod.addImport(moduleName, dep.module(moduleName));
}
pub fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
//if (self.staticBuild) {
//return;
// }
inline for (DynamicDepList) |d| {
b.installArtifact(b.dependency(
d.dep,
.{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
).artifact(d.artifact));
}
// b.installArtifact(b.dependency(
// "sdl3",
// .{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
// ).artifact("SDL3"));
}
const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } = &.{
.{ .dep = "sdl3", .artifact = "SDL3" },
.{ .dep = "spng", .artifact = "spng_c" },
.{ .dep = "lua", .artifact = "luac" },
.{ .dep = "miniaudio", .artifact = "miniaudio_c" },
.{ .dep = "zphysics", .artifact = "joltc" },
// .{ .dep = "enet", .artifact = "enet_c" },
.{ .dep = "ozz", .artifact = "ozz_cpp" },
};
// all other modules are disabled by default
pub const defaultEnabledModules: []const []const u8 = &.{
"core",
"assets",
"platform",
"rend",
};
pub const moduleOrder: []const []const u8 = &.{
"core",
"sys",
"assets",
"platform",
"net",
"physics",
"audio",
"rend",
"ui",
"imgui",
"papyrus",
};
pub const DynamicModule = struct {
name: []const u8,
allocator: std.mem.Allocator,
buildSystem: *BuildSystem,
programName: []const u8,
opts: AddProgramOptions,
extras: std.ArrayListUnmanaged(GameModule) = .{},
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
library: ?*std.Build.Step.Compile = null,
pub fn addExtraModule(self: *@This(), module: []const u8) void {
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
}
pub fn init(name: []const u8, p: *Program) *@This() {
var self: *@This() = p.allocator.create(@This()) catch @panic("out of memory");
self.* = .{
.opts = p.opts,
.programName = p.opts.name,
.buildSystem = p.buildSystem,
.allocator = p.allocator,
.name = name,
};
for (p.gameModules.items) |module| {
self.gameModules.append(self.allocator, module) catch @panic("out of memory");
}
for (p.extras.items) |module| {
self.extras.append(self.allocator, module) catch @panic("out of memory");
}
return self;
}
pub fn compileInstall(self: *@This()) *std.Build.Step.Compile {
if (self.library) |lib| {
return lib;
}
const b = self.buildSystem.b;
const static = self.buildSystem.staticBuild;
const lib = b.addLibrary(.{
.name = self.name,
.linkage = if (static) .static else .dynamic,
.root_module = b.createModule(.{
.root_source_file = self.opts.root_source_file,
.link_libc = true,
.optimize = self.buildSystem.optimize,
.target = self.buildSystem.target,
}),
});
const allocator = self.buildSystem.b.allocator;
var modlist = std.ArrayList([]const u8){};
for (self.gameModules.items) |mod| {
if (mod.enabled) {
modlist.append(allocator, mod.name) catch @panic("out of memory");
}
}
for (self.extras.items) |extra| {
self.buildSystem.addExtraModule(lib.root_module, extra.name);
}
lib.root_module.addImport("backlog", self.buildSystem.generateApi(self.name, modlist.items, null));
lib.root_module.addOptions("BacklogOptions", self.buildSystem.options);
if (static) {
// generateApi should create static callers for the main program
} else {
const installExtern = b.addInstallArtifact(lib, .{
.dest_dir = .{ .override = .{ .custom = "modules" } },
});
b.getInstallStep().dependOn(&installExtern.step);
}
return lib;
}
};
pub const GameModule = struct {
name: []const u8,
enabled: bool = true,
};
pub const Program = struct {
allocator: std.mem.Allocator,
opts: AddProgramOptions,
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
extras: std.ArrayListUnmanaged(GameModule) = .{},
dynamicModules: std.ArrayListUnmanaged(*DynamicModule) = .{},
buildSystem: *BuildSystem,
iconPath: ?std.Build.LazyPath = null,
pub fn setIconPath(self: *@This(), path: []const u8) void {
if (self.iconPath != null) {
return;
}
self.iconPath = self.buildSystem.generateRc(self.opts.name, path) catch return;
}
pub fn setModuleEnabled(self: *@This(), module: []const u8, enable: bool) void {
for (self.gameModules.items) |*m| {
if (std.mem.eql(u8, m.name, module)) {
m.enabled = enable;
return;
}
}
// module not found, add it here.
self.gameModules.append(self.allocator, .{ .name = module, .enabled = enable }) catch @panic("out of memory");
}
pub fn addExtraModule(self: *@This(), module: []const u8) void {
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
}
pub fn addDynamicModule(self: *@This(), module: []const u8, root_source_file: std.Build.LazyPath) *DynamicModule {
const dynamicModule = DynamicModule.init(module, self);
dynamicModule.opts.root_source_file = root_source_file;
self.dynamicModules.append(self.allocator, dynamicModule) catch @panic("out of memory");
return dynamicModule;
}
pub fn compileInstall(self: *@This()) *std.Build.Module {
const exe = self.buildSystem.addProgram(self.opts);
const allocator = self.buildSystem.b.allocator;
var modlist = std.ArrayList([]const u8){};
for (self.gameModules.items) |mod| {
if (mod.enabled) {
modlist.append(allocator, mod.name) catch @panic("out of memory");
}
}
for (self.extras.items) |extra| {
self.buildSystem.addExtraModule(exe, extra.name);
}
exe.addImport("backlog", self.buildSystem.generateApi(self.opts.name, modlist.items, self.dynamicModules.items));
if (self.buildSystem.target.result.os.tag == .windows) {
if (self.iconPath) |ip| {
exe.addWin32ResourceFile(.{
.file = ip,
.flags = &.{},
});
}
}
return exe;
}
};
pub fn program(self: *BuildSystem, opts: AddProgramOptions) *Program {
const prog = self.b.allocator.create(Program) catch unreachable;
prog.* = .{
.allocator = self.b.allocator,
.opts = opts,
.buildSystem = self,
};
for (moduleOrder) |module| {
prog.setModuleEnabled(module, false);
}
for (defaultEnabledModules) |module| {
prog.setModuleEnabled(module, true);
}
return prog;
}
// ========= standalone build instance =======
// maybe the default should be like an engine launcher/project launcher or something
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false;
const fwdGeneratorExe = b.addExecutable(.{
.name = "backlog-generate-fwd",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateFwd.zig"),
}),
});
const generateExe = b.addExecutable(.{
.name = "backlog-apigen",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateApi.zig"),
}),
});
const generateShaders = b.addExecutable(.{
.name = "backlog-shaderEmbedGen",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateEmbeddedShaders.zig"),
}),
});
const generateRcExe = b.addExecutable(.{
.name = "backlog-generate-rc",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateRc.zig"),
}),
});
const generateLoadDynamicsExe = b.addExecutable(.{
.name = "backlog-generate-loadDynamics",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateLoadDynamics.zig"),
}),
});
b.installArtifact(generateLoadDynamicsExe);
b.installArtifact(generateExe);
b.installArtifact(generateShaders);
b.installArtifact(generateRcExe);
{
const loadDynamicsStub = b.addModule("loadDynamicsStub", .{
.target = target,
.optimize = optimize,
.root_source_file = b.path("engine/loadDynamicsStub.zig"),
});
_ = loadDynamicsStub;
const mod = b.addModule("Backlog", .{
.target = target,
.optimize = optimize,
.root_source_file = b.path("engine/backlog.zig"),
});
for (engineDepList) |depName| {
const dep = b.dependency(
depName,
.{
.target = target,
.optimize = optimize,
.static_build = static_build,
},
);
const run = b.addRunArtifact(fwdGeneratorExe);
const output = run.addOutputFileArg(b.fmt("{s}_fwd.zig", .{depName}));
const modFwd = b.addModule(depName, .{
.target = target,
.optimize = optimize,
.root_source_file = output,
});
mod.addImport(b.fmt("{s}", .{depName}), modFwd);
modFwd.addImport("module", dep.module(depName));
// mod.addImport(depName, dep.module(depName));
}
// link in large platform support functions... special case.
{
const dep = b.dependency("sdl3", .{
.target = target,
.optimize = optimize,
.static_build = static_build,
});
const lib = dep.module("SDL3");
mod.addImport("sdl3_fwd", lib);
}
}
}
pub fn generateApi(self: *@This(), programName: []const u8, moduleList: []const []const u8, dynamicModules: ?[]const *DynamicModule) *std.Build.Module {
// std.debug.print("GENERATE API:: {s}\n", .{programName});
for (moduleList) |mod| {
_ = mod;
// std.debug.print("{s}\n", .{mod});
}
//
if (self.generatedApis.get(programName)) |m| {
return m;
}
const run = self.b.addRunArtifact(self.apigen);
const pfile = self.b.fmt("{s}Api.zig", .{programName});
const output = run.addOutputFileArg(pfile);
for (moduleList) |modName| {
run.addArg(modName);
}
const mod = self.b.addModule(pfile, .{
.target = self.target,
.optimize = self.optimize,
.root_source_file = output,
});
for (moduleList) |modName| {
mod.addImport(modName, self.nwdep.module(modName));
}
const loadStatics = self.generateInstallStaticResources(programName, "content/_shaders") catch unreachable;
mod.addImport("staticShaderLoader", loadStatics);
// deal with dynamics
if (dynamicModules) |dynamics| {
const loadDynamics = self.generateLoadDynamics(programName, dynamics) catch @panic("unknown");
mod.addImport("loadDynamics", loadDynamics);
} else {
mod.addImport("loadDynamics", self.nwdep.module("loadDynamicsStub"));
}
self.generatedApis.put(self.b.allocator, programName, mod) catch @panic("out of memory");
return mod;
}
pub fn generateRc(self: *@This(), programName: []const u8, iconPath: []const u8) !std.Build.LazyPath {
const run = self.b.addRunArtifact(self.rcGen);
const pfile = self.b.fmt("{s}.rc", .{programName});
const output = run.addOutputFileArg(pfile);
run.addArg(iconPath);
return output;
}
pub fn generateLoadDynamics(self: *@This(), programName: []const u8, dynamics: []const *DynamicModule) !*std.Build.Module {
const run = self.b.addRunArtifact(self.loadDynamicsGen);
const pfile = self.b.fmt("{s}LoadDynamics.zig", .{programName});
const output = run.addOutputFileArg(pfile);
if (self.staticBuild) {
run.addArg("static");
} else {
run.addArg("dynamic");
}
for (dynamics) |dyn| {
run.addArg(dyn.name);
}
const mod = self.b.addModule(pfile, .{
.target = self.target,
.optimize = self.optimize,
.root_source_file = output,
});
mod.addImport("core", self.nwdep.module("core"));
for (dynamics) |dyn| {
const dynmod = dyn.compileInstall();
mod.addImport(dyn.name, dynmod.root_module);
}
return mod;
}
pub fn generateInstallStaticResources(self: *@This(), programName: []const u8, shadersPath: []const u8) !*std.Build.Module {
const run = self.b.addRunArtifact(self.shaderEmbedGen);
const pfile = self.b.fmt("{s}ShaderGen.zig", .{programName});
const output = run.addOutputFileArg(pfile);
run.addArg(shadersPath);
const b = self.b;
const mod = self.b.addModule(pfile, .{
.target = self.target,
.optimize = self.optimize,
.root_source_file = output,
});
mod.addImport("core", self.nwdep.module("core"));
var dir = try std.fs.cwd().openDir("content/_shaders", .{ .iterate = true });
defer dir.close();
var walker = dir.iterate();
while (try walker.next()) |shaderType| {
if (shaderType.kind == .directory) {
var d2 = try dir.openDir(shaderType.name, .{ .iterate = true });
defer d2.close();
var w2 = d2.iterate();
while (try w2.next()) |shaderName| {
const shaderPath = b.fmt("content/_shaders/{s}/{s}", .{ shaderType.name, shaderName.name });
mod.addAnonymousImport(shaderName.name, .{
.root_source_file = b.path(shaderPath),
});
}
}
}
return mod;
}

686
build.zig
View File

@ -1,686 +0,0 @@
// MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 -- use this if you're getting that odd crash on mac
b: *std.Build,
nw_builder: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
nw_mod: *std.Build.Module,
gltf2ozz: ozz.GltfToOzz,
options: *std.Build.Step.Options,
cookShaders: bool,
backlogRoot: []const u8,
// list of all shaders discovered under
// content/_shaders/def
reflectShaderPathList: [][]u8 = undefined,
staticBuild: bool = false,
nwdep: *std.Build.Dependency,
apigen: *std.Build.Step.Compile,
shaderEmbedGen: *std.Build.Step.Compile,
loadDynamicsGen: *std.Build.Step.Compile,
rcGen: *std.Build.Step.Compile,
generatedApis: std.StringHashMapUnmanaged(*std.Build.Module) = .{},
const engineDepList = [_][]const u8{
"assets",
"audio",
"core",
"net",
"papyrus",
"platform",
"rend",
"ui",
"imgui",
"physics",
"sys",
};
const BuildSystem = @This();
const std = @import("std");
const Build = std.Build;
const LazyPath = Build.LazyPath;
const ozz = @import("ozz");
pub const InitOptions = struct {
import_name: []const u8 = "Backlog",
backlogRoot: []const u8 = "./BacklogEngine",
staticBuild: bool = false,
target: Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
};
pub fn init(b: *std.Build, opts: InitOptions) BuildSystem {
var buildOpts = declareBuildOptions(b);
if (opts.target.result.os.tag == .linux) {
// using a static build for linux... object loading hell is not fun
std.debug.print("Important! only supporting static builds for linux", .{});
buildOpts.static_build = true;
}
const nwdep = b.dependency(opts.import_name, .{
.target = opts.target,
.optimize = opts.optimize,
.static_build = buildOpts.static_build,
});
var self = BuildSystem{
.b = b,
.nw_builder = nwdep.builder,
.target = opts.target,
.optimize = opts.optimize,
.nw_mod = nwdep.module("Backlog"),
.backlogRoot = opts.backlogRoot,
.options = createGameOptions(b, buildOpts),
.gltf2ozz = ozz.GltfToOzz.init(nwdep.builder, .{}),
.cookShaders = b.option(bool, "cookShaders", "generates shaders and updates .json files before running the build. (needs to be done whenever shaders are updated, this just runs tools/scripts/cook-shaders.py)") orelse false,
.staticBuild = buildOpts.static_build,
.nwdep = nwdep,
.apigen = nwdep.artifact("backlog-apigen"),
.loadDynamicsGen = nwdep.artifact("backlog-generate-loadDynamics"),
.rcGen = nwdep.artifact("backlog-generate-rc"),
.shaderEmbedGen = nwdep.artifact("backlog-shaderEmbedGen"),
};
const exeList = [1]*std.Build.Step.Compile{self.gltf2ozz.exe};
const install_tools = b.step("tools", "installs tools needed to generate outputs for the engine");
for (exeList) |exe| {
const toolsInstall = b.addInstallArtifact(exe, .{
.dest_dir = .{ .override = .{ .custom = "tools" } },
});
install_tools.dependOn(&toolsInstall.step);
}
{
const runArtifact = b.addRunArtifact(self.gltf2ozz.exe);
if (b.args) |args| {
runArtifact.addArgs(args);
}
const run_exe = b.step("gltf2ozz", "runs the gltf animation converter.");
run_exe.dependOn(&runArtifact.step);
}
self.addDependencyInstalls(self.b, .ReleaseFast);
return self;
}
pub const AddProgramOptions = struct {
name: []const u8,
desc: []const u8,
root_source_file: LazyPath,
imports: []const Build.Module.Import = &.{},
};
pub fn addProgram(self: *BuildSystem, opts: AddProgramOptions) *std.Build.Module {
const b = self.b;
const exe = self.nw_builder.addExecutable(.{
.name = opts.name,
.root_module = b.createModule(.{
.target = self.target,
.optimize = self.optimize,
.root_source_file = self.nw_builder.path("engine/main.zig"),
}),
});
b.installArtifact(exe);
const runArtifact = b.addRunArtifact(exe);
if (b.args) |args| {
runArtifact.addArgs(args);
}
const run_exe = b.step(self.b.fmt("run-{s}", .{opts.name}), opts.desc);
run_exe.dependOn(&runArtifact.step);
// main path = name/main.zig
const mod = b.addModule(opts.name, .{
.target = self.target,
.optimize = self.optimize,
.root_source_file = opts.root_source_file,
.imports = opts.imports,
});
exe.root_module.addImport("main", mod);
// todo.. remove this one and see what happens
exe.root_module.addImport("core", self.nwdep.module("core"));
// mod.addImport("Backlog", self.nw_mod);
exe.root_module.addOptions("BacklogOptions", self.options);
if (self.cookShaders) {
const cookShadersScript = b.fmt("{s}/tools/scripts/cookShaders.py", .{self.backlogRoot});
const cookShadersCommand = b.addSystemCommand(&[_][]const u8{"python"});
cookShadersCommand.addArg(cookShadersScript);
run_exe.dependOn(&cookShadersCommand.step);
}
b.getInstallStep().dependOn(self.nw_builder.getInstallStep());
// run_exe.dependOn(b.getInstallStep());
return mod;
}
pub const BuildOptions = struct {
mutex_job_queue: bool,
static_build: bool,
zero_logging: bool,
slow_logging: bool,
force_mailbox: bool,
};
pub fn declareBuildOptions(b: *std.Build) BuildOptions {
return .{
.mutex_job_queue = b.option(bool, "mutex_job_queue", "temporary test, reverts to old mutex based queue behaviour in jobs.zig:JobManager") orelse false,
.static_build = b.option(bool, "static_build", "builds the entire game as a single executable") orelse false,
.zero_logging = b.option(bool, "zero_logging", "disables all logging, only intended for use on job dispatch testing") orelse false,
.slow_logging = b.option(bool, "slow_logging", "Disables buffered logging, takes a hit to performance but gain timing information on logging") orelse false,
.force_mailbox = b.option(bool, "force_mailbox", "forces mailbox mode for present mode. unlocks framerate to irresponsible levels") orelse false,
};
}
pub fn createGameOptions(b: *std.Build, options: BuildOptions) *std.Build.Step.Options {
const opts = b.addOptions();
inline for (@typeInfo(BuildOptions).@"struct".fields) |field| {
opts.addOption(bool, field.name, @field(options, field.name));
}
// build options for core.zig
// opts.addOption(
// bool,
// "mutex_job_queue",
// );
// opts.addOption(
// bool,
// "static_build",
// );
// opts.addOption(
// bool,
// "zero_logging",
// );
// opts.addOption(
// bool,
// "slow_logging",
// );
// opts.addOption(
// bool,
// "force_mailbox",
// );
return opts;
}
pub fn addExtraModule(self: *BuildSystem, mod: *std.Build.Module, moduleName: []const u8) void {
const dep = self.b.dependency(moduleName, .{ .target = self.target, .optimize = self.optimize, .static_build = self.staticBuild });
mod.addImport(moduleName, dep.module(moduleName));
}
pub fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
//if (self.staticBuild) {
//return;
// }
inline for (DynamicDepList) |d| {
b.installArtifact(b.dependency(
d.dep,
.{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
).artifact(d.artifact));
}
// b.installArtifact(b.dependency(
// "sdl3",
// .{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
// ).artifact("SDL3"));
}
const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } = &.{
.{ .dep = "sdl3", .artifact = "SDL3" },
.{ .dep = "spng", .artifact = "spng_c" },
.{ .dep = "lua", .artifact = "luac" },
.{ .dep = "miniaudio", .artifact = "miniaudio_c" },
.{ .dep = "zphysics", .artifact = "joltc" },
// .{ .dep = "enet", .artifact = "enet_c" },
.{ .dep = "ozz", .artifact = "ozz_cpp" },
};
// all other modules are disabled by default
pub const defaultEnabledModules: []const []const u8 = &.{
"core",
"assets",
"platform",
"rend",
};
pub const moduleOrder: []const []const u8 = &.{
"core",
"sys",
"assets",
"platform",
"net",
"physics",
"audio",
"rend",
"ui",
"imgui",
"papyrus",
};
pub const DynamicModule = struct {
name: []const u8,
allocator: std.mem.Allocator,
buildSystem: *BuildSystem,
programName: []const u8,
opts: AddProgramOptions,
extras: std.ArrayListUnmanaged(GameModule) = .{},
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
library: ?*std.Build.Step.Compile = null,
pub fn addExtraModule(self: *@This(), module: []const u8) void {
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
}
pub fn init(name: []const u8, p: *Program) *@This() {
var self: *@This() = p.allocator.create(@This()) catch @panic("out of memory");
self.* = .{
.opts = p.opts,
.programName = p.opts.name,
.buildSystem = p.buildSystem,
.allocator = p.allocator,
.name = name,
};
for (p.gameModules.items) |module| {
self.gameModules.append(self.allocator, module) catch @panic("out of memory");
}
for (p.extras.items) |module| {
self.extras.append(self.allocator, module) catch @panic("out of memory");
}
return self;
}
pub fn compileInstall(self: *@This()) *std.Build.Step.Compile {
if (self.library) |lib| {
return lib;
}
const b = self.buildSystem.b;
const static = self.buildSystem.staticBuild;
const lib = b.addLibrary(.{
.name = self.name,
.linkage = if (static) .static else .dynamic,
.root_module = b.createModule(.{
.root_source_file = self.opts.root_source_file,
.link_libc = true,
.optimize = self.buildSystem.optimize,
.target = self.buildSystem.target,
}),
});
const allocator = self.buildSystem.b.allocator;
var modlist = std.ArrayList([]const u8){};
for (self.gameModules.items) |mod| {
if (mod.enabled) {
modlist.append(allocator, mod.name) catch @panic("out of memory");
}
}
for (self.extras.items) |extra| {
self.buildSystem.addExtraModule(lib.root_module, extra.name);
}
lib.root_module.addImport("backlog", self.buildSystem.generateApi(self.name, modlist.items, null));
lib.root_module.addOptions("BacklogOptions", self.buildSystem.options);
if (static) {
// generateApi should create static callers for the main program
} else {
const installExtern = b.addInstallArtifact(lib, .{
.dest_dir = .{ .override = .{ .custom = "modules" } },
});
b.getInstallStep().dependOn(&installExtern.step);
}
return lib;
}
};
pub const GameModule = struct {
name: []const u8,
enabled: bool = true,
};
pub const Program = struct {
allocator: std.mem.Allocator,
opts: AddProgramOptions,
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
extras: std.ArrayListUnmanaged(GameModule) = .{},
dynamicModules: std.ArrayListUnmanaged(*DynamicModule) = .{},
buildSystem: *BuildSystem,
iconPath: ?std.Build.LazyPath = null,
pub fn setIconPath(self: *@This(), path: []const u8) void {
if (self.iconPath != null) {
return;
}
self.iconPath = self.buildSystem.generateRc(self.opts.name, path) catch return;
}
pub fn setModuleEnabled(self: *@This(), module: []const u8, enable: bool) void {
for (self.gameModules.items) |*m| {
if (std.mem.eql(u8, m.name, module)) {
m.enabled = enable;
return;
}
}
// module not found, add it here.
self.gameModules.append(self.allocator, .{ .name = module, .enabled = enable }) catch @panic("out of memory");
}
pub fn addExtraModule(self: *@This(), module: []const u8) void {
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
}
pub fn addDynamicModule(self: *@This(), module: []const u8, root_source_file: std.Build.LazyPath) *DynamicModule {
const dynamicModule = DynamicModule.init(module, self);
dynamicModule.opts.root_source_file = root_source_file;
self.dynamicModules.append(self.allocator, dynamicModule) catch @panic("out of memory");
return dynamicModule;
}
pub fn compileInstall(self: *@This()) *std.Build.Module {
const exe = self.buildSystem.addProgram(self.opts);
const allocator = self.buildSystem.b.allocator;
var modlist = std.ArrayList([]const u8){};
for (self.gameModules.items) |mod| {
if (mod.enabled) {
modlist.append(allocator, mod.name) catch @panic("out of memory");
}
}
for (self.extras.items) |extra| {
self.buildSystem.addExtraModule(exe, extra.name);
}
exe.addImport("backlog", self.buildSystem.generateApi(self.opts.name, modlist.items, self.dynamicModules.items));
if (self.buildSystem.target.result.os.tag == .windows) {
if (self.iconPath) |ip| {
exe.addWin32ResourceFile(.{
.file = ip,
.flags = &.{},
});
}
}
return exe;
}
};
pub fn program(self: *BuildSystem, opts: AddProgramOptions) *Program {
const prog = self.b.allocator.create(Program) catch unreachable;
prog.* = .{
.allocator = self.b.allocator,
.opts = opts,
.buildSystem = self,
};
for (moduleOrder) |module| {
prog.setModuleEnabled(module, false);
}
for (defaultEnabledModules) |module| {
prog.setModuleEnabled(module, true);
}
return prog;
}
// ========= standalone build instance =======
// maybe the default should be like an engine launcher/project launcher or something
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false;
const fwdGeneratorExe = b.addExecutable(.{
.name = "backlog-generate-fwd",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateFwd.zig"),
}),
});
const generateExe = b.addExecutable(.{
.name = "backlog-apigen",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateApi.zig"),
}),
});
const generateShaders = b.addExecutable(.{
.name = "backlog-shaderEmbedGen",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateEmbeddedShaders.zig"),
}),
});
const generateRcExe = b.addExecutable(.{
.name = "backlog-generate-rc",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateRc.zig"),
}),
});
const generateLoadDynamicsExe = b.addExecutable(.{
.name = "backlog-generate-loadDynamics",
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
.root_source_file = b.path("build/generateLoadDynamics.zig"),
}),
});
b.installArtifact(generateLoadDynamicsExe);
b.installArtifact(generateExe);
b.installArtifact(generateShaders);
b.installArtifact(generateRcExe);
{
const loadDynamicsStub = b.addModule("loadDynamicsStub", .{
.target = target,
.optimize = optimize,
.root_source_file = b.path("engine/loadDynamicsStub.zig"),
});
_ = loadDynamicsStub;
const mod = b.addModule("Backlog", .{
.target = target,
.optimize = optimize,
.root_source_file = b.path("engine/backlog.zig"),
});
for (engineDepList) |depName| {
const dep = b.dependency(
depName,
.{
.target = target,
.optimize = optimize,
.static_build = static_build,
},
);
const run = b.addRunArtifact(fwdGeneratorExe);
const output = run.addOutputFileArg(b.fmt("{s}_fwd.zig", .{depName}));
const modFwd = b.addModule(depName, .{
.target = target,
.optimize = optimize,
.root_source_file = output,
});
mod.addImport(b.fmt("{s}", .{depName}), modFwd);
modFwd.addImport("module", dep.module(depName));
// mod.addImport(depName, dep.module(depName));
}
// link in large platform support functions... special case.
{
const dep = b.dependency("sdl3", .{
.target = target,
.optimize = optimize,
.static_build = static_build,
});
const lib = dep.module("SDL3");
mod.addImport("sdl3_fwd", lib);
}
}
}
pub fn generateApi(self: *@This(), programName: []const u8, moduleList: []const []const u8, dynamicModules: ?[]const *DynamicModule) *std.Build.Module {
// std.debug.print("GENERATE API:: {s}\n", .{programName});
for (moduleList) |mod| {
_ = mod;
// std.debug.print("{s}\n", .{mod});
}
//
if (self.generatedApis.get(programName)) |m| {
return m;
}
const run = self.b.addRunArtifact(self.apigen);
const pfile = self.b.fmt("{s}Api.zig", .{programName});
const output = run.addOutputFileArg(pfile);
for (moduleList) |modName| {
run.addArg(modName);
}
const mod = self.b.addModule(pfile, .{
.target = self.target,
.optimize = self.optimize,
.root_source_file = output,
});
for (moduleList) |modName| {
mod.addImport(modName, self.nwdep.module(modName));
}
const loadStatics = self.generateInstallStaticResources(programName, "content/_shaders") catch unreachable;
mod.addImport("staticShaderLoader", loadStatics);
// deal with dynamics
if (dynamicModules) |dynamics| {
const loadDynamics = self.generateLoadDynamics(programName, dynamics) catch @panic("unknown");
mod.addImport("loadDynamics", loadDynamics);
} else {
mod.addImport("loadDynamics", self.nwdep.module("loadDynamicsStub"));
}
self.generatedApis.put(self.b.allocator, programName, mod) catch @panic("out of memory");
return mod;
}
pub fn generateRc(self: *@This(), programName: []const u8, iconPath: []const u8) !std.Build.LazyPath {
const run = self.b.addRunArtifact(self.rcGen);
const pfile = self.b.fmt("{s}.rc", .{programName});
const output = run.addOutputFileArg(pfile);
run.addArg(iconPath);
return output;
}
pub fn generateLoadDynamics(self: *@This(), programName: []const u8, dynamics: []const *DynamicModule) !*std.Build.Module {
const run = self.b.addRunArtifact(self.loadDynamicsGen);
const pfile = self.b.fmt("{s}LoadDynamics.zig", .{programName});
const output = run.addOutputFileArg(pfile);
if (self.staticBuild) {
run.addArg("static");
} else {
run.addArg("dynamic");
}
for (dynamics) |dyn| {
run.addArg(dyn.name);
}
const mod = self.b.addModule(pfile, .{
.target = self.target,
.optimize = self.optimize,
.root_source_file = output,
});
mod.addImport("core", self.nwdep.module("core"));
for (dynamics) |dyn| {
const dynmod = dyn.compileInstall();
mod.addImport(dyn.name, dynmod.root_module);
}
return mod;
}
pub fn generateInstallStaticResources(self: *@This(), programName: []const u8, shadersPath: []const u8) !*std.Build.Module {
const run = self.b.addRunArtifact(self.shaderEmbedGen);
const pfile = self.b.fmt("{s}ShaderGen.zig", .{programName});
const output = run.addOutputFileArg(pfile);
run.addArg(shadersPath);
const b = self.b;
const mod = self.b.addModule(pfile, .{
.target = self.target,
.optimize = self.optimize,
.root_source_file = output,
});
mod.addImport("core", self.nwdep.module("core"));
var dir = try std.fs.cwd().openDir("content/_shaders", .{ .iterate = true });
defer dir.close();
var walker = dir.iterate();
while (try walker.next()) |shaderType| {
if (shaderType.kind == .directory) {
var d2 = try dir.openDir(shaderType.name, .{ .iterate = true });
defer d2.close();
var w2 = d2.iterate();
while (try w2.next()) |shaderName| {
const shaderPath = b.fmt("content/_shaders/{s}/{s}", .{ shaderType.name, shaderName.name });
mod.addAnonymousImport(shaderName.name, .{
.root_source_file = b.path(shaderPath),
});
}
}
}
return mod;
}

51
engine/main2.zig Normal file
View File

@ -0,0 +1,51 @@
const std = @import("std");
const core = @import("core").module;
const panickers = core.panickers;
const realMain = @import("main");
pub const options = @import("BacklogOptions");
pub const std_options = std.Options{
.enable_segfault_handler = false,
};
//pub const build_options = @import("build_options");
//pub const tracy_enabled = build_options.tracy_enabled;
// pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, x: ?usize) noreturn {
// core.forceFlush();
// core.script.lua.takeDump();
// std.debug.defaultPanic(error_return_trace, x, msg);
// }
fn fileExists(path: []const u8) bool {
std.fs.cwd().access(path, .{}) catch return false;
return true;
}
pub fn main() !void {
if (!core.BuildOption("RootDeploymentOnly")) {
// if we don't see a content/ folder or a
// content.pak file in the current directory,
// walk up the directory tree until we see one, then change dirs to that before doing anything else
var iterations: u32 = 0;
var BUFFER: [8192]u8 = undefined;
while (iterations < 8) : (iterations += 1) {
if (fileExists("content") or fileExists("content.pak")) {
break;
}
var dir = try std.fs.cwd().openDir("..", .{});
defer dir.close();
core.engine_log("content not found scanning dir {s}", .{try dir.realpath(".", &BUFFER)});
try dir.setAsCwd();
}
core.engine_log("Working Dir set: {s}", .{try std.fs.cwd().realpath(".", &BUFFER)});
}
// panickers.attachSegfaultHandler();
try realMain.main();
}

View File

@ -13,8 +13,8 @@ pub fn build(b: *std.Build) void {
const sampleGame = blbuild.program(.{ const sampleGame = blbuild.program(.{
.name = "sampleGame", .name = "sampleGame",
.desc = "sdl3 project sample", .desc = "sample game with hot reloading",
.root_source_file = b.path("sampleGame/main.zig"), .root_source_file = b.path("sampleGame/sampleGame.zig"),
}); });
sampleGame.setModuleEnabled("imgui", true); sampleGame.setModuleEnabled("imgui", true);
@ -23,68 +23,14 @@ pub fn build(b: *std.Build) void {
sampleGame.setModuleEnabled("ui", true); sampleGame.setModuleEnabled("ui", true);
sampleGame.setModuleEnabled("sys", true); sampleGame.setModuleEnabled("sys", true);
sampleGame.setModuleEnabled("net", true); sampleGame.setModuleEnabled("net", true);
sampleGame.addExtraModule("gameExtras"); sampleGame.addExtraModule("gameExtras");
sampleGame.addExtraModule("videoplayer"); sampleGame.addExtraModule("videoplayer");
sampleGame.addExtraModule("doomplayer"); sampleGame.addExtraModule("doomplayer");
sampleGame.addExtraModule("bsp"); sampleGame.addExtraModule("bsp");
{ // extern game
const externGame = sampleGame.addDynamicModule("externGame", b.path("sampleGame/externGame/externGame.zig"));
_ = externGame.compileInstall();
}
sampleGame.setIconPath("icons/icon.ico"); sampleGame.setIconPath("icons/icon.ico");
const sampleGameExe = sampleGame.compileInstall(); const sampleGameExe = sampleGame.compileInstall();
_ = sampleGameExe; _ = sampleGameExe;
// tools
const newProjectMaker = blbuild.program(.{
.name = "newProject",
.desc = "new project maker",
.root_source_file = b.path("tools/newProjectMaker.zig"),
});
newProjectMaker.setModuleEnabled("imgui", true);
newProjectMaker.setModuleEnabled("audio", false);
newProjectMaker.setModuleEnabled("sys", true);
newProjectMaker.setIconPath("icons/NewProject.ico");
_ = newProjectMaker.compileInstall();
const toolbox = blbuild.program(.{
.name = "toolbox",
.desc = "graphical toolbox for random stuff",
.root_source_file = b.path("tools/toolbox.zig"),
});
toolbox.setModuleEnabled("imgui", true);
toolbox.setModuleEnabled("audio", false);
toolbox.setModuleEnabled("sys", true);
toolbox.setModuleEnabled("net", false);
toolbox.addExtraModule("gameExtras");
const toolboxExe = toolbox.compileInstall();
// Add Windows icon resource for toolbox
if (target.result.os.tag == .windows) {
toolboxExe.addWin32ResourceFile(.{
.file = b.path("tools/toolbox.rc"),
.flags = &.{},
});
}
const headless = blbuild.program(.{
.name = "headless",
.desc = "headless program, core-only",
.root_source_file = b.path("headless/main.zig"),
});
headless.setModuleEnabled("assets", false);
headless.setModuleEnabled("platform", false);
headless.setModuleEnabled("audio", false);
headless.setModuleEnabled("net", false);
headless.setModuleEnabled("rend", false);
_ = headless.compileInstall();
blbuild.addDependencyInstalls(b, .ReleaseFast); //.ReleaseFast);
} }

View File

@ -0,0 +1,31 @@
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "SampleGame");
allocator: std.mem.Allocator = undefined,
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{};
return self;
}
// called after the object has been reloaded
pub fn objectReload(self: *@This()) !void {
_ = self;
}
pub fn tick(self: *@This(), dt: f64) void {
_ = self;
_ = dt;
}
pub fn destroy(self: *@This()) void {
self.allocator.destroy(self);
}
pub fn start_module(args: core.ModuleLoaderArgs) !void {}
pub fn shutdown_module() void {}
const std = @import("std");
const backlog = @import("backlog");
const core = backlog.core;

5
todo.txt Normal file
View File

@ -0,0 +1,5 @@
how the fuck do i do this
1. change it so that the core executable only links core
actual issues we need to fix.