//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #ifndef EDICT_H #define EDICT_H #ifdef _WIN32 #pragma once #endif #include "bitvec.h" #include "cmodel.h" #include "const.h" #include "engine/ICollideable.h" #include "globalvars_base.h" #include "iserverentity.h" #include "iservernetworkable.h" #include "mathlib/vector.h" struct edict_t; //----------------------------------------------------------------------------- // Purpose: Defines the ways that a map can be loaded. //----------------------------------------------------------------------------- enum MapLoadType_t { MapLoad_NewGame = 0, MapLoad_LoadGame, MapLoad_Transition, MapLoad_Background, }; //----------------------------------------------------------------------------- // Purpose: Global variables shared between the engine and the game .dll //----------------------------------------------------------------------------- class CGlobalVars : public CGlobalVarsBase { public: CGlobalVars(bool bIsClient); public: // Current map string_t mapname; int mapversion; string_t startspot; MapLoadType_t eLoadType; // How the current map was loaded bool bMapLoadFailed; // Map has failed to load, we need to kick back to the // main menu // game specific flags bool deathmatch; bool coop; bool teamplay; // current maxentities int maxEntities; int serverCount; }; inline CGlobalVars::CGlobalVars(bool bIsClient) : CGlobalVarsBase(bIsClient) { serverCount = 0; } class CPlayerState; class IServerNetworkable; class IServerEntity; #define FL_EDICT_CHANGED \ (1 << 0) // Game DLL sets this when the entity state changes // Mutually exclusive with FL_EDICT_PARTIAL_CHANGE. #define FL_EDICT_FREE (1 << 1) // this edict if free for reuse #define FL_EDICT_FULL (1 << 2) // this is a full server entity #define FL_EDICT_FULLCHECK \ (0 << 0) // call ShouldTransmit() each time, this is a fake flag #define FL_EDICT_ALWAYS (1 << 3) // always transmit this entity #define FL_EDICT_DONTSEND (1 << 4) // don't transmit this entity #define FL_EDICT_PVSCHECK \ (1 << 5) // always transmit entity, but cull against PVS // Used by local network backdoor. #define FL_EDICT_PENDING_DORMANT_CHECK (1 << 6) // This is always set at the same time EFL_DIRTY_PVS_INFORMATION is set, but it // gets cleared in a different place. #define FL_EDICT_DIRTY_PVS_INFORMATION (1 << 7) // This is used internally to edict_t to remember that it's carrying a // "full change list" - all its properties might have changed their value. #define FL_FULL_EDICT_CHANGED (1 << 8) // Max # of variable changes we'll track in an entity before we treat it // like they all changed. #define MAX_CHANGE_OFFSETS 19 #define MAX_EDICT_CHANGE_INFOS 100 class CEdictChangeInfo { public: // Edicts remember the offsets of properties that change unsigned short m_ChangeOffsets[MAX_CHANGE_OFFSETS]; unsigned short m_nChangeOffsets; }; // Shared between engine and game DLL. class CSharedEdictChangeInfo { public: CSharedEdictChangeInfo() { m_iSerialNumber = 1; } // Matched against edict_t::m_iChangeInfoSerialNumber to determine if its // change info is valid. unsigned short m_iSerialNumber; CEdictChangeInfo m_ChangeInfos[MAX_EDICT_CHANGE_INFOS]; unsigned short m_nChangeInfos; // How many are in use this frame. }; extern CSharedEdictChangeInfo *g_pSharedChangeInfo; class IChangeInfoAccessor { public: inline void SetChangeInfo(unsigned short info) { m_iChangeInfo = info; } inline void SetChangeInfoSerialNumber(unsigned short sn) { m_iChangeInfoSerialNumber = sn; } inline unsigned short GetChangeInfo() const { return m_iChangeInfo; } inline unsigned short GetChangeInfoSerialNumber() const { return m_iChangeInfoSerialNumber; } private: unsigned short m_iChangeInfo; unsigned short m_iChangeInfoSerialNumber; }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- // NOTE: YOU CAN'T CHANGE THE LAYOUT OR SIZE OF CBASEEDICT AND REMAIN COMPATIBLE // WITH HL2_VC6!!!!! class CBaseEdict { public: // Returns an IServerEntity if FL_FULLEDICT is set or NULL if this // is a lightweight networking entity. IServerEntity *GetIServerEntity(); const IServerEntity *GetIServerEntity() const; IServerNetworkable *GetNetworkable(); IServerUnknown *GetUnknown(); // Set when initting an entity. If it's only a networkable, this is false. void SetEdict(IServerUnknown *pUnk, bool bFullEdict); int AreaNum() const; const char *GetClassName() const; bool IsFree() const; void SetFree(); void ClearFree(); bool HasStateChanged() const; void ClearStateChanged(); void StateChanged(); void StateChanged(unsigned short offset); void ClearTransmitState(); void SetChangeInfo(unsigned short info); void SetChangeInfoSerialNumber(unsigned short sn); unsigned short GetChangeInfo() const; unsigned short GetChangeInfoSerialNumber() const; public: // NOTE: this is in the edict instead of being accessed by a virtual because // the engine needs fast access to it. NOTE: YOU CAN'T CHANGE THE LAYOUT OR // SIZE OF CBASEEDICT AND REMAIN COMPATIBLE WITH HL2_VC6!!!!! #ifdef _XBOX unsigned short m_fStateFlags; #else int m_fStateFlags; #endif // NOTE: this is in the edict instead of being accessed by a virtual because // the engine needs fast access to it. int m_NetworkSerialNumber; // NOTE: m_EdictIndex is an optimization since computing the edict index // from a CBaseEdict* pointer otherwise requires divide-by-20. values for // m_NetworkSerialNumber all fit within a 16-bit integer range, so we're // repurposing the other 16 bits to cache off the index without changing // the overall layout or size of this struct. existing mods compiled with // a full 32-bit serial number field should still work. henryg 8/17/2011 #if VALVE_LITTLE_ENDIAN short m_NetworkSerialNumber; short m_EdictIndex; #else short m_EdictIndex; short m_NetworkSerialNumber; #endif // NOTE: this is in the edict instead of being accessed by a virtual because // the engine needs fast access to it. IServerNetworkable *m_pNetworkable; protected: IServerUnknown *m_pUnk; public: IChangeInfoAccessor *GetChangeAccessor(); // The engine implements this and // the game .dll implements as const IChangeInfoAccessor *GetChangeAccessor() const; // The engine implements this and the game .dll implements as // as callback through to the engine!!! // NOTE: YOU CAN'T CHANGE THE LAYOUT OR SIZE OF CBASEEDICT AND REMAIN // COMPATIBLE WITH HL2_VC6!!!!! This breaks HL2_VC6!!!!! References a // CEdictChangeInfo with a list of modified network props. // unsigned short m_iChangeInfo; // unsigned short m_iChangeInfoSerialNumber; friend void InitializeEntityDLLFields(edict_t *pEdict); }; //----------------------------------------------------------------------------- // CBaseEdict inlines. //----------------------------------------------------------------------------- inline IServerEntity *CBaseEdict::GetIServerEntity() { if (m_fStateFlags & FL_EDICT_FULL) return (IServerEntity *)m_pUnk; else return 0; } inline bool CBaseEdict::IsFree() const { return (m_fStateFlags & FL_EDICT_FREE) != 0; } inline bool CBaseEdict::HasStateChanged() const { return (m_fStateFlags & FL_EDICT_CHANGED) != 0; } inline void CBaseEdict::ClearStateChanged() { m_fStateFlags &= ~(FL_EDICT_CHANGED | FL_FULL_EDICT_CHANGED); SetChangeInfoSerialNumber(0); } inline void CBaseEdict::StateChanged() { // Note: this should only happen for properties in data tables that used // some kind of pointer dereference. If the data is directly offsetable m_fStateFlags |= (FL_EDICT_CHANGED | FL_FULL_EDICT_CHANGED); SetChangeInfoSerialNumber(0); } inline void CBaseEdict::StateChanged(unsigned short offset) { if (m_fStateFlags & FL_FULL_EDICT_CHANGED) return; m_fStateFlags |= FL_EDICT_CHANGED; IChangeInfoAccessor *accessor = GetChangeAccessor(); if (accessor->GetChangeInfoSerialNumber() == g_pSharedChangeInfo->m_iSerialNumber) { // Ok, I still own this one. CEdictChangeInfo *p = &g_pSharedChangeInfo->m_ChangeInfos[accessor->GetChangeInfo()]; // Now add this offset to our list of changed variables. for (unsigned short i = 0; i < p->m_nChangeOffsets; i++) if (p->m_ChangeOffsets[i] == offset) return; if (p->m_nChangeOffsets == MAX_CHANGE_OFFSETS) { // Invalidate our change info. accessor->SetChangeInfoSerialNumber(0); m_fStateFlags |= FL_FULL_EDICT_CHANGED; // So we don't get in here again. } else { p->m_ChangeOffsets[p->m_nChangeOffsets++] = offset; } } else { if (g_pSharedChangeInfo->m_nChangeInfos == MAX_EDICT_CHANGE_INFOS) { // Shucks.. have to mark the edict as fully changed because we don't // have room to remember this change. accessor->SetChangeInfoSerialNumber(0); m_fStateFlags |= FL_FULL_EDICT_CHANGED; } else { // Get a new CEdictChangeInfo and fill it out. accessor->SetChangeInfo(g_pSharedChangeInfo->m_nChangeInfos); g_pSharedChangeInfo->m_nChangeInfos++; accessor->SetChangeInfoSerialNumber( g_pSharedChangeInfo->m_iSerialNumber); CEdictChangeInfo *p = &g_pSharedChangeInfo->m_ChangeInfos[accessor->GetChangeInfo()]; p->m_ChangeOffsets[0] = offset; p->m_nChangeOffsets = 1; } } } inline void CBaseEdict::SetFree() { m_fStateFlags |= FL_EDICT_FREE; } // WARNING: Make sure you don't really want to call ED_ClearFreeFlag which will // also // remove this edict from the g_FreeEdicts bitset. inline void CBaseEdict::ClearFree() { m_fStateFlags &= ~FL_EDICT_FREE; } inline void CBaseEdict::ClearTransmitState() { m_fStateFlags &= ~(FL_EDICT_ALWAYS | FL_EDICT_PVSCHECK | FL_EDICT_DONTSEND); } inline const IServerEntity *CBaseEdict::GetIServerEntity() const { if (m_fStateFlags & FL_EDICT_FULL) return (IServerEntity *)m_pUnk; else return 0; } inline IServerUnknown *CBaseEdict::GetUnknown() { return m_pUnk; } inline IServerNetworkable *CBaseEdict::GetNetworkable() { return m_pNetworkable; } inline void CBaseEdict::SetEdict(IServerUnknown *pUnk, bool bFullEdict) { m_pUnk = pUnk; if ((pUnk != NULL) && bFullEdict) { m_fStateFlags = FL_EDICT_FULL; } else { m_fStateFlags = 0; } } inline int CBaseEdict::AreaNum() const { if (!m_pUnk) return 0; return m_pNetworkable->AreaNum(); } inline const char *CBaseEdict::GetClassName() const { if (!m_pUnk) return ""; return m_pNetworkable->GetClassName(); } inline void CBaseEdict::SetChangeInfo(unsigned short info) { GetChangeAccessor()->SetChangeInfo(info); } inline void CBaseEdict::SetChangeInfoSerialNumber(unsigned short sn) { GetChangeAccessor()->SetChangeInfoSerialNumber(sn); } inline unsigned short CBaseEdict::GetChangeInfo() const { return GetChangeAccessor()->GetChangeInfo(); } inline unsigned short CBaseEdict::GetChangeInfoSerialNumber() const { return GetChangeAccessor()->GetChangeInfoSerialNumber(); } //----------------------------------------------------------------------------- // Purpose: The engine's internal representation of an entity, including some // basic collision and position info and a pointer to the class wrapped on top // of the structure //----------------------------------------------------------------------------- struct edict_t : public CBaseEdict { public: ICollideable *GetCollideable(); // The server timestampe at which the edict was freed (so we can try to use // other edicts before reallocating this one) float freetime; }; inline ICollideable *edict_t::GetCollideable() { IServerEntity *pEnt = GetIServerEntity(); if (pEnt) return pEnt->GetCollideable(); else return NULL; } #endif // EDICT_H