pub const core = @import("core"); pub const platform = @import("platform"); pub const assets = @import("assets"); pub const audio = @import("audio"); pub const graphics = @import("graphics"); pub const vkImgui = @import("vkImgui"); pub const ui = @import("ui"); pub const papyrus = @import("papyrus"); pub const physics = @import("physics"); const modulelist = @import("modulelist.zig").list; const std = @import("std"); pub const NwArgs = struct { useGPA: bool = true, // slower zig based memory allocator, provides detailed tracking of leaks and memory violations vulkanValidation: bool = true, fastTest: bool = false, dmt: bool = false, // detailed memory tracking, implements a timeline for tracking all memory allocations fatDump: bool = false, // takes a full fat minidump on crash, very large files are produced }; pub fn getArgs() !NwArgs { const a = try core.ParseArgs(NwArgs); return a; } // entry function for a backlog program // when this function is called it will use the settings specified by the program spec // to conditionally start up feature modules within the engine var shutdownList: std.ArrayListUnmanaged(*const fn (std.mem.Allocator) void) = .{}; pub fn start_modules(comptime programSpec: anytype, maybeArgs: ?NwArgs, allocator: std.mem.Allocator) !void { const Backlog = @This(); inline for (modulelist) |feature| { if (@hasDecl(Backlog, feature)) { const Struct = @field(Backlog, feature); if (comptime core.isModuleEnabled(Struct.Module, programSpec)) { if (maybeArgs) |args| { try Struct.start_module(programSpec, args, allocator); } else { try Struct.start_module(programSpec, NwArgs{}, allocator); } try shutdownList.append(allocator, Struct.shutdown_module); core.engine_logs("module started >>>> " ++ feature ++ " <<<<"); } } } } pub fn shutdown_modules(allocator: std.mem.Allocator) void { var i: isize = @intCast(shutdownList.items.len - 1); while (i >= 0) : (i -= 1) { shutdownList.items[@intCast(i)](allocator); } shutdownList.deinit(allocator); } pub fn start_everything(comptime spec: anytype, allocator: std.mem.Allocator, maybeArgs: ?NwArgs) !void { if (maybeArgs) |args| { if (args.vulkanValidation) graphics.setStartupSettings("vulkanValidation", true); } try start_modules(spec, maybeArgs, allocator); } pub fn shutdown_everything(allocator: std.mem.Allocator) void { shutdown_modules(allocator); } pub fn run_everything(comptime GameContext: type) !void { var canTick: bool = false; if (@hasDecl(GameContext, "tick")) { canTick = true; } var gameContext = try core.createObject(GameContext, .{ .can_tick = canTick }); if (@hasDecl(GameContext, "prepare_game")) { gameContext.prepare_game() catch @panic("Unable to run base level prepare script"); } else if (@hasDecl(GameContext, "prepare")) { gameContext.prepare() catch @panic("Unable to run base level prepare script"); } try core.gEngine.run(); while (!core.gEngine.exitFinished()) { const z = core.tracy.ZoneN(@src(), "shutdown poll"); platform.getInstance().pollEvents(); z.End(); } } pub fn initializeAndRunStandardProgram(comptime GameContext: type, comptime spec: anytype) !void { const args = try getArgs(); var backingAllocator: std.mem.Allocator = std.heap.c_allocator; var gpa: std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = 20, }) = .{}; defer { const cleanupStatus = gpa.deinit(); if (cleanupStatus == .leak) { std.debug.print("gpa cleanup leaked memory\n", .{}); } } if (args.useGPA) { backingAllocator = gpa.allocator(); } const memory = core.MemoryTracker; memory.MTSetup(backingAllocator, .{ .timeline = args.dmt }); defer memory.MTShutdown(); var tracker = memory.MTGet().?; const allocator = tracker.allocator(); if (args.vulkanValidation) { core.engine_logs("Using vulkan validation"); } graphics.setStartupSettings("vulkanValidation", args.vulkanValidation); if (@hasField(@TypeOf(spec), "windowName")) { platform.setWindowSettings(.{ .windowName = spec.windowName }); } else { platform.setWindowSettings(.{ .windowName = spec.name }); } try start_everything(spec, allocator, args); defer shutdown_everything(allocator); try run_everything(GameContext); }