diff --git a/build.zig b/build.zig index e0df537..68a27d6 100644 --- a/build.zig +++ b/build.zig @@ -21,7 +21,7 @@ pub fn build(b: *std.Build) !void { .has_tracy = b.option(bool, "tracy", "Tracy integration"), .win32_has_console = b.option(bool, "console", "Show console (Window only)"), .win32_png_icon = b.path("src/assets/icon.png"), - .code_hot_reload = b.option(bool, "code-hot-reload", "Dynamically load game code at runtime"), + .code_dynamic_linking = b.option(bool, "code-hot-reload", "Dynamically load game code at runtime"), .asset_hot_reload = b.option(bool, "asset-hot-reload", "Load assets at runtime"), .asset_dir = b.path("src/assets/"), }); diff --git a/engine/build.zig b/engine/build.zig index 4032015..4eb1425 100644 --- a/engine/build.zig +++ b/engine/build.zig @@ -14,8 +14,8 @@ const InitOptions = struct { dep_engine: *std.Build.Dependency, - statically_linked: ?bool = null, - code_hot_reload: ?bool = null, + code_static_linking: ?bool = null, + code_dynamic_linking: ?bool = null, has_imgui: ?bool = null, has_tracy: ?bool = null, win32_has_console: ?bool = null, @@ -31,22 +31,22 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void { const isDebug = (opts.optimize == .Debug); var has_tracy = false; - var code_hot_reload = false; + var code_dynamic_linking = false; var asset_hot_reload = false; if (!isWasm) { - code_hot_reload = opts.code_hot_reload orelse isDebug; + code_dynamic_linking = opts.code_dynamic_linking orelse isDebug; has_tracy = opts.has_tracy orelse isDebug; asset_hot_reload = opts.asset_hot_reload orelse isDebug; } const has_imgui = opts.has_imgui orelse isDebug; - const statically_linked = opts.statically_linked orelse true; + const code_static_linking = opts.code_static_linking orelse true; var build_options = b.addOptions(); build_options.addOption(bool, "has_imgui", has_imgui); build_options.addOption(bool, "has_tracy", has_tracy); - build_options.addOption(bool, "code_hot_reload", code_hot_reload); - build_options.addOption(bool, "statically_linked", statically_linked); + build_options.addOption(bool, "code_dynamic_linking", code_dynamic_linking); + build_options.addOption(bool, "code_static_linking", code_static_linking); build_options.addOption(bool, "asset_hot_reload", asset_hot_reload); if (asset_hot_reload) { build_options.addOptionPath("asset_dir", opts.asset_dir); @@ -69,7 +69,7 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void { .has_imgui = has_imgui }); engine_module.root_module.addImport("engine_lib", engine_lib); - if (statically_linked) { + if (code_static_linking) { engine_module.root_module.addImport("game", opts.root_module); } @@ -130,7 +130,7 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void { if (outer_builder.args) |args| { run_cmd.addArgs(args); } - if (code_hot_reload) { + if (code_dynamic_linking) { opts.root_module.resolved_target = opts.target; opts.root_module.optimize = opts.optimize; const game_lib = b.addLibrary(.{ diff --git a/engine/src/engine/debug_system.zig b/engine/src/engine/debug_system.zig new file mode 100644 index 0000000..1eca835 --- /dev/null +++ b/engine/src/engine/debug_system.zig @@ -0,0 +1,66 @@ +const std = @import("std"); +const Environment = @import("./environment.zig"); +const GameLinking = @import("./game_linking.zig"); +const Input = @import("./input.zig"); + +const DebugSystem = @This(); + +allocator: std.mem.Allocator, + +is_mouse_inside_imgui: bool, + +show_debug: bool, +restart_game: bool, +recording: ?Recording, + +play_recording: bool, +recording_tick: u64, + +dynamic_linking: ?GameLinking.Dynamic, + +const Recording = struct { + arena: std.heap.ArenaAllocator, + + initial: Environment.Init, + ticks: std.ArrayList(Environment.Tick), + + pub fn init(gpa: std.mem.Allocator, initial: Environment.Init) Recording { + return Recording{ + .arena = .init(gpa), + .ticks = .empty, + .initial = initial + }; + } + + pub fn appendTick(self: *Recording, tick: Environment.Tick) !void { + const arena = self.arena.allocator(); + + try self.ticks.append(arena, .{ + .dt_ns = tick.dt_ns, + .events = try arena.dupe(Input.Event, tick.events) + }); + } + + pub fn deinit(self: Recording) void { + self.arena.deinit(); + } +}; + +pub fn init(allocator: std.mem.Allocator) DebugSystem { + return DebugSystem{ + .allocator = allocator, + .is_mouse_inside_imgui = false, + .show_debug = false, + .restart_game = false, + .recording = null, + .play_recording = false, + .recording_tick = 0, + .dynamic_linking = null + }; +} + +pub fn deinit(self: *DebugSystem) void { + if (self.dynamic_linking) |*dynamic| { + dynamic.deinit(self.allocator); + } +} diff --git a/engine/src/engine/environment.zig b/engine/src/engine/environment.zig new file mode 100644 index 0000000..328c188 --- /dev/null +++ b/engine/src/engine/environment.zig @@ -0,0 +1,205 @@ +const std = @import("std"); +const log = std.log.scoped(.engine); +const assert = std.debug.assert; + +const Input = @import("./input.zig"); + +const ScreenScalar = @import("./screen_scaler.zig"); + +pub const Lib = @import("engine_lib"); +const GameCallbacks = Lib.Callbacks; + +const Math = Lib.Math; +const Vec2 = Math.Vec2; +const rgb = Math.rgb; +const Vec4 = Math.Vec4; + +const Environment = @This(); + +gpa: std.mem.Allocator, + +callbacks: GameCallbacks, +state: ?GameCallbacks.State, +frame: ?Lib.Frame, + +mouse_position: ?Vec2, +started_at: std.time.Instant, +last_frame_at: Lib.Nanoseconds, + +pub const Init = struct { + screen_size: Vec2, + seed: u64 +}; + +pub const Tick = struct { + dt_ns: u64, + events: []Input.Event, +}; + +const TickResult = struct { + audio: []Lib.AudioCommand, + graphics: []Lib.GraphicsCommand, + clear_color: Vec4, + cursor_hidden: bool +}; + +pub fn init(gpa: std.mem.Allocator, callbacks: GameCallbacks) Environment { + return Environment{ + .gpa = gpa, + .mouse_position = null, + .started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"), + .last_frame_at = 0, + .state = null, + .frame = null, + .callbacks = callbacks + }; +} + +pub fn deinit(self: *Environment) void { + if (self.frame) |*frame| { + frame.deinit(); + } +} + +fn processEvent(self: *Environment, event: Input.Event) void { + assert(self.frame != null); + const frame = &self.frame.?; + + switch (event) { + .key_pressed => |opts| { + if (!opts.repeat) { + frame.keyboard.press(opts.code, frame.time_ns); + } + }, + .key_released => |key_code| { + frame.keyboard.release(key_code); + }, + .mouse_leave => { + frame.keyboard.releaseAll(); + + assert(self.mouse_position != null); + self.mouse_position = null; + frame.mouse_button = .empty; + }, + .mouse_enter => |pos| { + assert(self.mouse_position == null); + self.mouse_position = pos; + }, + .mouse_move => |pos| { + assert(self.mouse_position != null); + self.mouse_position = pos; + }, + .mouse_pressed => |opts| { + assert(self.mouse_position != null); + self.mouse_position = opts.position; + frame.mouse_button.press(opts.button, frame.time_ns); + }, + .mouse_released => |opts| { + assert(self.mouse_position != null); + self.mouse_position = opts.position; + frame.mouse_button.release(opts.button); + }, + .window_resize => |opts| { + frame.screen_size = .initFromInt(u32, opts.width, opts.height); + }, + else => {} + } +} + +pub fn stateInit(self: *Environment, initial: Init) void { + assert(self.frame == null); + const opts = Lib.Init{ + .gpa = self.gpa, + .seed = initial.seed, + }; + self.state = self.callbacks.init(&opts); + + self.frame = .init(self.gpa, initial.screen_size); +} + +pub fn stateDeinit(self: *Environment) void { + assert(self.state != null); + self.callbacks.deinit(self.state.?); + self.state = null; +} + +pub fn stateTick(self: *Environment, opts: Environment.Tick) !TickResult { + assert(self.frame != null); + const frame = &self.frame.?; + + _ = frame.arena.reset(.retain_capacity); + const arena = frame.arena.allocator(); + + const audio_commands_capacity = frame.audio_commands.capacity; + frame.audio_commands = .empty; + try frame.audio_commands.ensureTotalCapacity(arena, audio_commands_capacity); + + const graphics_commands_capacity = frame.graphics_commands.capacity; + frame.graphics_commands = .empty; + try frame.graphics_commands.ensureTotalCapacity(arena, graphics_commands_capacity); + + frame.dt_ns = opts.dt_ns; + frame.time_ns += opts.dt_ns; + + for (opts.events) |event| { + if (self.mouse_position == null) { + if (event == .mouse_leave) { + continue; + } + + const enter_position = switch(event) { + .mouse_pressed => |e_opts| e_opts.position, + .mouse_released => |e_opts| e_opts.position, + .mouse_move => |pos| pos, + else => null + }; + + if (enter_position) |pos| { + self.processEvent(.{ .mouse_enter = pos }); + } + } else { + if (event == .mouse_enter) { + continue; + } + } + + self.processEvent(event); + } + + frame.mouse_position = self.mouse_position; + + var maybe_screen_scaler: ?ScreenScalar = null; + if (frame.canvas_size) |canvas_size| { + const screen_scaler = ScreenScalar.init(frame.screen_size, canvas_size); + maybe_screen_scaler = screen_scaler; + screen_scaler.push(frame); + + if (frame.mouse_position) |mouse_position| { + frame.mouse_position = mouse_position.sub(screen_scaler.translation).divideScalar(screen_scaler.scale); + } + } + + assert(self.state != null); + self.callbacks.tick(self.state.?, frame); + + if (maybe_screen_scaler) |screen_scaler| { + screen_scaler.pop(frame, frame.clear_color); + } + + frame.keyboard.pressed = .initEmpty(); + frame.keyboard.released = .initEmpty(); + frame.mouse_button.pressed = .initEmpty(); + frame.mouse_button.released = .initEmpty(); + + return TickResult{ + .graphics = frame.graphics_commands.items, + .audio = frame.audio_commands.items, + .clear_color = frame.clear_color, + .cursor_hidden = frame.hide_cursor + }; +} + +pub fn stateDebug(self: *Environment, imgui_ctx: *Lib.ImGui) void { + assert(self.state != null); + self.callbacks.debug(self.state.?, imgui_ctx); +} diff --git a/engine/src/engine/game_linking.zig b/engine/src/engine/game_linking.zig index e85533a..e2edd16 100644 --- a/engine/src/engine/game_linking.zig +++ b/engine/src/engine/game_linking.zig @@ -8,13 +8,7 @@ const build_options = Lib.build_options; const GameLinking = @This(); -kind: union(enum) { - static, - dynamic: KindDynamic, -}, -callbacks: GameCallbacks, - -const KindDynamic = struct { +pub const Dynamic = struct { fan_fd: std.os.linux.fd_t, path: []const u8, dir_path: [:0]const u8, @@ -39,7 +33,7 @@ const KindDynamic = struct { }; } - fn init(gpa: std.mem.Allocator, path: []const u8) !KindDynamic { + pub fn init(gpa: std.mem.Allocator, path: []const u8) !Dynamic { const dir_path_z = try gpa.dupeZ(u8, std.fs.path.dirname(path) orelse "."); errdefer gpa.free(dir_path_z); @@ -77,7 +71,7 @@ const KindDynamic = struct { const lib = try std.DynLib.open(path); - return KindDynamic{ + return Dynamic{ .fan_fd = fan_fd, .dir_path = dir_path_z, .path = path_dupe, @@ -88,18 +82,18 @@ const KindDynamic = struct { }; } - fn deinit(self: *KindDynamic, gpa: std.mem.Allocator) void { + pub fn deinit(self: *Dynamic, gpa: std.mem.Allocator) void { self.lib.close(); gpa.free(self.dir_path); gpa.free(self.path); std.posix.close(self.fan_fd); } - fn getCallbacks(self: *KindDynamic) !GameCallbacks { + pub fn getCallbacks(self: *Dynamic) !GameCallbacks { return getCallbacksFromLibrary(&self.lib); } - fn checkForChanges(self: *KindDynamic) !?GameCallbacks { + pub fn checkForChanges(self: *Dynamic) !bool { const fanotify = std.os.linux.fanotify; const M = fanotify.event_metadata; const fan_fd = self.fan_fd; @@ -187,6 +181,8 @@ const KindDynamic = struct { var new_lib = try std.DynLib.open(tmp_path); const new_callbacks = try getCallbacksFromLibrary(&new_lib); + // TODO: + _ = new_callbacks; // autofix try tmp_dir.deleteFile(tmp_filename); @@ -196,69 +192,20 @@ const KindDynamic = struct { self.reload_index += 1; self.ignore_next_event = true; - return new_callbacks; + return true; } } - return null; + return false; } }; -const KindStatic = struct { - fn getCallbacks() GameCallbacks { - const game = @import("game"); - return GameCallbacks{ - .init = game.init, - .deinit = game.deinit, - .tick = game.tick, - .debug = if (build_options.has_imgui) game.debug else {}, - }; - } -}; - -pub fn initDynamic(gpa: std.mem.Allocator, path: []const u8) !GameLinking { - if (!build_options.code_hot_reload) { - return error.NotSupported; - } - - var dynamic = try KindDynamic.init(gpa, path); - errdefer dynamic.deinit(gpa); - - const callbacks = try dynamic.getCallbacks(); - - return GameLinking{ - .kind = .{ .dynamic = dynamic }, - .callbacks = callbacks +pub fn getStatic() ?GameCallbacks { + const game = @import("game"); + return GameCallbacks{ + .init = game.init, + .deinit = game.deinit, + .tick = game.tick, + .debug = if (build_options.has_imgui) game.debug else {}, }; } - -pub fn initStatic() !GameLinking { - if (!build_options.statically_linked) { - return error.NotStaticallyLinked; - } - - return GameLinking{ - .kind = .static, - .callbacks = KindStatic.getCallbacks() - }; -} - -pub fn deinit(self: *GameLinking, gpa: std.mem.Allocator) void { - if (build_options.code_hot_reload and self.kind == .dynamic) { - self.kind.dynamic.deinit(gpa); - } -} - -pub fn check(self: *GameLinking) !void { - if (self.kind == .static) { - return; - } - - if (!build_options.code_hot_reload) { - return; - } - - if (try self.kind.dynamic.checkForChanges()) |new_callbacks| { - self.callbacks = new_callbacks; - } -} diff --git a/engine/src/engine/root.zig b/engine/src/engine/root.zig index 4d25dad..b088a2a 100644 --- a/engine/src/engine/root.zig +++ b/engine/src/engine/root.zig @@ -28,227 +28,11 @@ pub const Vec2 = Math.Vec2; const rgb = Math.rgb; const Vec4 = Math.Vec4; +const DebugSystem = @import("./debug_system.zig"); + const GameLinking = @import("game_linking.zig"); -const GameState = struct { - gpa: std.mem.Allocator, - - state: ?GameCallbacks.State, - frame: ?Lib.Frame, - - mouse_position: ?Vec2, - started_at: std.time.Instant, - last_frame_at: Lib.Nanoseconds, - - const Init = struct { - screen_size: Vec2, - seed: u64 - }; - - const Tick = struct { - dt_ns: u64, - events: []Input.Event, - }; - - const TickResult = struct { - audio: []Lib.AudioCommand, - graphics: []Lib.GraphicsCommand, - clear_color: Vec4, - cursor_hidden: bool - }; - - pub fn init(gpa: std.mem.Allocator) GameState { - return GameState{ - .gpa = gpa, - .mouse_position = null, - .started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"), - .last_frame_at = 0, - .state = null, - .frame = null, - }; - } - - pub fn deinit(self: *GameState) void { - if (self.frame) |*frame| { - frame.deinit(); - } - } - - fn processEvent(self: *GameState, event: Input.Event) void { - assert(self.frame != null); - const frame = &self.frame.?; - - switch (event) { - .key_pressed => |opts| { - if (!opts.repeat) { - frame.keyboard.press(opts.code, frame.time_ns); - } - }, - .key_released => |key_code| { - frame.keyboard.release(key_code); - }, - .mouse_leave => { - frame.keyboard.releaseAll(); - - assert(self.mouse_position != null); - self.mouse_position = null; - frame.mouse_button = .empty; - }, - .mouse_enter => |pos| { - assert(self.mouse_position == null); - self.mouse_position = pos; - }, - .mouse_move => |pos| { - assert(self.mouse_position != null); - self.mouse_position = pos; - }, - .mouse_pressed => |opts| { - assert(self.mouse_position != null); - self.mouse_position = opts.position; - frame.mouse_button.press(opts.button, frame.time_ns); - }, - .mouse_released => |opts| { - assert(self.mouse_position != null); - self.mouse_position = opts.position; - frame.mouse_button.release(opts.button); - }, - .window_resize => |opts| { - frame.screen_size = .initFromInt(u32, opts.width, opts.height); - }, - else => {} - } - } - - fn runGameInit( - self: *GameState, - init_fn: GameCallbacks.InitFn, - initial: Init, - ) void { - assert(self.frame == null); - const opts = Lib.Init{ - .gpa = self.gpa, - .seed = initial.seed, - }; - self.state = init_fn(&opts); - - self.frame = .init(self.gpa, initial.screen_size); - } - - fn runGameDeinit(self: *GameState, deinit_fn: GameCallbacks.DeinitFn) void { - assert(self.state != null); - deinit_fn(self.state.?); - self.state = null; - } - - fn tick(self: *GameState, tick_fn: GameCallbacks.TickFn, opts: GameState.Tick) !TickResult { - assert(self.frame != null); - const frame = &self.frame.?; - - _ = frame.arena.reset(.retain_capacity); - const arena = frame.arena.allocator(); - - const audio_commands_capacity = frame.audio_commands.capacity; - frame.audio_commands = .empty; - try frame.audio_commands.ensureTotalCapacity(arena, audio_commands_capacity); - - const graphics_commands_capacity = frame.graphics_commands.capacity; - frame.graphics_commands = .empty; - try frame.graphics_commands.ensureTotalCapacity(arena, graphics_commands_capacity); - - frame.dt_ns = opts.dt_ns; - frame.time_ns += opts.dt_ns; - - for (opts.events) |event| { - if (self.mouse_position == null) { - if (event == .mouse_leave) { - continue; - } - - const enter_position = switch(event) { - .mouse_pressed => |e_opts| e_opts.position, - .mouse_released => |e_opts| e_opts.position, - .mouse_move => |pos| pos, - else => null - }; - - if (enter_position) |pos| { - self.processEvent(.{ .mouse_enter = pos }); - } - } else { - if (event == .mouse_enter) { - continue; - } - } - - self.processEvent(event); - } - - frame.mouse_position = self.mouse_position; - - var maybe_screen_scaler: ?ScreenScalar = null; - if (frame.canvas_size) |canvas_size| { - const screen_scaler = ScreenScalar.init(frame.screen_size, canvas_size); - maybe_screen_scaler = screen_scaler; - screen_scaler.push(frame); - - if (frame.mouse_position) |mouse_position| { - frame.mouse_position = mouse_position.sub(screen_scaler.translation).divideScalar(screen_scaler.scale); - } - } - - assert(self.state != null); - tick_fn(self.state.?, frame); - - if (maybe_screen_scaler) |screen_scaler| { - screen_scaler.pop(frame, frame.clear_color); - } - - frame.keyboard.pressed = .initEmpty(); - frame.keyboard.released = .initEmpty(); - frame.mouse_button.pressed = .initEmpty(); - frame.mouse_button.released = .initEmpty(); - - return TickResult{ - .graphics = frame.graphics_commands.items, - .audio = frame.audio_commands.items, - .clear_color = frame.clear_color, - .cursor_hidden = frame.hide_cursor - }; - } - - fn runGameDebug(self: *GameState, debug_fn: GameCallbacks.DebugFn, imgui_ctx: *Lib.ImGui) void { - assert(self.state != null); - debug_fn(self.state.?, imgui_ctx); - } -}; - -const Recording = struct { - arena: std.heap.ArenaAllocator, - - initial: GameState.Init, - ticks: std.ArrayList(GameState.Tick), - - pub fn init(gpa: std.mem.Allocator, initial: GameState.Init) Recording { - return Recording{ - .arena = .init(gpa), - .ticks = .empty, - .initial = initial - }; - } - - pub fn appendTick(self: *Recording, tick: GameState.Tick) !void { - const arena = self.arena.allocator(); - - try self.ticks.append(arena, .{ - .dt_ns = tick.dt_ns, - .events = try arena.dupe(Input.Event, tick.events) - }); - } - - pub fn deinit(self: Recording) void { - self.arena.deinit(); - } -}; +const Environment = @import("environment.zig"); const Engine = @This(); @@ -256,46 +40,49 @@ allocator: std.mem.Allocator, graphics: Graphics, audio: Audio, -game_linking: GameLinking, -game_state: GameState, +seed: u64, +// game_linking: GameLinking, +environment: Environment, queued_events: std.ArrayList(Input.Event), is_mouse_inside: bool, -seed: u64, - -show_debug: bool, -restart_game: bool, -recording: ?Recording, - -play_recording: bool, -recording_tick: u64, +debug: DebugSystem, pub const RunOptions = struct { - pub const AssetLoading = union(enum) { - bundle_bytes: []const u8, - bundle_path: []const u8, - dir_path: []const u8 - }; - allocator: std.mem.Allocator, game_library_path: ?[]const u8 = null, do_recording: bool = (builtin.mode == .Debug), screen_width: u32 = 640, screen_height: u32 = 480, - - asset_loading: AssetLoading }; pub fn run(self: *Engine, opts: RunOptions) !void { - var game_linking: GameLinking = undefined; - if (opts.game_library_path) |game_library_path| { - game_linking = try .initDynamic(opts.allocator, game_library_path); - } else { - game_linking = try .initStatic(); + // var game_linking: GameLinking = undefined; + // if (opts.game_library_path) |game_library_path| { + // game_linking = try .initDynamic(opts.allocator, game_library_path); + // } else { + // game_linking = try .initStatic(); + // } + + var debug_system = DebugSystem.init(opts.allocator); + + var maybe_callbacks: ?GameCallbacks = null; + if (build_options.code_static_linking) { + maybe_callbacks = GameLinking.getStatic(); + } + if (build_options.code_dynamic_linking and opts.game_library_path != null) { + var dynamic = try GameLinking.Dynamic.init(opts.allocator, opts.game_library_path.?); + maybe_callbacks = try dynamic.getCallbacks(); + debug_system.dynamic_linking = dynamic; } - const initial = GameState.Init{ + if (maybe_callbacks == null) { + return error.NotLinked; + } + const callbacks = maybe_callbacks.?; + + const initial = Environment.Init{ .seed = @bitCast(std.time.milliTimestamp()), .screen_size = .initFromInt(u32, opts.screen_width, opts.screen_height) }; @@ -314,23 +101,19 @@ pub fn run(self: *Engine, opts: RunOptions) !void { .allocator = opts.allocator, .logger = .{ .func = sokolLogCallback }, }), - .game_state = .init(opts.allocator), - .game_linking = game_linking, - .show_debug = false, - .restart_game = false, - .recording = null, + .environment = .init(opts.allocator, callbacks), .queued_events = .empty, - .is_mouse_inside = false, - .play_recording = false, - .recording_tick = 0, .seed = initial.seed, + .is_mouse_inside = false, + .debug = debug_system }; - self.game_state.runGameInit(game_linking.callbacks.init, initial); + self.environment.stateInit(initial); - if (opts.do_recording) { - self.recording = .init(opts.allocator, initial); - } + // TODO: + // if (opts.do_recording) { + // self.recording = .init(opts.allocator, initial); + // } tracy.setThreadName("Main"); @@ -412,16 +195,19 @@ fn sokolCleanup(self: *Engine) void { const zone = tracy.initZone(@src(), .{ }); defer zone.deinit(); - if (self.recording) |recording| { - recording.deinit(); - } + // TODO: + // if (self.recording) |recording| { + // recording.deinit(); + // } self.queued_events.deinit(self.allocator); - self.game_state.runGameDeinit(self.game_linking.callbacks.deinit); - self.game_state.deinit(); + self.environment.stateDeinit(); + self.environment.deinit(); self.audio.deinit(); self.graphics.deinit(self.allocator); - self.game_linking.deinit(self.allocator); + self.debug.deinit(); + // TODO: + // self.game_linking.deinit(self.allocator); } fn sokolFrame(self: *Engine) !void { @@ -430,38 +216,44 @@ fn sokolFrame(self: *Engine) !void { const zone = tracy.initZone(@src(), .{ }); defer zone.deinit(); - try self.game_linking.check(); - - const now = std.time.Instant.now() catch @panic("Instant.now() unsupported"); - const time_passed = now.since(self.game_state.started_at); - const dt_ns = time_passed - self.game_state.last_frame_at; - self.game_state.last_frame_at = time_passed; - - if (self.play_recording) { - assert(self.recording != null); - if (self.recording_tick >= self.recording.?.ticks.items.len) { - self.play_recording = false; + if (self.debug.dynamic_linking) |*dynamic| { + if (try dynamic.checkForChanges()) { + self.environment.callbacks = try dynamic.getCallbacks(); } } - var tick: GameState.Tick = undefined; - if (self.play_recording) { - assert(self.recording != null); - const ticks = self.recording.?.ticks.items; - tick = ticks[self.recording_tick]; - self.recording_tick += 1; - } else { - tick = GameState.Tick{ + const now = std.time.Instant.now() catch @panic("Instant.now() unsupported"); + const time_passed = now.since(self.environment.started_at); + const dt_ns = time_passed - self.environment.last_frame_at; + self.environment.last_frame_at = time_passed; + + // TODO: + // if (self.play_recording) { + // assert(self.recording != null); + // if (self.recording_tick >= self.recording.?.ticks.items.len) { + // self.play_recording = false; + // } + // } + + // TODO: + var tick: Environment.Tick = undefined; + // if (self.play_recording) { + // assert(self.recording != null); + // const ticks = self.recording.?.ticks.items; + // tick = ticks[self.recording_tick]; + // self.recording_tick += 1; + // } else { + tick = Environment.Tick{ .dt_ns = dt_ns, .events = self.queued_events.items }; + // + // if (self.recording) |*recording| { + // try recording.appendTick(tick); + // } + // } - if (self.recording) |*recording| { - try recording.appendTick(tick); - } - } - - const tick_result = try self.game_state.tick(self.game_linking.callbacks.tick, tick); + const tick_result = try self.environment.stateTick(tick); self.queued_events.clearRetainingCapacity(); @@ -474,9 +266,10 @@ fn sokolFrame(self: *Engine) !void { self.graphics.drawCommands(tick_result.graphics); - if (self.show_debug and build_options.has_imgui) { - try self.showDebugWindow(); - } + // TODO: + // if (self.show_debug and build_options.has_imgui) { + // try self.showDebugWindow(); + // } } for (tick_result.audio) |command| { @@ -484,25 +277,27 @@ fn sokolFrame(self: *Engine) !void { } } - if (self.restart_game) { - self.restart_game = false; - - const initial = GameState.Init{ - .seed = self.seed, - .screen_size = .init(sapp.widthf(), sapp.heightf()) - }; - - self.game_state.runGameDeinit(self.game_linking.callbacks.deinit); - self.game_state.deinit(); - - self.game_state = .init(self.allocator); - self.game_state.runGameInit(self.game_linking.callbacks.init, initial); - - if (self.recording) |recording| { - recording.deinit(); - self.recording = .init(self.allocator, initial); - } - } + // TODO: + // if (self.restart_game) { + // self.restart_game = false; + // + // const initial = Environment.Init{ + // .seed = self.seed, + // .screen_size = .init(sapp.widthf(), sapp.heightf()) + // }; + // + // self.environment.runGameDeinit(self.game_linking.callbacks.deinit); + // self.environment.deinit(); + // + // self.environment = .init(self.allocator); + // self.environment.runGameInit(self.game_linking.callbacks.init, initial); + // + // // TODO: + // // if (self.recording) |recording| { + // // recording.deinit(); + // // self.recording = .init(self.allocator, initial); + // // } + // } } fn showDebugWindow(self: *Engine) !void { @@ -524,7 +319,7 @@ fn showDebugWindow(self: *Engine) !void { var imgui_ctx = Lib.ImGui.init(self.allocator); defer imgui_ctx.deinit(); - self.game_state.runGameDebug(self.game_linking.callbacks.debug, &imgui_ctx); + self.environment.runGameDebug(self.game_linking.callbacks.debug, &imgui_ctx); for (imgui_ctx.commands.items) |cmd| { switch (cmd) { .text => |str| imgui.text(str) @@ -535,31 +330,34 @@ fn showDebugWindow(self: *Engine) !void { if (imgui.beginTabItem("Engine")) { defer imgui.endTabItem(); - if (imgui.button("Restart")) { - self.restart_game = true; - } + // TODO: + // if (imgui.button("Restart")) { + // self.restart_game = true; + // } - if (self.recording) |recording| { - imgui.beginDisabled(self.play_recording); - defer imgui.endDisabled(); + // TODO: + // if (self.recording) |recording| { + // imgui.beginDisabled(self.play_recording); + // defer imgui.endDisabled(); + // + // if (imgui.button("Replay")) { + // self.environment.runGameDeinit(self.game_linking.callbacks.deinit); + // self.environment.deinit(); + // + // self.environment = .init(self.allocator); + // self.environment.runGameInit(self.game_linking.callbacks.init, recording.initial); + // + // self.play_recording = true; + // self.recording_tick = 0; + // } + // } - if (imgui.button("Replay")) { - self.game_state.runGameDeinit(self.game_linking.callbacks.deinit); - self.game_state.deinit(); - - self.game_state = .init(self.allocator); - self.game_state.runGameInit(self.game_linking.callbacks.init, recording.initial); - - self.play_recording = true; - self.recording_tick = 0; - } - } - - if (self.game_linking.kind == .dynamic) { - imgui.textFmt("Linking: dynamic (from {s})", .{ self.game_linking.kind.dynamic.path }); - } else { - imgui.textFmt("Linking: static", .{ }); - } + // TODO: + // if (self.game_linking.kind == .dynamic) { + // imgui.textFmt("Linking: dynamic (from {s})", .{ self.game_linking.kind.dynamic.path }); + // } else { + // imgui.textFmt("Linking: static", .{ }); + // } if (build_options.asset_hot_reload) { imgui.textFmt("Asset loading: runtime (from {s})", .{build_options.asset_dir}); @@ -569,7 +367,7 @@ fn showDebugWindow(self: *Engine) !void { imgui.textFmt("Seed: 0x{x:08}", .{ self.seed }); - const time_ms: f64 = @floatFromInt(@divFloor(self.game_state.last_frame_at, std.time.ns_per_ms)); + const time_ms: f64 = @floatFromInt(@divFloor(self.environment.last_frame_at, std.time.ns_per_ms)); imgui.textFmt("Time: {:.2}", .{ time_ms / 1000 }); // imgui.textFmt("Draw commands: {}\n", .{ @@ -602,19 +400,23 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool { if (debug_keybinds and e.type == .KEY_DOWN) { if (e.key_code == .F4) { - self.show_debug = !self.show_debug; + // TODO: + // self.show_debug = !self.show_debug; return true; } if (e.key_code == .F5) { - self.restart_game = true; + // TODO: + // self.restart_game = true; return true; } } - if (self.play_recording) { - return false; - } + + // TODO: + // if (self.play_recording) { + // return false; + // } blk: switch (e.type) { .MOUSE_DOWN => { diff --git a/engine/src/runtime/main.zig b/engine/src/runtime/main.zig index c8bad01..48f7ee7 100644 --- a/engine/src/runtime/main.zig +++ b/engine/src/runtime/main.zig @@ -29,16 +29,16 @@ pub fn main() !void { var game_library_path: ?[]const u8 = null; defer if (game_library_path) |str| allocator.free(str); - var asset_loading: Engine.RunOptions.AssetLoading = undefined; - if (!build_options.asset_hot_reload) { - asset_loading = .{ - .bundle_bytes = @embedFile("asset_bundle") - }; - } else { - asset_loading = .{ - .dir_path = build_options.asset_dir - }; - } + // var asset_loading: Engine.RunOptions.AssetLoading = undefined; + // if (!build_options.asset_hot_reload) { + // asset_loading = .{ + // .bundle_bytes = @embedFile("asset_bundle") + // }; + // } else { + // asset_loading = .{ + // .dir_path = build_options.asset_dir + // }; + // } if (!isWasm) { var cli: CLI = undefined; @@ -51,7 +51,7 @@ pub fn main() !void { }); var opt_game_lib_path: ?CLI.Option.Id = null; - if (build_options.code_hot_reload) { + if (build_options.code_dynamic_linking) { opt_game_lib_path = try cli.addOption(.{ .long_name = "game-lib-path", .description = "Load game code from dynamic library", @@ -103,7 +103,7 @@ pub fn main() !void { try engine.run(.{ .allocator = allocator, .game_library_path = game_library_path, - .asset_loading = asset_loading + // .asset_loading = asset_loading }); }