AI Entities - zeroKilo/GROBackendWV GitHub Wiki

Overview

In AI.dll, entities are all the objects that can be created via network. The most important are AI_EntityPlayer - player object, and AI_EntityPlayerAbstract - player abstraction object.

All the AI_EntityPlayer objects must have corresponding abstract player objects.

AI_EntityPlayerAbstract is also used as a highest-level server instance, created on the server, then serialized, sent and updated to all the player clients (see cObjectManager::InitEntityAndReplicate). There each local copy of the server's abstract player entity controls local events like explosions, ability usage etc.

Inheritance

Entities inherit from one another. As mentioned above, the most important are AI_EntityPlayer and AI_EntityPlayerAbstract. If an entity receives replicated data, the fields are listed as a sublist. The inheritance paths are (every lower class extends the upper):

AI_EntityPlayer

  • AI_BaseGAO - base class with core game object (GAO) fields, not considered an entity
  • AI_Entity - base entity class, has its own handle and serialization flags
  • AI_EntityConcrete- extension for some other entity objects, not really important
  • AI_EntityDynamic - adds health object and base player fields like team and soldier class IDs
  • AI_EntityPawn - adds physics handling and a part of player's inventory management
    • m_ReplicatedPosition (3D float vector)
    • m_ReplicatedAngle (2D float vector)
    • m_CritSalt (uint)
    • m_CritRandSeed (uint)
    • m_ShootPosition (3D float vector)
    • m_ShootTargetHandle (uint)
    • m_WhistlingBullet (byte)
    • m_Rush (byte)
    • m_PlayerFire (2x byte)
    • m_CurrentWeaponSlot (byte)
    • m_WantedWeaponSlot (byte)
    • m_OldWeaponSlot (byte)
  • AI_EntityHuman - adds animation handling, stances, cover interactions, more inventory management
    • m_ReplicatedCamPitch (2x float)
    • m_GoToPosition (3D float vector)
    • m_MoveMode (byte)
    • m_FocusedEntityReplication (byte)
    • m_CoverHeight (float)
    • m_CoverFlagWanted (short)
    • m_CoverNormal (3D float vector)
    • m_State (byte)
    • m_StateServer (byte)
    • m_LaserSightStateCurr (byte)
    • m_SlideVelocity (float)
    • m_SlideToRosaceAnim (byte)
    • m_BlitzShieldArmed (byte)
    • m_OrderStatus (byte)
    • m_FireModeType (byte)
    • m_RollAnimIndex (byte)
    • m_ReplicatedPowerPC (byte + float)
    • m_CurrentEnergyPC (float)
    • m_KikooMoveCount (short)
    • m_Mood (uint)
    • m_HitPart (byte)
    • m_LeftHandSide (byte)
  • AI_EntityPlayer - final player object, holds weapon objects
    • m_bHealthRegenActive (byte)

AI_EntityPlayerAbstract

  • AI_Entity
  • AI_EntityService - not important
  • AI_EntityPlayerAbstract - final player abstraction object
    • m_DeathCount (uint)
    • m_AbilityInventoryId (uint)
    • m_PassiveAbilityInventoryId (uint)
    • m_DesiredWeaponIds (4x uint)
    • m_AchievementPoints (uint)
    • m_HelmetInventoryId (uint)
    • m_ArmorTierInventoryId (uint)
    • m_SpawnBlockingReasons (short)
    • m_Class (byte)
    • m_ClassLevel (short)
    • m_PortraitId (uint)
    • m_PersonaName (string)

Class IDs

Entities have internal IDs referred to as 'class'. They are used to create entity handles. The IDs are:

  • 1 - AI_Entity
  • 2 - AI_EntityService
  • 3 - AI_EntityConcrete
  • 4 - AI_EntityDynamic
  • 10 - AI_EntityPawn
  • 11 - AI_EntityHuman
  • 31 - AI_EntityPlayer
  • 33 - AI_EntityPlayerAbstract

Handles

Entity handles allow referencing a particular entity. Here's how server entity handle is generated:

_DWORD *__cdecl AI_EntityPlayerAbstract::ds_GetDSHandle(_DWORD *handle, AI_EntityPlayerAbstract *entity)
{
  cEntityManager *entMan; // eax
  int maxHandle; // esi

  *handle = 0;
  if ( cEntityManager::Instance )
  {
    entMan = cEntityManager::Get();
    maxHandle = entMan->nextNewHandle;
    entMan->nextNewHandle = maxHandle + 1;
    handle = (maxHandle + ((entity->pVMT->AI_EntityPlayerAbstract::GetEntityClassId)() << 24));// get 31 for entityPlayer or 33 for abstract; 33 << 24 = 0x21000000
    hEntity::GetHandle(handle, &handle);
  }
  return handle;
}

Since entity class IDs are used to generate a handle, it allows entity class identification as shown below (from cObjectManager::BroadcastMessage):

entity = ((*ptr_DLLEngine.vmt)->ptr_yeti_sub_416F00__CreateEntityObject)(obj, "AI_Entity");
        if ( entity )
        {
          className = (entity->pVMT->AI_EntityPlayer::GetClassName)(entity, handle);
          DBG_SendSessionLog(
            0,
            "cObjectManager::BroadcastMessage ( 268 ) ",
            "Object Created [Owner=0x%x, Class=%s, Object Handle=0x%x]",
            Owner,
            className);
          AI_EntityPlayer::SetMasterFlag(entity, Owner);
          v11 = dynamicBankId;
          LoadFrom = entity->pVMT->AI_EntityPlayer::LoadFrom;
          entity->bankIndex = dynamicBankElementId;
          entity->bankId = v11;
          (LoadFrom)(entity, &v29 + 1);         // deserialization
          AI_EntityPlayer::CallInitEntityAbstractOrPlayer(entity, st7_0, 0);// init deserialized entity
        }

Typical handle validation looks like this:

bool __cdecl cEntityManager::ValidHandle(unsigned int handle)
{
  unsigned int handleRsh; // esi
  bool isValid; // bl

  if ( !cEntityManager::Instance || !AI_NetworkManager::Instance )
    return 1;
  if ( handle )
  {
    handleRsh = handle >> 24;
    if ( AI_NetworkManager::IsServer() )
    {
      if ( (handle & 0xFFFFFF) < cEntityManager::Get()->nextNewHandle && handleRsh < 102 )
        return 1;
      isValid = 0;
    }
    else                                        // client
    {
      isValid = handleRsh < 102;
      if ( handleRsh < 102 )
        return isValid;
    }
    dword_102E8610 = rev_SendErrorMessage(
                       dword_102E8610 != 1,
                       "..\\SRC\\Entity\\cEntityManager.cpp",
                       "cEntityManager::ValidHandle",
                       212,
                       "cEntityManager::ValidHandle invalid handle. Please crash");
    if ( dword_102E8610 == 3 )
      __debugbreak();
    return isValid;
  }
  return 1;
}

Serialization flags

Serialization flags is a working name for 0x30 entity uint that holds flags indicating entity's network role, which is core for proper replication and duplicated objects handling. First 6 bits are used:

  • Bit 1
    • set: master entity (local player)
    • unset: duplica/replica entity
  • Bit 2
    • set: server entity
    • unset: client entity
  • Bit 4
    • set: entity is registered by cEntityManager
    • unset: entity was not registered by cEntityManager
  • Bit 8
    • set: entity is initialized
    • unset: entity is not initialized
  • Bit 16
    • set: not seen used
    • unset: not seen used
  • Bit 32
    • set: fire Brain event
    • unset: set Brain flag

Other objects

There are other objects that inherit from AI_Entity:

  • AI_DynObject
  • AI_EntityDedicatedSpy