Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

game.time() wraps around after ~30 game days #153

Open
Decane opened this issue Feb 27, 2023 · 1 comment
Open

game.time() wraps around after ~30 game days #153

Decane opened this issue Feb 27, 2023 · 1 comment

Comments

@Decane
Copy link
Owner

Decane commented Feb 27, 2023

It turns out game.time() wraps around to 0 after ~30¼ game days ((2³² - 1) - 1,679,936,192 game milliseconds). One option would be to convert the C++ struct returned by game.get_game_time() into seconds and use that in place of game.time() wherever sub-second precision is not required. The Lua built-in os.time function could be used for the conversion, albeit at the cost of sub-second rounding errors. Here is an example of how to implement that: 9919135. If millisecond precision were required, get_game_time_in_seconds() in the aforementioned commit could be modified to return a fractional os.time(time_table) + ms * 0.001, however saving it without losing precision might be tricky since the engine doesn't expose a double-precision floating point save method.

Saving a large u32 — like that returned by get_game_time_in_seconds() in the commit above, assuming not modified for fractional precision — as a float32 is a bad idea; we begin losing precision with odd numbers above 16,777,216 (2²⁴) and even numbers above 33,554,432 (2²⁵). That means we can only save up to ~194.18 days of seconds into a float32 before we begin seeing ±1 second rounding errors. Past ~388.36 days, these grow to ±2 seconds. If counting were to begin from the epoch (as in os.time), we'd already be way over the precision limit when the game starts at 06:10 2011/09/10, and our rounding errors would be immense (±64 seconds). Even if counting began from 2011/01/01 instead, we'd be up to 21,795,000 seconds when the game starts. We could hard-code the counting to begin at the vanilla game start time, but that could break mods that use a different start time. As there seems to be no way to access the game start time directly from Lua, my solution against game.time() precision loss in f4b5ce8 was to detect fractional numbers in xr_logic.pstor_save_all and save those as float32, else save as s32 or u32 yielding 2,147,483,647 (2³¹ - 1) or 4,294,967,295 (2³² - 1) seconds of wiggle room, respectively.

To save a fractional get_game_time_in_seconds() without precision loss would likely require saving the fractional part separately, perhaps as a u16.

@Decane
Copy link
Owner Author

Decane commented Feb 27, 2023

Another quirk of vanilla X-Ray seems to be that the precision of integers >= 2³¹ saved as u32 is limited to the nearest multiple of 256. So, any integers >= 2,147,483,648 will be rounded to the nearest multiple of 256 when saved using e.g. xr_logic.pstor_save_all. Thus, if the number saved is in milliseconds, then the number loaded would have a rounding error of up to 128 milliseconds. No big deal if we only need to-the-nearest-second precision, but suppose we're saving a number denominated in seconds. We'd have a rounding error of over 2 minutes in the worst-case scenario!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant