halflife
halflife copied to clipboard
Race condition between user messages and engine's svc_updateuserinfo message
In Opposing Force, when a player is assigned to a team in ClientPutInServer or at any time in the same frame as that function execution, the client will receive the TeamInfo message before the engine has received the svc_updateuserinfo message. When the client rebuilds the teams it won't have the new player info yet, so the scoreboard won't include them in the list.
The TeamInfo message is sent from CHalfLifeCTFplay::ChangePlayerTeam. Here is the version from the SDK, which is the player model-based teamplay mode:
https://github.com/ValveSoftware/halflife/blob/c7240b965743a53a29491dd49320c88eecf6257b/dlls/teamplay_gamerules.cpp#L294-L298
Received here: https://github.com/ValveSoftware/halflife/blob/c7240b965743a53a29491dd49320c88eecf6257b/cl_dll/vgui_TeamFortressViewport.cpp#L2494-L2512
The client queries the engine for player info here: https://github.com/ValveSoftware/halflife/blob/c7240b965743a53a29491dd49320c88eecf6257b/cl_dll/vgui_ScorePanel.cpp#L482
https://github.com/ValveSoftware/halflife/blob/c7240b965743a53a29491dd49320c88eecf6257b/cl_dll/vgui_TeamFortressViewport.cpp#L2094-L2103
This happens because the new player's user info is sent using svc_updateuserinfo, which is added to the server's reliable datagram buffer. This buffer is appended to each player's message buffer. MSG_ONE user messages are also added to the message buffer, so the client will first receive the TeamInfo message and later on in the same frame it will receive the new player's info.
An Opposing Force player can join a team in the same frame as the ClientPutInServer function's execution if the relevant client commands are executed at the right time. In my case i was simulating this by adding a bot and assigning it to a team and character immediately, which triggered the bug.
Unfortunately there may not be a solution for this bug, at least not a simple one. The reliable datagram buffer is used by MSG_ALL user messages so changing the way the buffers are combined could trigger even more race conditions.
Although this bug only manifested in Opposing Force it can happen in any game that sends user messages that result in the client trying to use data retrieved with gEngfuncs.pfnGetPlayerInfo that involves players whose user info has not yet been processed by the client at that time.