From 4b186784bc1cb78ce7cc0da7f50b813ebad43a6e Mon Sep 17 00:00:00 2001
From: Nate Anderson <nate.anderson@vasion.com>
Date: Wed, 22 Jan 2025 10:51:04 -0700
Subject: [PATCH] Removed the leak, but still lost on the game update loop

---
 src/game_state.zig  | 64 +++++++++++++++++++++++++--------------------
 src/main.zig        | 36 ++++++++++++++-----------
 src/utils/timer.zig | 33 +++++++++++++----------
 3 files changed, 75 insertions(+), 58 deletions(-)

diff --git a/src/game_state.zig b/src/game_state.zig
index dd5e42a..7899909 100644
--- a/src/game_state.zig
+++ b/src/game_state.zig
@@ -18,7 +18,7 @@ pub const GameStateController = struct {
     SCREEN_HEIGHT: u32 = 480,
     slime_factory_controller: sf.SlimeFactoryController = undefined,
     game_text_factory_controller: text.GameTextFactoryController = undefined,
-    prev_game_state: *GameState = undefined,
+    prev_game_state: ?*GameState = null,
     current_game_state: *GameState = undefined,
     renderer: *sdl.struct_SDL_Renderer = undefined,
     rng: std.Random = undefined,
@@ -51,6 +51,11 @@ pub const GameStateController = struct {
 
     pub fn deinit(self: *GameStateController) void {
         self.slime_factory_controller.deinit();
+
+        self.allocator.destroy(self.current_game_state);
+        if (self.prev_game_state) |gs| {
+            self.allocator.destroy(gs);
+        }
     }
 
     // Physics main loop that processes variable time steps
@@ -114,10 +119,12 @@ pub const GameStateController = struct {
         // sdl.SDL_Delay(100);
 
         var game_state = try self.allocator.create(GameState);
-        game_state.allocator = self.allocator;
+        if (self.prev_game_state) |prev_gs| {
+            self.allocator.destroy(prev_gs);
+        }
         game_state.slime_factory = try self.slime_factory_controller.spawnFactory(self.allocator);
         game_state.fps_timer = &self.fps_timer;
-        game_state.phys_ticks = @floatFromInt(self.phys_timer.getTicks());
+        game_state.phys_ticks = self.phys_timer.getTicks();
         game_state.phys_updates_since_render = self.phys_updates_since_last_render;
 
         // self.allocator.destroy(self.prev_game_state);
@@ -145,7 +152,7 @@ pub const GameStateController = struct {
     }
 
     fn getFpsText(self: *GameStateController, buf: []u8) ![:0]const u8 {
-        const ticks_f: f64 = @floatFromInt(self.fps_timer.getTicks());
+        const ticks_f: f64 = self.fps_timer.getTicks();
         const frames_f: f64 = @floatFromInt(self.frames);
         const avg_fps: f64 = @round(frames_f / (ticks_f / 1000.0));
         return try std.fmt.bufPrintZ(buf, "{d} fps", .{avg_fps});
@@ -153,38 +160,37 @@ pub const GameStateController = struct {
 };
 
 pub const GameState = struct {
-    allocator: std.mem.Allocator,
     slime_factory: sf.SlimeFactory = undefined,
     fps_timer: *Timer,
     phys_ticks: f64,
     phys_updates_since_render: u32,
     // Checks all struct fields in GameState and calls deinit
-    pub fn deinit(self: *const GameState) void {
-        const game_state_info = @typeInfo(GameState);
-        switch (game_state_info) {
-            .Struct => {
-                inline for (game_state_info.Struct.fields) |field| {
-                    switch (@typeInfo(field.type)) {
-                        .Struct => {
-                            // const inner_struct_info = @typeInfo(field.type);
+    // pub fn deinit(self: *const GameState) void {
+    // const game_state_info = @typeInfo(GameState);
+    // switch (game_state_info) {
+    //     .Struct => {
+    //         inline for (game_state_info.Struct.fields) |field| {
+    //             switch (@typeInfo(field.type)) {
+    //                 .Struct => {
+    //                     // const inner_struct_info = @typeInfo(field.type);
 
-                            // Check if struct has `deinit` method
-                            if (@hasDecl(field.type, "deinit")) {
-                                const field_ptr = @field(self, field.name);
-                                field_ptr.deinit();
-                                std.debug.print("Deinit called on {s}\n", .{field.name});
-                            }
-                        },
-                        else => {},
-                    }
-                }
-            },
+    //                     // Check if struct has `deinit` method
+    //                     if (@hasDecl(field.type, "deinit")) {
+    //                         const field_ptr = @field(self, field.name);
+    //                         field_ptr.deinit();
+    //                         std.debug.print("Deinit called on {s}\n", .{field.name});
+    //                     }
+    //                 },
+    //                 else => {},
+    //             }
+    //         }
+    //     },
 
-            else => {},
-        }
+    //     else => {},
+    // }
 
-        // Loop over fields of `GameState`
+    // Loop over fields of `GameState`
 
-        self.allocator.destroy(self);
-    }
+    //     self.allocator.destroy(self);
+    // }
 };
diff --git a/src/main.zig b/src/main.zig
index 37ed154..bd47543 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -38,6 +38,9 @@ pub fn main() !void {
 
     var quit = false;
 
+    var game_timer = Timer{};
+    game_timer.start();
+
     var frame_time = Timer{};
     frame_time.start();
 
@@ -72,9 +75,10 @@ pub fn main() !void {
         // Wait for time to render screen, keep running physics
         accumulator = accumulator + SCREEN_TICKS_PER_FRAME;
         const total_phys_ticks: u32 = @intFromFloat(PHYS_TICKS_PER_UPDATE * PHYS_UPDATES_PER_RENDER);
-        const phys_done_at_ticks: u32 = sdl.SDL_GetTicks() + total_phys_ticks;
+        const game_time_ms: u32 = @intFromFloat(game_timer.getTicks());
+        const phys_done_at_ticks: u32 = game_time_ms + total_phys_ticks;
         var phys_ticks_used: f64 = 0;
-        std.debug.print("phys ticks/upd: {d}, Acc: {d}\nTotal phys ticks {d}ms\nPhys done at {d}ms\n", .{ PHYS_TICKS_PER_UPDATE, accumulator, total_phys_ticks, phys_done_at_ticks });
+        std.debug.print("Phys ticks/upd: {d}\nAcc: {d}\nTotal phys ticks {d}ms\nPhys done at {d}ms\n", .{ PHYS_TICKS_PER_UPDATE, accumulator, total_phys_ticks, phys_done_at_ticks });
         // Run a physics update
         while (accumulator >= PHYS_TICKS_PER_UPDATE) {
             // Control loop here
@@ -86,6 +90,7 @@ pub fn main() !void {
                     },
                     sdl.SDL_KEYDOWN => {
                         switch (event.key.keysym.sym) {
+                            sdl.SDLK_RETURN => {},
                             else => {},
                         }
                         log("Got key event: %i\n", event.key.keysym.sym);
@@ -96,23 +101,24 @@ pub fn main() !void {
             prev_game_state = current_game_state;
             current_game_state = try game_state_controller.update_tick();
             const phys_ticks_this_update: f64 = current_game_state.?.phys_ticks - phys_ticks_used;
+            accumulator -= phys_ticks_this_update;
 
-            std.debug.print(" PU in {d}ms ", .{phys_ticks_this_update});
-            std.debug.print(" Acc:{d} - ", .{accumulator});
+            // std.debug.print(" PU in {d}ms ", .{phys_ticks_this_update});
+            // std.debug.print(" Acc:{d} - ", .{accumulator});
 
             // TODO dont turn it into an int, take the extra phys_frames and do a partial render
-            if (phys_ticks_this_update < PHYS_TICKS_PER_UPDATE) {
-                const ticks: u32 = @intFromFloat(PHYS_TICKS_PER_UPDATE - phys_ticks_this_update);
-                sdl.SDL_Delay(ticks);
-                accumulator -= PHYS_TICKS_PER_UPDATE;
-            } else {
-                std.debug.print("\nPHYSICS JANK\nPhysics Update took {d}ms, target: {d}ms\n", .{ phys_ticks_this_update, PHYS_TICKS_PER_UPDATE });
-                accumulator -= phys_ticks_this_update;
-            }
+            // if (phys_ticks_this_update < PHYS_TICKS_PER_UPDATE) {
+            //     const ticks: u32 = @intFromFloat(PHYS_TICKS_PER_UPDATE - phys_ticks_this_update);
+            //     sdl.SDL_Delay(ticks);
+            //     const ticksf: f64 = @floatFromInt(ticks);
+            //     accumulator -= (phys_ticks_this_update + ticksf);
+            // } else {
+            //     // std.debug.print("PHYSICS JANK: Physics Update took {d}ms, target: {d}ms\n", .{ phys_ticks_this_update, PHYS_TICKS_PER_UPDATE });
+            //     accumulator -= phys_ticks_this_update;
+            // }
             phys_ticks_used = current_game_state.?.phys_ticks;
-            std.debug.print(" PhyTicks this render: {d} ", .{current_game_state.?.phys_ticks});
+            // std.debug.print(" PhyTicks this render: {d} ", .{current_game_state.?.phys_ticks});
         }
-
         // TODO lerp together prev and current states
         // Otherwise will have physics jank
 
@@ -120,7 +126,7 @@ pub fn main() !void {
         std.debug.print("Took {d} phys frames\n", .{current_game_state.?.phys_updates_since_render});
         try game_state_controller.render(current_game_state.?);
 
-        prev_frame_ms = @floatFromInt(frame_time.getTicks());
+        prev_frame_ms = frame_time.getTicks();
         std.debug.print("Rendered to screen in {d}ms\n***\n***\n", .{prev_frame_ms});
         frame_time.reset();
         // const frame_time_ticks = frame_time.getTicks();
diff --git a/src/utils/timer.zig b/src/utils/timer.zig
index f1a2be6..1d1a081 100644
--- a/src/utils/timer.zig
+++ b/src/utils/timer.zig
@@ -1,57 +1,62 @@
 const sdl = @import("./../sdl.zig").c;
+const std = @import("std");
 
 pub const TimerStatus = enum { started, stopped, paused };
 
 pub const Timer = struct {
-    start_ticks_ms: u32 = 0,
-    pause_ticks_ms: u32 = 0,
+    start_ticks_ns: f64 = 0.0,
+    pause_ticks_ns: f64 = 0.0,
     status: TimerStatus = TimerStatus.stopped,
 
     pub fn start(self: *Timer) void {
         if (self.status == .started) return;
         self.status = .started;
-        self.start_ticks_ms = sdl.SDL_GetTicks();
-        self.pause_ticks_ms = 0;
+        self.start_ticks_ns = @floatFromInt(std.time.nanoTimestamp());
+        self.pause_ticks_ns = 0;
     }
 
     pub fn stop(self: *Timer) void {
         if (self.status == .stopped) return;
         self.status = .stopped;
-        self.start_ticks_ms = 0;
-        self.pause_ticks_ms = 0;
+        self.start_ticks_ns = 0;
+        self.pause_ticks_ns = 0;
     }
 
     // Resets the timer to 0 and starts it
     pub fn reset(self: *Timer) void {
         self.status = .started;
-        self.start_ticks_ms = sdl.SDL_GetTicks();
-        self.pause_ticks_ms = 0;
+        self.start_ticks_ns = @floatFromInt(std.time.nanoTimestamp());
+        self.pause_ticks_ns = 0;
     }
 
     pub fn pause(self: *Timer) void {
         if (self.status == .paused or self.status == .stopped) return;
         self.status = .paused;
-        self.pause_ticks_ms = sdl.SDL_GetTicks();
+        const ns_stampf: f64 = @floatFromInt(std.time.nanoTimestamp());
+        self.pause_ticks_ns = (ns_stampf - self.start_ticks_ns) / 1_000_000;
     }
 
     pub fn unpause(self: *Timer) void {
         if (self.status == .started or self.status == .stopped) return;
         self.status = .started;
-        self.start_ticks_ms = sdl.SDL_GetTicks() - self.pause_ticks_ms;
-        self.pause_ticks_ms = 0;
+        const ns_stampf: f64 = @floatFromInt(std.time.nanoTimestamp());
+        const diff_ns: f64 = ns_stampf - self.pause_ticks_ns;
+        self.start_ticks_ns = self.start_ticks_ns + diff_ns;
+        self.pause_ticks_ns = 0;
     }
 
     // Gets the current tick/ms of the timer
-    pub fn getTicks(self: *Timer) u32 {
+    pub fn getTicks(self: *Timer) f64 {
         switch (self.status) {
             .stopped => {
                 return 0;
             },
             .paused => {
-                return self.pause_ticks_ms;
+                return self.pause_ticks_ns / 1_000_000;
             },
             .started => {
-                return sdl.SDL_GetTicks() - self.start_ticks_ms;
+                const ns_stampf: f64 = @floatFromInt(std.time.nanoTimestamp());
+                return (ns_stampf - self.start_ticks_ns) / 1_000_000;
             },
         }
     }