From 402704bf4f0dff94ab9508f14d04fa1faec33365 Mon Sep 17 00:00:00 2001 From: peterino2 Date: Thu, 2 Oct 2025 13:42:47 -0700 Subject: [PATCH] hotreload hardening --- engine/core/src/engineObject.zig | 13 +++ engine/core/src/inputs/inputStack.zig | 10 ++- engine/imgui/src/imgui.zig | 1 + engine/imgui/src/sgpu/backend_impl.zig | 20 ++++- lib/cimgui/src/cimgui.zig | 16 ++++ projects/sampleGame/externGame/externGame.zig | 82 ++++++++++++++++++- projects/sampleGame/main.zig | 1 + 7 files changed, 139 insertions(+), 4 deletions(-) diff --git a/engine/core/src/engineObject.zig b/engine/core/src/engineObject.zig index 6550854..ff19b33 100644 --- a/engine/core/src/engineObject.zig +++ b/engine/core/src/engineObject.zig @@ -74,7 +74,20 @@ pub fn PatchStruct(comptime T: type, p: *T, old: []const FieldInfo) void { dest.ptr = @ptrCast(&@field(new, field.name)); dest.len = @sizeOf(@TypeOf(@field(new, field.name))); + std.debug.print( + "patching field {s} {d} bytes {d} -> {d} {s}\n", + .{ + field.name, + src.len, + of.offset, + @offsetOf(T, field.name), + if (of.offset == @offsetOf(T, field.name)) "" else "change!", + }, + ); + std.mem.copyForwards(u8, dest, src); + } else { + std.debug.print("new field added {s}", .{field.name}); } } diff --git a/engine/core/src/inputs/inputStack.zig b/engine/core/src/inputs/inputStack.zig index 1d77239..777e771 100644 --- a/engine/core/src/inputs/inputStack.zig +++ b/engine/core/src/inputs/inputStack.zig @@ -545,8 +545,16 @@ pub const BindingLayer = struct { pub fn removeBindingByName(self: *@This(), _name: core.Name) void { var name = _name; - _ = self.bindingStack.orderedRemove(self.bindingsByName.get(name.handle()).?); + const index = self.bindingsByName.get(name.handle()).?; + _ = self.bindingStack.orderedRemove(index); _ = self.bindingsByName.remove(name.handle()); + + var iter = self.bindingsByName.valueIterator(); + while (iter.next()) |next| { + if (next.* > index) { + next.* -= 1; + } + } } pub fn destroy(self: *@This()) void { diff --git a/engine/imgui/src/imgui.zig b/engine/imgui/src/imgui.zig index dae4ba4..aecdfe2 100644 --- a/engine/imgui/src/imgui.zig +++ b/engine/imgui/src/imgui.zig @@ -23,6 +23,7 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me pub fn setupFromModule() void { const impl = core.getEngineObject(Impl).?; api.setCurrentContext(impl.context); + api.setBeginCount(&impl.beginCount); } pub fn shutdown_module(allocator: std.mem.Allocator) void { diff --git a/engine/imgui/src/sgpu/backend_impl.zig b/engine/imgui/src/sgpu/backend_impl.zig index c506d0d..20cdb20 100644 --- a/engine/imgui/src/sgpu/backend_impl.zig +++ b/engine/imgui/src/sgpu/backend_impl.zig @@ -6,6 +6,8 @@ pub const Impl = struct { rendererDebug: bool = true, context: *ig.Context = undefined, + beginCount: u32 = 0, + pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "imgui.BackendImpl"); pub const Settings = struct { @@ -32,6 +34,11 @@ pub const Impl = struct { try platform.getInstance().addSDLProcessFunction(processSDLEvents); self.context = ig.getCurrentContext().?; + self.setupBeginCount(); + } + + pub fn setupBeginCount(self: *@This()) void { + ig.setBeginCount(&self.beginCount); } pub fn processSDLEvents(event: *sdl3.Event) void { @@ -44,6 +51,16 @@ pub const Impl = struct { const self: *@This() = @ptrCast(@alignCast(p)); platform.context().imguiMouseConsumed = ig.isWindowHovered(.{ .any_window = true }); + const count = self.beginCount; + while (self.beginCount > 0) { + ig.end(); + } + if (count > 0) { + _ = ig.begin("error", null, .{}); + ig.textf("IMGUI ERORR", .{}); + core.engine_logs("error"); + ig.end(); + } ig.render(); if (platform.getInstance().imguiVisible) { @@ -76,11 +93,12 @@ pub const Impl = struct { } pub fn preTick(self: *@This(), dt: f64) core.EngineDataEventError!void { + _ = self; + c.Imgui_SDL3_NewFrame(); c.igNewFrame(); _ = ig.dockSpaceOverViewport(ig.getMainViewport(), .{ .passthru_central_node = true }, null); - _ = self; _ = dt; } diff --git a/lib/cimgui/src/cimgui.zig b/lib/cimgui/src/cimgui.zig index a892f5c..8b36dbe 100644 --- a/lib/cimgui/src/cimgui.zig +++ b/lib/cimgui/src/cimgui.zig @@ -2388,12 +2388,28 @@ pub inline fn styleColorsLight(dst: [*c]Style) void { //igStyleColorsLight pub inline fn styleColorsClassic(dst: [*c]Style) void { //igStyleColorsClassic c.igStyleColorsClassic(dst); } + +pub var gBeginCount: *u32 = undefined; + +pub fn setBeginCount(beginCount: *u32) void { + gBeginCount = beginCount; +} + pub inline fn begin(name: [*c]const u8, p_open: [*c]bool, flags: WindowFlags) bool { //igBegin + gBeginCount.* += 1; return c.igBegin(name, p_open, @bitCast(flags)); } pub inline fn end() void { //igEnd + gBeginCount.* -= 1; c.igEnd(); } + +pub fn checkIgEnd() void { + while (gBeginCount.* > 0) { + end(); + } +} + pub inline fn beginChild_Str(str_id: [*c]const u8, size: Vec2, border: bool, flags: WindowFlags) bool { //igBeginChild_Str return c.igBeginChild_Str(str_id, @bitCast(size), border, @bitCast(flags)); } diff --git a/projects/sampleGame/externGame/externGame.zig b/projects/sampleGame/externGame/externGame.zig index d90180c..93babad 100644 --- a/projects/sampleGame/externGame/externGame.zig +++ b/projects/sampleGame/externGame/externGame.zig @@ -38,9 +38,11 @@ pub fn start_module(args: core.ModuleLoaderArgs) !void { //log("gonna try something dumb- lets patch the vtable of that old object with my vtable's values", .{}); const ref = core.getEngineObjectRef(ExternGameObject).?; const vtable: *core.EngineObjectVTable = @constCast(ref.vtable); - vtable.* = ExternGameObject.NeonObjectTable; core.PatchStruct(ExternGameObject, @ptrCast(@alignCast(ref.ptr)), ref.vtable.fieldList.?); + vtable.* = ExternGameObject.NeonObjectTable; + + core.get(ExternGameObject).onHotReload(); } pub const BoxObject = struct { @@ -93,7 +95,7 @@ pub const BoxObject = struct { pub const ExternGameObject = struct { pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "game.ExternGameObject"); - pub const Slack = core.SlackStruct(@This(), 512); + pub const Slack = core.SlackStruct(@This(), 1024); allocator: std.mem.Allocator = undefined, consoleWindow: imgui.utils.ConsoleWindow = undefined, @@ -105,7 +107,12 @@ pub const ExternGameObject = struct { session: ?*net.Session = null, clientLink: ?*net.Link = null, + recompiling: bool = false, volume: f32 = 100, + volume2: f64 = 100, + volume4: f64 = 100, + newBool: bool = true, + new2Bool: bool = true, //lmao: bool = false, //lmao2: bool = true, // lib: bool = false, @@ -113,8 +120,11 @@ pub const ExternGameObject = struct { // firstTick: bool = true, + frameCount: u32 = 0, + fireInput: ?*core.ActionBinding = null, altFireInput: ?*core.ActionBinding = null, + reloadInput: ?*core.ActionBinding = null, a: core.Vector2f = .{ .x = 1, .y = 4 }, b: core.Vector2f = .{ .x = 4, .y = 1 }, @@ -134,6 +144,28 @@ pub const ExternGameObject = struct { } pub fn _beginPlay(self: *@This()) !void { + try self.setupInputs(); + } + + pub fn setupInputs(self: *@This()) !void { + if (self.fireInput) |fireInput| { + fireInput.deactivate(); + } + if (self.altFireInput) |altFireInput| { + altFireInput.deactivate(); + } + + if (self.reloadInput) |input| { + input.deactivate(); + } + + if (self.reloadInput == null) { + self.reloadInput = try core.ActionBinding.create(core.MakeName("input")); + self.reloadInput.?.addKey(.@"9", .keyDown); + _ = self.reloadInput.?.data.addListener(self, onRequestReload); + } + self.reloadInput.?.activate(); + if (self.fireInput == null) { self.fireInput = try core.ActionBinding.create(core.MakeName("fire")); self.fireInput.?.addKey(.Mouse1, .keyDown); @@ -149,6 +181,12 @@ pub const ExternGameObject = struct { self.altFireInput.?.activate(); } + pub fn onHotReload(self: *@This()) void { + self.setupInputs() catch { + core.engine_errs("Unable to setup inputs"); + }; + } + pub fn endPlay(p: *anyopaque) void { const self = core.cast(*@This(), p); self.fireInput.?.deactivate(); @@ -297,6 +335,37 @@ pub const ExternGameObject = struct { core.debugSphere(v2ToV3(vp), 0.1, .{ .color = .{ .y = 1.0, .z = 1.0, .x = 1.0 } }); } + pub fn recompileComplete(ctx: ?*anyopaque) void { + const self = core.cast(*@This(), ctx.?); + self.recompiling = false; + } + + fn onRequestReload(ctx: ?*anyopaque, _: core.ActionEvent) void { + const self = core.cast(*@This(), ctx.?); + + if (!self.recompiling) { + self.recompiling = true; + const x = core.EngineObject(sys.SystemRunner).get(); + x.commandQueue.pushLocked(.{ + .subprocess = .{ + .context = self, + .onComplete = recompileComplete, + .task = sys.SubprocessTask.runCommand( + x.allocator, + &.{ "zig", "build", "install" }, + ".", + ) catch { + self.recompiling = false; + return; + }, + }, + }) catch { + self.recompiling = false; + return; + }; + } + } + fn onAltFire(ctx: ?*anyopaque, _: core.ActionEvent) void { const self: *@This() = @ptrCast(@alignCast(ctx)); @@ -355,6 +424,15 @@ pub const ExternGameObject = struct { self.firstTick = false; } + imgui.utils.structDebugWindow(self); + + if (self.recompiling) { + if (ig.begin("recompiling", null, .{})) { + ig.textf("recompiling...", .{}); + } + ig.end(); + } + if (platform.context().isCursorEnabled()) { if (ig.begin("meh", null, .{})) { // if (ig.checkbox("move lights ", null)) {} diff --git a/projects/sampleGame/main.zig b/projects/sampleGame/main.zig index 1d764ba..623b888 100644 --- a/projects/sampleGame/main.zig +++ b/projects/sampleGame/main.zig @@ -170,6 +170,7 @@ pub const FoxObject = struct { pr.scale = pr.scale.fmul(0.01); scene.setPosRot(pr); scene.updateTransform(); + scene.setMobility(.moveable); const mesh = fox.addComponent(rend.MeshComponent).?; mesh.setMesh("m_fox");