Description
Team assignments are net synced but team selections are not which can be problematic. This behavior can be exploited with game breaking consequences. GenTool fixes this issue by preventing the selection of newly assigned teams until the assignment is completed.
For game.dat
Inject naked function CC_AssignTeamInUI
at address 0x615BE0
taking 5 bytes.
Inject naked function CC_SelectTeamInUI
at address 0x615C55
taking 5 bytes.
CC_AssignTeamInUI
will just save a state and then continue code execution normally.
CC_SelectTeamInUI
will check when team was assigned, and if it was less frames ago than the match latency (10-64), then it will skip the user team selection action.
// When assigning a team to an object it takes at least 10 frames till the job is done
// Meanwhile team selection happens instantly - this is problematic and abusable
// We need to make sure that a team cannot be selected when it was just assigned earlier
//////////////////////////////////////////////////////////////////////////////////////
DWORD ccAssignTeamInUiRet;
DWORD ccSelectTeamInUiRet;
uint32 ccTeamNumber;
__declspec(naked) void CC_AssignTeamInUI()
{
__asm pop [ccAssignTeamInUiRet]
__asm ADD EAX,-0x2E
__asm mov ccTeamNumber,eax
__asm pushad
CGameAccess::Instance.OnAssignTeamInUI(ccTeamNumber);
__asm popad
__asm CMP EAX,EBX
__asm push [ccAssignTeamInUiRet]
__asm ret
}
__declspec(naked) void CC_SelectTeamInUI()
{
__asm pop [ccSelectTeamInUiRet]
__asm ADD EAX,-0x38
__asm mov ccTeamNumber,eax
__asm pushad
if (CGameAccess::Instance.GroupingWasExploited(ccTeamNumber))
{
__asm popad
__asm mov eax,-1
}
else
{
__asm popad
}
__asm CMP EAX,EBX
__asm push [ccSelectTeamInUiRet]
__asm ret
}
Here is high level logic: In OnAssignTeamInUI
function we just remember at which frame a team was assigned to something.
void CGameAccess::OnAssignTeamInUI(uint32 teamNumber)
{
if (teamNumber < 10)
{
Hack_GetFrameCount(m_teamAssignmentFrameId[teamNumber]);
}
}
And in GroupingWasExploited
we check if we passed the latency time we should wait between Team assignment and Team selection. If enough frames passed, then it is good to continue as usual. If not enough frames passed, we return false and team selection is aborted. User can try to select team later again.
bool CGameAccess::GroupingWasExploited(uint32 teamNumber)
{
if (teamNumber < 10)
{
uint32 currentFrameId = 0;
uint32 latencyFrameCount;
Hack_GetFrameCount(currentFrameId);
if (Hack_GetLatency(latencyFrameCount))
{
// This will not hit in Single Player, because there is no latency
latencyFrameCount += 1; // Add a little lean time just to make sure
if (currentFrameId - m_teamAssignmentFrameId[teamNumber] <= latencyFrameCount)
{
return true;
}
}
}
return false;
}