LemonUIbyLemon/LemonUI

Replace Game.GameTime with GET_GAME_TIMER in RPH

Closed this issue · 3 comments

Why is that a UINT if the game reports it as an INT?

kagikn commented

Actually, RPH returns the game time in the same way as how rage::fwTimer::GetTimeInMilliseconds instended, which returns an unsigned int. While it seems that all the other frameworks avoid using unsigned values for the native. well, for the SHVDN case, it looks like crosire didn't bother to opt for unsigned values for any game APIs.

A decompiled code of GetTimeInMilliseconds of a build of roadpathsgenerator_win64_beta with pdb info (probably built in 2022):

__int64 __fastcall rage::fwTimer::GetTimeInMilliseconds()
{
  return rage::fwTimer::sm_gameTime.m_Time; // `sm_gameTime` is a `rage::fwTimeSet`
}

The equivalent code should be included in GTA5.exe as well. You can't tell if the return value is signed just by looking at how the function is defined because the function only contains mov and ret in the x64 assembly code. However, since some of the callers of GetTimeInMilliseconds compares and do branch jumps with jnb or jbe, which are unsigned, it would be safe to assume that m_Time of rage::fwTimeSet is supposed to be unsigned.

Another reason why I think m_Time is an unsigned int is, that signed integer overflows falls into undefined behaviors in C++ (so the compiler can do anything) while unsigned integer overflows are well defined, but there's no really a good reason to choose a signed integer over unsigned one for a monotonically increasing timer. For example, In C#, you can calculate the time difference with right - left with no problems even if they're signed and an overflow happens, but the C++ compiler can ignore the case where an signed overflow happens.

Would you recommend switching everything to uint or go back to int? I guess uint would be the right choice.

I ended up switching to long values.