Entity Commands - zeroKilo/GROBackendWV GitHub Wiki
Memory Layout
struct Command
{
_DWORD pVMT;
_DWORD dword4;
_DWORD cmdID;
_DWORD targetID;
Data optionalData;
};
Networking
Format
Entity commands are sent inside of broadcast messages. They are first converted into a BitBuffer structure, which is then serialized into BM parameters and sent.
BitBuffer-to-BM-params conversion looks like this:
const char *__cdecl BitBuffer::WriteToBM(BitBuffer *this, int msgId, unsigned __int8 a3)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
AIDLL::BM_ResetNetPushQueue(msgId, a3);
begin = this->bufferBitSize;
shift = this->bufferBitSize >> 3;
if ( begin & 7 )
++shift;
result = AIDLL::BM_PushNetParamI8(msgId, a3, 8 * shift - begin);// write buffer size
if ( this->bufferByteSize )
buff = this->buffer;
else
buff = 0;
for ( ; shift; --shift ) // write buffer
result = AIDLL::BM_PushNetParamI8(msgId, a3, *buff++);
return result;
}
Broadcast messages
Broadcast messages that store entity commands are:
- 0x96 (standard entity commands)
- 0xA7 (setting sequence casts)
- 0x226 (some animation events for
AI_EntityPlayer
/AI_EntityHuman
)
Command List
0x00 Invalid
0x01 HasHitATarget
0x02 ThrowGrenade
0x03 WillBeBump
0x04 MoveToPos
0x05 SetInvincible
0x06 EntityHeartBeat
0x07 Freeze
0x08 Teleport
0x09 DamageTaken
0x0A DamageGivenFeedback
0x0B PlayDead
0x0C PlayerRefillAmmo
0x0D PlayerRepAmmoInfo
0x0E PowerButtonStateChangePC
0x0F ForcePowerStatePC
0x10 PowerOverchargingStartedPC
0x11 PowerOverchargingStoppedPC
0x12 PowerOverchargedPC
0x13 PowerCoolingDownPC
0x14 BlackedOutPC
0x15 CreateBlackoutExplosion
0x16 EnemyDetectedWithHBS
0x17 SetADSFlags
0x18 SetADSConeAngle
0x19 SetADSExtraFOV
0x1A CancelBlitz
0x1B HeartBeat
0x1C FallingDamage
0x1D HasMissATarget
0x1E UpdateHealth
0x1F UpdateDefaultHealth
0x20 UpdateHealthState
0x21 BulletStartMarker
0x22 BulletEndMarker
0x23 ShowTotalDamage
0x24 Melee
0x25 PushbackEffect
0x26 LaserSightInfo
0x27 LaserWaypoint
0x28 Gesture
0x29 GestureAnimIdx
0x2A GestureAnimIdxStanceIdx - sets player's stance (prone/crouch/standing)
0x2B Cover
0x2C CoverEnter
0x2D CoverPeekSide
0x2E CoverPeekUp
0x2F AmmoLootAddedInServer
0x30 AmmoLootTakenConfirmedInServer
0x31 AmmoLootRemovedInMaster
0x32 ReloadInventory
0x33 AbstractPlayerChangeState
0x34 AbstractPlayerRequestSpawn
0x35 AbstractPlayerClientReady
0x36 FireAction
0x37 DecreaseAegisOwnerEnergy
0x38 Suicide
0x39 Kill
0x3A BlitzShieldHit
0x3B ReduceAmmoBoostClipByOne
0x3C ToggleAmmoBoost
0x3D CheatCommand
0x3E Count
Target List
0x00 All
0x01 Server
0x02 Master
0x03 Count
Processing
Only master or server entities can process commands. targetId
defines who can process a command (from AI_Entity::ProcessCmdFromBuffer
):
targetId = cmd->targetID;
v6 = 0;
v41 = -1;
if ( !targetId ) // targetId 0 - All
{
(this->pVMT->AI_EntityPlayer::ProcessCmd)(cmd);// target 0 processes cmds everytime
v11 = cmd->pVMT->Command::WriteToBitBuffer;
v39 = v35;
v11(cmd, &bitBuff3);
v9 = v35;
bTarget0 = 1;
LABEL_18:
v6 = v9 - v39;
goto LABEL_19;
}
targetMinus1 = targetId - 1;
if ( !targetMinus1 ) // targetId 1 - Server
{
if ( this->serializationFlags & 2 ) // server
{
(this->pVMT->AI_EntityPlayer::ProcessCmd)(cmd);// target 1 server processes server cmds
}
else // client
{
v10 = cmd->pVMT->Command::WriteToBitBuffer;
v39 = v27;
v10(cmd, &bitBuff1);
v6 = v27 - v39;
v41 = AI_NetworkManager::Get()->serverId;
bClient = 1;
}
goto LABEL_19;
}
if ( targetMinus1 == 1 ) // targetId 2 - Master
{
if ( this->serializationFlags & 1 ) // master
{
(this->pVMT->AI_EntityPlayer::ProcessCmd)(cmd);// target 2 master processes master cmds
goto LABEL_19;
}
v8 = cmd->pVMT->Command::WriteToBitBuffer;// replica
v39 = v31;
v8(cmd, &bitBuff2);
v9 = v31;
v41 = this->playerStationID;
bReplica = 1;
goto LABEL_18;
}