diff --git a/src/time/time.c b/src/time/time.c index de12317..5a0bbc1 100644 --- a/src/time/time.c +++ b/src/time/time.c @@ -33,16 +33,18 @@ void timeUpdate(void) { #if TIME_FIXED == 0 #if TIME_SDL2 - float_t newTime = (float_t)SDL_GetTicks() / 1000.0f; - TIME.dynamicDelta = newTime - TIME.dynamicTime; - TIME.dynamicTime = newTime; + float_t msElapsed = ((float_t)SDL_GetTicks64() / 1000.0f) + TIME_STEP; + TIME.dynamicDelta = msElapsed - TIME.dynamicTime; + TIME.dynamicTime = msElapsed; TIME.dynamicUpdate = true; #else #error "No time platform defined" #endif assertTrue(TIME.dynamicDelta >= 0.0f, "Time delta is negative"); - if(TIME.dynamicTime - TIME.time >= TIME_STEP) { + + // Is within 1ms of a full step? + if(TIME.dynamicTime - TIME.time >= TIME_STEP * 0.999f) { TIME.dynamicUpdate = false; TIME.delta = TIME_STEP; TIME.time += TIME_STEP; diff --git a/src/time/time.h b/src/time/time.h index e95ae54..e56178f 100644 --- a/src/time/time.h +++ b/src/time/time.h @@ -8,8 +8,7 @@ #pragma once #include "dusk.h" -#define TIME_STEP (1.0f / 60.0f) // 60 Ticks per second (what we are aiming for) - +#define TIME_STEP (16.0f / 1000.0f) #ifndef TIME_FIXED #define TIME_FIXED 0 #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0534717..0f39308 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,4 +3,5 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT +add_subdirectory(time) add_subdirectory(util) \ No newline at end of file diff --git a/test/time/CMakeLists.txt b/test/time/CMakeLists.txt new file mode 100644 index 0000000..06f100b --- /dev/null +++ b/test/time/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +include(dusktest) + +# Tests +dusktest(test_time.c) \ No newline at end of file diff --git a/test/time/test_time.c b/test/time/test_time.c new file mode 100644 index 0000000..e8f3869 --- /dev/null +++ b/test/time/test_time.c @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "dusktest.h" +#include + +// Mocks +static uint64_t SDL_GETTICKS_TICKS = 0; +static bool_t SDL_GETTICKS_CALLED = false; + +uint64_t SDL_GetTicks64(void) { + SDL_GETTICKS_CALLED = true; + return SDL_GETTICKS_TICKS; +} + +// Mocked includes +#include "util/memory.h" +#include "time/time.h" + +// Tests +static void test_timeInit(void **state) { + (void)state; + + // Fill time with garbage + memorySet(&TIME, 0xFF, sizeof(TIME)); + + // Init + timeInit(); + + // Inital struct is set. + assert_float_equal(TIME.time, TIME_STEP, 0.0001f); + assert_float_equal(TIME.delta, TIME_STEP, 0.0001f); + assert_float_equal(TIME.dynamicTime, TIME_STEP, 0.0001f); + assert_float_equal(TIME.dynamicDelta, TIME_STEP, 0.0001f); + assert_false(TIME.dynamicUpdate); +} + +static void test_timeUpdate(void **state) { + (void)state; + + // Init first + timeInit(); + + // First time update, calls SDL_GetTicks + SDL_GETTICKS_TICKS = 14; // Simulate 15ms elapsed (just barely not one step) + SDL_GETTICKS_CALLED = false; + timeUpdate(); + assert_true(SDL_GETTICKS_CALLED); + assert_true(TIME.dynamicUpdate); + assert_float_equal(TIME.dynamicDelta, 0.014f, 0.0001f); + assert_float_equal(TIME.dynamicTime, TIME_STEP + TIME.dynamicDelta, 0.0001f); + assert_float_equal(TIME.delta, TIME_STEP, 0.0001f); // Not changed + assert_float_equal(TIME.time, TIME_STEP, 0.0001f); // Not changed + + // Simulate total 16ms elapsed (2ms more) + SDL_GETTICKS_TICKS = 16; + SDL_GETTICKS_CALLED = false; + timeUpdate(); + assert_true(SDL_GETTICKS_CALLED); + assert_false(TIME.dynamicUpdate); + assert_float_equal(TIME.dynamicDelta, 0.002f, 0.0001f); + assert_float_equal(TIME.dynamicTime, TIME_STEP * 2, 0.0001f); + assert_float_equal(TIME.delta, TIME_STEP, 0.0001f); + assert_float_equal(TIME.time, TIME_STEP * 2, 0.0001f); // Stepped once + + // Second test, half step (16 + 8) + SDL_GETTICKS_TICKS = 24; + SDL_GETTICKS_CALLED = false; + timeUpdate(); + assert_true(SDL_GETTICKS_CALLED); + assert_true(TIME.dynamicUpdate); + assert_float_equal(TIME.dynamicDelta, 0.008f, 0.0001f); + assert_float_equal(TIME.dynamicTime, TIME_STEP + 0.024f, 0.0001f); + assert_float_equal(TIME.delta, TIME_STEP, 0.0001f); + assert_float_equal(TIME.time, TIME_STEP * 2, 0.0001f);// Unchanged. + + // Third test, another half step, leading to non dynamic update + SDL_GETTICKS_TICKS = 32; + SDL_GETTICKS_CALLED = false; + timeUpdate(); + assert_true(SDL_GETTICKS_CALLED); + assert_false(TIME.dynamicUpdate); + assert_float_equal(TIME.dynamicDelta, 0.008f, 0.0001f); + assert_float_equal(TIME.dynamicTime, TIME_STEP + 0.032f, 0.0001f); + assert_float_equal(TIME.delta, TIME_STEP, 0.0001f); + assert_float_equal(TIME.time, TIME_STEP * 3, 0.0001f); + + // Fourth test, large jump, should only step once + SDL_GETTICKS_TICKS = 100; // Simulate 100ms elapsed + SDL_GETTICKS_CALLED = false; + timeUpdate(); + assert_true(SDL_GETTICKS_CALLED); + assert_false(TIME.dynamicUpdate); + assert_float_equal(TIME.dynamicDelta, 0.068f, 0.0001f); + assert_float_equal(TIME.dynamicTime, TIME_STEP + 0.100f, 0.0001f); + assert_float_equal(TIME.delta, TIME_STEP, 0.0001f); + assert_float_equal(TIME.time, TIME_STEP * 4, 0.0001f); + + // Fifth test, despite a small time passing the game should be trying to catch + // and running extra ticks + SDL_GETTICKS_TICKS = 104; // Simulate 4ms elapsed + SDL_GETTICKS_CALLED = false; + timeUpdate(); + assert_true(SDL_GETTICKS_CALLED); + assert_false(TIME.dynamicUpdate); + assert_float_equal(TIME.dynamicDelta, 0.004f, 0.0001f); + assert_float_equal(TIME.dynamicTime, TIME_STEP + 0.104f, 0.0001f); + assert_float_equal(TIME.delta, TIME_STEP, 0.0001f); + assert_float_equal(TIME.time, TIME_STEP * 5, 0.0001f); +} + +int main(int argc, char **argv) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_timeInit), + cmocka_unit_test(test_timeUpdate), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} \ No newline at end of file