diff --git a/core/HalfLife2.cpp b/core/HalfLife2.cpp index 6aec953e..2ea8faf2 100644 --- a/core/HalfLife2.cpp +++ b/core/HalfLife2.cpp @@ -70,7 +70,6 @@ CHalfLife2::~CHalfLife2() for (iter=m_Tables.begin(); iter!=m_Tables.end(); iter++) { pInfo = (*iter); - sm_trie_destroy(pInfo->lookup); delete pInfo; } @@ -115,6 +114,8 @@ void CHalfLife2::OnSourceModAllInitialized() m_HinTextMsg = g_UserMsgs.GetMessageIndex("HintText"); m_VGUIMenu = g_UserMsgs.GetMessageIndex("VGUIMenu"); g_ShareSys.AddInterface(NULL, this); + + FindInSendTable("CTFPlayer", "m_nDisguiseClass"); } #if !defined METAMOD_PLAPI_VERSION @@ -129,7 +130,10 @@ IChangeInfoAccessor *CBaseEdict::GetChangeAccessor() return engine->GetChangeAccessor( (const edict_t *)this ); } -SendProp *UTIL_FindInSendTable(SendTable *pTable, const char *name) +bool UTIL_FindInSendTable(SendTable *pTable, + const char *name, + sm_sendprop_info_t *info, + unsigned int offset) { const char *pname; int props = pTable->GetNumProps(); @@ -141,13 +145,19 @@ SendProp *UTIL_FindInSendTable(SendTable *pTable, const char *name) pname = prop->GetName(); if (pname && strcmp(name, pname) == 0) { - return prop; + info->prop = prop; + info->actual_offset = offset + info->prop->GetOffset(); + return true; } if (prop->GetDataTable()) { - if ((prop=UTIL_FindInSendTable(prop->GetDataTable(), name)) != NULL) + if (UTIL_FindInSendTable(prop->GetDataTable(), + name, + info, + offset + prop->GetOffset()) + ) { - return prop; + return true; } } } @@ -187,6 +197,7 @@ typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name) ServerClass *CHalfLife2::FindServerClass(const char *classname) { DataTableInfo *pInfo = _FindServerClass(classname); + if (!pInfo) { return NULL; @@ -207,7 +218,6 @@ DataTableInfo *CHalfLife2::_FindServerClass(const char *classname) if (strcmp(classname, sc->GetName()) == 0) { pInfo = new DataTableInfo; - pInfo->lookup = sm_trie_create(); pInfo->sc = sc; sm_trie_insert(m_pClasses, classname, pInfo); m_Tables.push_back(pInfo); @@ -224,25 +234,46 @@ DataTableInfo *CHalfLife2::_FindServerClass(const char *classname) return pInfo; } +bool CHalfLife2::FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info) +{ + DataTableInfo *pInfo; + sm_sendprop_info_t *prop; + + if ((pInfo = _FindServerClass(classname)) == NULL) + { + return false; + } + + if ((prop = pInfo->lookup.retrieve(offset)) == NULL) + { + sm_sendprop_info_t temp_info; + + if (!UTIL_FindInSendTable(pInfo->sc->m_pTable, offset, &temp_info, 0)) + { + return false; + } + + pInfo->lookup.insert(offset, temp_info); + *info = temp_info; + } + else + { + *info = *prop; + } + + return true; +} + SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset) { - DataTableInfo *pInfo = _FindServerClass(classname); + sm_sendprop_info_t info; - if (!pInfo) + if (!FindSendPropInfo(classname, offset, &info)) { return NULL; } - SendProp *pProp = NULL; - if (!sm_trie_retrieve(pInfo->lookup, offset, (void **)&pProp)) - { - if ((pProp = UTIL_FindInSendTable(pInfo->sc->m_pTable, offset)) != NULL) - { - sm_trie_insert(pInfo->lookup, offset, pProp); - } - } - - return pProp; + return info.prop; } typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset) @@ -272,7 +303,9 @@ void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset) if (offset) { pEdict->StateChanged(offset); - } else { + } + else + { pEdict->StateChanged(); } } diff --git a/core/HalfLife2.h b/core/HalfLife2.h index ca5fb754..e6bcf05a 100644 --- a/core/HalfLife2.h +++ b/core/HalfLife2.h @@ -35,6 +35,7 @@ #include #include #include +#include #include "sm_trie.h" #include "sm_globals.h" #include "sm_queue.h" @@ -52,7 +53,7 @@ using namespace SourceMod; struct DataTableInfo { ServerClass *sc; - Trie *lookup; + KTrie lookup; }; struct DataMapTrie @@ -89,6 +90,7 @@ public: /*void OnSourceModAllShutdown();*/ public: //IGameHelpers SendProp *FindInSendTable(const char *classname, const char *offset); + bool FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info); datamap_t *GetDataMap(CBaseEntity *pEntity); ServerClass *FindServerClass(const char *classname); typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset); diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 6007b001..049ee1d2 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -45,7 +45,7 @@ AdditionalIncludeDirectories="..\;..\systems;..\..\public;..\..\public\sourcepawn;$(HL2SDK)\public;$(HL2SDK)\public\dlls;$(HL2SDK)\public\engine;$(HL2SDK)\public\tier0;$(HL2SDK)\public\tier1;$(HL2SDK)\public\vstdlib;$(HL2SDK)\tier1;$(SOURCEMM);$(SOURCEMM)\sourcemm;$(SOURCEMM)\sourcehook" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SOURCEMOD_MM_EXPORTS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;SOURCEMOD_BUILD;SM_DEFAULT_THREADER" RuntimeLibrary="0" - EnableEnhancedInstructionSet="1" + EnableEnhancedInstructionSet="0" RuntimeTypeInfo="false" UsePrecompiledHeader="0" WarningLevel="3" @@ -128,7 +128,7 @@ MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" - EnableEnhancedInstructionSet="1" + EnableEnhancedInstructionSet="0" RuntimeTypeInfo="false" UsePrecompiledHeader="0" WarningLevel="3" @@ -288,7 +288,7 @@ AdditionalIncludeDirectories="..\;..\systems;..\..\public;..\..\public\sourcepawn;$(HL2SDKOB)\public;$(HL2SDKOB)\public\dlls;$(HL2SDKOB)\public\engine;$(HL2SDKOB)\public\tier0;$(HL2SDKOB)\public\tier1;$(HL2SDKOB)\public\vstdlib;$(HL2SDKOB)\tier1;$(SOURCEMM);$(SOURCEMM)\sourcemm;$(SOURCEMM)\sourcehook" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SOURCEMOD_MM_EXPORTS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;SOURCEMOD_BUILD;SM_DEFAULT_THREADER;ORANGEBOX_BUILD" RuntimeLibrary="0" - EnableEnhancedInstructionSet="1" + EnableEnhancedInstructionSet="0" RuntimeTypeInfo="false" UsePrecompiledHeader="0" WarningLevel="3" @@ -371,7 +371,7 @@ MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" - EnableEnhancedInstructionSet="1" + EnableEnhancedInstructionSet="0" RuntimeTypeInfo="false" UsePrecompiledHeader="0" WarningLevel="3" @@ -453,7 +453,7 @@ MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" - EnableEnhancedInstructionSet="1" + EnableEnhancedInstructionSet="0" RuntimeTypeInfo="false" UsePrecompiledHeader="0" WarningLevel="3" @@ -534,7 +534,7 @@ AdditionalIncludeDirectories="..\;..\systems;..\..\public;..\..\public\sourcepawn;$(HL2SDK)\public;$(HL2SDK)\public\dlls;$(HL2SDK)\public\engine;$(HL2SDK)\public\tier0;$(HL2SDK)\public\tier1;$(HL2SDK)\public\vstdlib;$(HL2SDK)\tier1;$(SOURCEMM142);$(SOURCEMM142)\sourcemm;$(SOURCEMM142)\sourcehook" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SOURCEMOD_MM_EXPORTS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;SOURCEMOD_BUILD;SM_DEFAULT_THREADER" RuntimeLibrary="0" - EnableEnhancedInstructionSet="1" + EnableEnhancedInstructionSet="0" RuntimeTypeInfo="false" UsePrecompiledHeader="0" WarningLevel="3" diff --git a/core/smn_entities.cpp b/core/smn_entities.cpp index dd69c267..f8fe5ecf 100644 --- a/core/smn_entities.cpp +++ b/core/smn_entities.cpp @@ -44,6 +44,16 @@ enum PropType Prop_Data }; +enum PropFieldType +{ + PropField_Unsupported, /**< The type is unsupported. */ + PropField_Integer, /**< Valid for SendProp and Data fields */ + PropField_Float, /**< Valid for SendProp and Data fields */ + PropField_Entity, /**< Valid for Data fields only (SendProp shows as int) */ + PropField_Vector, /**< Valid for SendProp and Data fields */ + PropField_String, /**< Valid for SendProp and Data fields */ +}; + inline edict_t *GetEdict(cell_t num) { edict_t *pEdict = engine->PEntityOfEntIndex(num); @@ -292,7 +302,7 @@ static cell_t GetEntData(IPluginContext *pContext, const cell_t *params) } int offset = params[2]; - if (offset < 0 || offset > 32768) + if (offset <= 0 || offset > 32768) { return pContext->ThrowNativeError("Offset %d is invalid", offset); } @@ -321,7 +331,7 @@ static cell_t SetEntData(IPluginContext *pContext, const cell_t *params) } int offset = params[2]; - if (offset < 0 || offset > 32768) + if (offset <= 0 || offset > 32768) { return pContext->ThrowNativeError("Offset %d is invalid", offset); } @@ -366,7 +376,7 @@ static cell_t GetEntDataFloat(IPluginContext *pContext, const cell_t *params) } int offset = params[2]; - if (offset < 0 || offset > 32768) + if (offset <= 0 || offset > 32768) { return pContext->ThrowNativeError("Offset %d is invalid", offset); } @@ -387,7 +397,7 @@ static cell_t SetEntDataFloat(IPluginContext *pContext, const cell_t *params) } int offset = params[2]; - if (offset < 0 || offset > 32768) + if (offset <= 0 || offset > 32768) { return pContext->ThrowNativeError("Offset %d is invalid", offset); } @@ -413,7 +423,7 @@ static cell_t GetEntDataVector(IPluginContext *pContext, const cell_t *params) } int offset = params[2]; - if (offset < 0 || offset > 32768) + if (offset <= 0 || offset > 32768) { return pContext->ThrowNativeError("Offset %d is invalid", offset); } @@ -441,7 +451,7 @@ static cell_t SetEntDataVector(IPluginContext *pContext, const cell_t *params) } int offset = params[2]; - if (offset < 0 || offset > 32768) + if (offset <= 0 || offset > 32768) { return pContext->ThrowNativeError("Offset %d is invalid", offset); } @@ -463,6 +473,7 @@ static cell_t SetEntDataVector(IPluginContext *pContext, const cell_t *params) return 1; } +/* THIS GUY IS DEPRECATED. */ static cell_t GetEntDataEnt(IPluginContext *pContext, const cell_t *params) { CBaseEntity *pEntity; @@ -474,7 +485,7 @@ static cell_t GetEntDataEnt(IPluginContext *pContext, const cell_t *params) } int offset = params[2]; - if (offset < 0 || offset > 32768) + if (offset <= 0 || offset > 32768) { return pContext->ThrowNativeError("Offset %d is invalid", offset); } @@ -486,11 +497,65 @@ static cell_t GetEntDataEnt(IPluginContext *pContext, const cell_t *params) return 0; } - /* :TODO: check serial no.? */ - return hndl.GetEntryIndex(); } +int CheckBaseHandle(CBaseHandle &hndl) +{ + if (!hndl.IsValid()) + { + return -1; + } + + int index = hndl.GetEntryIndex(); + + edict_t *pStoredEdict; + CBaseEntity *pStoredEntity; + + pStoredEdict = GetEntity(index, &pStoredEntity); + + if (pStoredEdict == NULL || pStoredEntity == NULL) + { + return -1; + } + + IServerEntity *pSE = pStoredEdict->GetIServerEntity(); + + if (pSE == NULL) + { + return -1; + } + + if (pSE->GetRefEHandle() != hndl) + { + return -1; + } + + return index; +} + +static cell_t GetEntDataEnt2(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (pEdict == NULL || pEntity == NULL) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset <= 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset); + + return CheckBaseHandle(hndl); +} + +/* THIS GUY IS DEPRECATED. */ static cell_t SetEntDataEnt(IPluginContext *pContext, const cell_t *params) { CBaseEntity *pEntity; @@ -502,7 +567,7 @@ static cell_t SetEntDataEnt(IPluginContext *pContext, const cell_t *params) } int offset = params[2]; - if (offset < 0 || offset > 32768) + if (offset <= 0 || offset > 32768) { return pContext->ThrowNativeError("Offset %d is invalid", offset); } @@ -530,6 +595,47 @@ static cell_t SetEntDataEnt(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t SetEntDataEnt2(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + edict_t *pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + int offset = params[2]; + if (offset <= 0 || offset > 32768) + { + return pContext->ThrowNativeError("Offset %d is invalid", offset); + } + + CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset); + + if (params[3] == -1) + { + hndl.Set(NULL); + } + else + { + edict_t *pEdict = GetEdict(params[3]); + if (!pEdict) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[3]); + } + IServerEntity *pEntOther = pEdict->GetIServerEntity(); + hndl.Set(pEntOther); + } + + if (params[4]) + { + g_HL2.SetEdictStateChanged(pEdict, offset); + } + + return 1; +} + static cell_t ChangeEdictState(IPluginContext *pContext, const cell_t *params) { edict_t *pEdict = GetEdict(params[1]); @@ -560,6 +666,59 @@ static cell_t FindSendPropOffs(IPluginContext *pContext, const cell_t *params) return pSend->GetOffset(); } +static cell_t FindSendPropInfo(IPluginContext *pContext, const cell_t *params) +{ + char *cls, *prop; + sm_sendprop_info_t info; + cell_t *pType, *pBits, *pLocal; + + pContext->LocalToString(params[1], &cls); + pContext->LocalToString(params[2], &prop); + + if (!g_HL2.FindSendPropInfo(cls, prop, &info)) + { + return -1; + } + + pContext->LocalToPhysAddr(params[3], &pType); + pContext->LocalToPhysAddr(params[4], &pBits); + pContext->LocalToPhysAddr(params[5], &pLocal); + + switch (info.prop->GetType()) + { + case DPT_Int: + { + *pType = PropField_Integer; + break; + } + case DPT_Float: + { + *pType = PropField_Float; + break; + } + case DPT_String: + { + *pType = PropField_String; + break; + } + case DPT_Vector: + { + *pType = PropField_Vector; + break; + } + default: + { + *pType = PropField_Unsupported; + break; + } + } + + *pBits = info.prop->m_nBits; + *pLocal = info.prop->GetOffset(); + + return info.actual_offset; +} + static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params) { CBaseEntity *pEntity; @@ -584,6 +743,79 @@ static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params) return -1; } + if (params[0] == 4) + { + cell_t *pType, *pSize; + + pContext->LocalToPhysAddr(params[3], &pType); + pContext->LocalToPhysAddr(params[4], &pSize); + + switch (td->fieldType) + { + case FIELD_TICK: + case FIELD_MODELINDEX: + case FIELD_MATERIALINDEX: + case FIELD_INTEGER: + case FIELD_COLOR32: + { + *pType = PropField_Integer; + *pSize = 32; + break; + } + case FIELD_VECTOR: + case FIELD_POSITION_VECTOR: + { + *pType = PropField_Vector; + *pSize = 12; + break; + } + case FIELD_SHORT: + { + *pType = PropField_Integer; + *pSize = 16; + break; + } + case FIELD_BOOLEAN: + { + *pType = PropField_Integer; + *pSize = 1; + break; + } + case FIELD_CHARACTER: + { + if (td->fieldSize == 1) + { + *pType = PropField_Integer; + *pSize = 8; + } + else + { + *pType = PropField_String; + *pSize = 8 * td->fieldSize; + } + break; + } + case FIELD_FLOAT: + case FIELD_TIME: + { + *pType = PropField_Float; + *pSize = 32; + break; + } + case FIELD_EHANDLE: + { + *pType = PropField_Entity; + *pSize = 32; + break; + } + default: + { + *pType = PropField_Unsupported; + *pSize = 0; + } + } + } + return td->fieldOffset[TD_OFFSET_NORMAL]; } @@ -640,59 +872,764 @@ static cell_t SetEntDataString(IPluginContext *pContext, const cell_t *params) return len; } -static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params) +#define FIND_PROP_DATA(td) \ + datamap_t *pMap; \ + if ((pMap = CBaseEntity_GetDataDescMap(pEntity)) == NULL) \ + { \ + return pContext->ThrowNativeError("Could not retrieve datamap"); \ + } \ + if ((td = g_HL2.FindInDataMap(pMap, prop)) == NULL) \ + { \ + return pContext->ThrowNativeError("Property \"%s\" not found (entity %d/%s)", \ + prop, \ + params[1], \ + class_name); \ + } + +#define FIND_PROP_SEND(pProp) \ + IServerNetworkable *pNet = pEdict->GetNetworkable(); \ + if (!pNet) \ + { \ + return pContext->ThrowNativeError("Edict %d is not networkable", params[1]); \ + } \ + if (!g_HL2.FindSendPropInfo(pNet->GetServerClass()->GetName(), prop, pProp)) \ + { \ + return pContext->ThrowNativeError("Property \"%s\" not found (entity %d/%s)", \ + prop, \ + params[1], \ + class_name); \ + } + +inline int MatchFieldAsInteger(int field_type) +{ + switch (field_type) + { + case FIELD_TICK: + case FIELD_MODELINDEX: + case FIELD_MATERIALINDEX: + case FIELD_INTEGER: + case FIELD_COLOR32: + return 32; + case FIELD_SHORT: + return 16; + case FIELD_CHARACTER: + return 8; + case FIELD_BOOLEAN: + return 1; + default: + return 0; + } + + return 0; +} + +static cell_t GetEntProp(IPluginContext *pContext, const cell_t *params) { CBaseEntity *pEntity; char *prop; int offset; - edict_t *pEdict = GetEntity(params[1], &pEntity); + const char *class_name; + edict_t *pEdict; + int bit_count; + + pEdict = GetEntity(params[1], &pEntity); if (!pEdict || !pEntity) { return pContext->ThrowNativeError("Entity %d is invalid", params[1]); } + if ((class_name = pEdict->GetClassName()) == NULL) + { + class_name = ""; + } + + pContext->LocalToString(params[3], &prop); + switch (params[2]) { case Prop_Data: { - datamap_t *pMap; typedescription_t *td; - if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL) + + FIND_PROP_DATA(td); + + if ((bit_count = MatchFieldAsInteger(td->fieldType)) == 0) { - return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset"); - } - pContext->LocalToString(params[3], &prop); - if ((td=g_HL2.FindInDataMap(pMap, prop)) == NULL) - { - return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]); - } - if (td->fieldType != FIELD_CHARACTER) - { - return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop); + return pContext->ThrowNativeError("Data field %s is not an integer (%d)", + prop, + td->fieldType); } + offset = td->fieldOffset[TD_OFFSET_NORMAL]; break; } case Prop_Send: { - char *prop; - IServerNetworkable *pNet = pEdict->GetNetworkable(); - if (!pNet) + sm_sendprop_info_t info; + + FIND_PROP_SEND(&info); + + if (info.prop->GetType() != DPT_Int) { - return pContext->ThrowNativeError("The edict is not networkable"); + return pContext->ThrowNativeError("SendProp %s is not an integer ([%d,%d] != %d)", + prop, + info.prop->GetType(), + info.prop->m_nBits, + DPT_Int); } - pContext->LocalToString(params[3], &prop); - SendProp *pSend = g_HL2.FindInSendTable(pNet->GetServerClass()->GetName(), prop); - if (!pSend) + + bit_count = info.prop->m_nBits; + offset = info.actual_offset; + break; + } + default: + { + return pContext->ThrowNativeError("Invalid Property type %d", params[2]); + } + } + + if (bit_count < 1) + { + bit_count = params[4] * 8; + } + + if (bit_count >= 17) + { + return *(int32_t *)((uint8_t *)pEntity + offset); + } + else if (bit_count >= 9) + { + return *(int16_t *)((uint8_t *)pEntity + offset); + } + else if (bit_count >= 2) + { + return *(int8_t *)((uint8_t *)pEntity + offset); + } + else + { + return *(bool *)((uint8_t *)pEntity + offset) ? 1 : 0; + } + + return 0; +} + +static cell_t SetEntProp(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + char *prop; + int offset; + const char *class_name; + edict_t *pEdict; + int bit_count; + + pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + if ((class_name = pEdict->GetClassName()) == NULL) + { + class_name = ""; + } + + pContext->LocalToString(params[3], &prop); + + switch (params[2]) + { + case Prop_Data: + { + typedescription_t *td; + + FIND_PROP_DATA(td); + + if ((bit_count = MatchFieldAsInteger(td->fieldType)) == 0) { - return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]); + return pContext->ThrowNativeError("Data field %s is not an integer (%d)", + prop, + td->fieldType); } - if (pSend->GetType() != DPT_String) + + offset = td->fieldOffset[TD_OFFSET_NORMAL]; + break; + } + case Prop_Send: + { + sm_sendprop_info_t info; + + FIND_PROP_SEND(&info); + + if (info.prop->GetType() != DPT_Int) { - return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop); + return pContext->ThrowNativeError("SendProp %s is not an integer ([%d,%d] != %d)", + prop, + info.prop->GetType(), + info.prop->m_nBits, + DPT_Int); } - offset = pSend->GetOffset(); + + bit_count = info.prop->m_nBits; + offset = info.actual_offset; + break; + } + default: + { + return pContext->ThrowNativeError("Invalid Property type %d", params[2]); + } + } + + if (bit_count < 1) + { + bit_count = params[5] * 8; + } + + if (bit_count >= 17) + { + *(int32_t *)((uint8_t *)pEntity + offset) = params[4]; + } + else if (bit_count >= 9) + { + *(int16_t *)((uint8_t *)pEntity + offset) = (int16_t)params[4]; + } + else if (bit_count >= 2) + { + *(int8_t *)((uint8_t *)pEntity + offset) = (int8_t)params[4]; + } + else + { + *(bool *)((uint8_t *)pEntity + offset) = params[4] ? true : false; + } + + return 0; +} + +static cell_t GetEntPropFloat(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + char *prop; + int offset; + const char *class_name; + edict_t *pEdict; + + pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + if ((class_name = pEdict->GetClassName()) == NULL) + { + class_name = ""; + } + + pContext->LocalToString(params[3], &prop); + + switch (params[2]) + { + case Prop_Data: + { + typedescription_t *td; + + FIND_PROP_DATA(td); + + if (td->fieldType != FIELD_FLOAT + && td->fieldType != FIELD_TIME) + { + return pContext->ThrowNativeError("Data field %s is not a float (%d != [%d,%d])", + prop, + td->fieldType, + FIELD_FLOAT, + FIELD_TIME); + } + + offset = td->fieldOffset[TD_OFFSET_NORMAL]; + break; + } + case Prop_Send: + { + sm_sendprop_info_t info; + + FIND_PROP_SEND(&info); + + if (info.prop->GetType() != DPT_Float) + { + return pContext->ThrowNativeError("SendProp %s is not a float (%d != %d)", + prop, + info.prop->GetType(), + DPT_Float); + } + + offset = info.actual_offset; + break; + } + default: + { + return pContext->ThrowNativeError("Invalid Property type %d", params[2]); + } + } + + float val = *(float *)((uint8_t *)pEntity + offset); + + return sp_ftoc(val); +} + +static cell_t SetEntPropFloat(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + char *prop; + int offset; + const char *class_name; + edict_t *pEdict; + + pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + if ((class_name = pEdict->GetClassName()) == NULL) + { + class_name = ""; + } + + pContext->LocalToString(params[3], &prop); + + switch (params[2]) + { + case Prop_Data: + { + typedescription_t *td; + + FIND_PROP_DATA(td); + + if (td->fieldType != FIELD_FLOAT + && td->fieldType != FIELD_TIME) + { + return pContext->ThrowNativeError("Data field %s is not a float (%d != [%d,%d])", + prop, + td->fieldType, + FIELD_FLOAT, + FIELD_TIME); + } + + offset = td->fieldOffset[TD_OFFSET_NORMAL]; + break; + } + case Prop_Send: + { + sm_sendprop_info_t info; + + FIND_PROP_SEND(&info); + + if (info.prop->GetType() != DPT_Float) + { + return pContext->ThrowNativeError("SendProp %s is not a float (%d != %d)", + prop, + info.prop->GetType(), + DPT_Float); + } + + offset = info.actual_offset; + break; + } + default: + { + return pContext->ThrowNativeError("Invalid Property type %d", params[2]); + } + } + + *(float *)((uint8_t *)pEntity + offset) = sp_ctof(params[4]); + + if (params[2] == Prop_Send) + { + g_HL2.SetEdictStateChanged(pEdict, offset); + } + + return 1; +} + +static cell_t GetEntPropEnt(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + char *prop; + int offset; + const char *class_name; + edict_t *pEdict; + + pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + if ((class_name = pEdict->GetClassName()) == NULL) + { + class_name = ""; + } + + pContext->LocalToString(params[3], &prop); + + switch (params[2]) + { + case Prop_Data: + { + typedescription_t *td; + + FIND_PROP_DATA(td); + + if (td->fieldType != FIELD_EHANDLE) + { + return pContext->ThrowNativeError("Data field %s is not an entity (%d != %d)", + prop, + td->fieldType, + FIELD_EHANDLE); + } + + offset = td->fieldOffset[TD_OFFSET_NORMAL]; + break; + } + case Prop_Send: + { + sm_sendprop_info_t info; + + FIND_PROP_SEND(&info); + + if (info.prop->GetType() != DPT_Int) + { + return pContext->ThrowNativeError("SendProp %s is not an integer (%d != %d)", + prop, + info.prop->GetType(), + DPT_Int); + } + + offset = info.actual_offset; + break; + } + default: + { + return pContext->ThrowNativeError("Invalid Property type %d", params[2]); + } + } + + CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset); + + return CheckBaseHandle(hndl); +} + +static cell_t SetEntPropEnt(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + char *prop; + int offset; + const char *class_name; + edict_t *pEdict; + + pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + if ((class_name = pEdict->GetClassName()) == NULL) + { + class_name = ""; + } + + pContext->LocalToString(params[3], &prop); + + switch (params[2]) + { + case Prop_Data: + { + typedescription_t *td; + + FIND_PROP_DATA(td); + + if (td->fieldType != FIELD_EHANDLE) + { + return pContext->ThrowNativeError("Data field %s is not an entity (%d != %d)", + prop, + td->fieldType, + FIELD_EHANDLE); + } + + offset = td->fieldOffset[TD_OFFSET_NORMAL]; + break; + } + case Prop_Send: + { + sm_sendprop_info_t info; + + FIND_PROP_SEND(&info); + + if (info.prop->GetType() != DPT_Int) + { + return pContext->ThrowNativeError("SendProp %s is not an integer (%d != %d)", + prop, + info.prop->GetType(), + DPT_Int); + } + + offset = info.actual_offset; + break; + } + default: + { + return pContext->ThrowNativeError("Invalid Property type %d", params[2]); + } + } + + CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset); + + if (params[4] == -1) + { + hndl.Set(NULL); + } + else + { + edict_t *pOther; + CBaseEntity *pOtherEnt; + + if ((pOther = GetEntity(params[4], &pOtherEnt)) == NULL) + { + return pContext->ThrowNativeError("Invalid entity %d", params[4]); + } + + hndl.Set(pOther->GetIServerEntity()); + } + + if (params[2] == Prop_Send) + { + g_HL2.SetEdictStateChanged(pEdict, offset); + } + + return 1; +} + +static cell_t GetEntPropVector(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + char *prop; + int offset; + const char *class_name; + edict_t *pEdict; + + pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + if ((class_name = pEdict->GetClassName()) == NULL) + { + class_name = ""; + } + + pContext->LocalToString(params[3], &prop); + + switch (params[2]) + { + case Prop_Data: + { + typedescription_t *td; + + FIND_PROP_DATA(td); + + if (td->fieldType != FIELD_VECTOR + && td->fieldType != FIELD_POSITION_VECTOR) + { + return pContext->ThrowNativeError("Data field %s is not a vector (%d != [%d,%d])", + prop, + td->fieldType, + FIELD_VECTOR, + FIELD_POSITION_VECTOR); + } + + offset = td->fieldOffset[TD_OFFSET_NORMAL]; + break; + } + case Prop_Send: + { + sm_sendprop_info_t info; + + FIND_PROP_SEND(&info); + + if (info.prop->GetType() != DPT_Vector) + { + return pContext->ThrowNativeError("SendProp %s is not a vector (%d != %d)", + prop, + info.prop->GetType(), + DPT_Vector); + } + + offset = info.actual_offset; + break; + } + default: + { + return pContext->ThrowNativeError("Invalid Property type %d", params[2]); + } + } + + Vector *v = (Vector *)((uint8_t *)pEntity + offset); + + cell_t *vec; + pContext->LocalToPhysAddr(params[4], &vec); + + vec[0] = sp_ftoc(v->x); + vec[1] = sp_ftoc(v->y); + vec[2] = sp_ftoc(v->z); + + return 1; +} + +static cell_t SetEntPropVector(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + char *prop; + int offset; + const char *class_name; + edict_t *pEdict; + + pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + if ((class_name = pEdict->GetClassName()) == NULL) + { + class_name = ""; + } + + pContext->LocalToString(params[3], &prop); + + switch (params[2]) + { + case Prop_Data: + { + typedescription_t *td; + + FIND_PROP_DATA(td); + + if (td->fieldType != FIELD_VECTOR + && td->fieldType != FIELD_POSITION_VECTOR) + { + return pContext->ThrowNativeError("Data field %s is not a vector (%d != [%d,%d])", + prop, + td->fieldType, + FIELD_VECTOR, + FIELD_POSITION_VECTOR); + } + + offset = td->fieldOffset[TD_OFFSET_NORMAL]; + break; + } + case Prop_Send: + { + sm_sendprop_info_t info; + + FIND_PROP_SEND(&info); + + if (info.prop->GetType() != DPT_Vector) + { + return pContext->ThrowNativeError("SendProp %s is not a vector (%d != %d)", + prop, + info.prop->GetType(), + DPT_Vector); + } + + offset = info.actual_offset; + break; + } + default: + { + return pContext->ThrowNativeError("Invalid Property type %d", params[2]); + } + } + + Vector *v = (Vector *)((uint8_t *)pEntity + offset); + + cell_t *vec; + pContext->LocalToPhysAddr(params[4], &vec); + + v->x = sp_ctof(vec[0]); + v->y = sp_ctof(vec[1]); + v->z = sp_ctof(vec[2]); + + if (params[2] == Prop_Send) + { + g_HL2.SetEdictStateChanged(pEdict, offset); + } + + return 1; +} + +static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + char *prop; + int offset; + const char *class_name; + edict_t *pEdict; + + pEdict = GetEntity(params[1], &pEntity); + + if (!pEdict || !pEntity) + { + return pContext->ThrowNativeError("Entity %d is invalid", params[1]); + } + + if ((class_name = pEdict->GetClassName()) == NULL) + { + class_name = ""; + } + + pContext->LocalToString(params[3], &prop); + + switch (params[2]) + { + case Prop_Data: + { + typedescription_t *td; + + FIND_PROP_DATA(td); + + if (td->fieldType != FIELD_CHARACTER) + { + return pContext->ThrowNativeError("Data field %s is not a string (%d != %d)", + prop, + td->fieldType, + FIELD_CHARACTER); + } + + offset = td->fieldOffset[TD_OFFSET_NORMAL]; + break; + } + case Prop_Send: + { + sm_sendprop_info_t info; + + FIND_PROP_SEND(&info); + + if (info.prop->GetType() != DPT_String) + { + return pContext->ThrowNativeError("SendProp %s is not a string (%d != %d)", + prop, + info.prop->GetType(), + DPT_String); + } + + offset = info.actual_offset; + break; break; } default: @@ -714,7 +1651,6 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params) char *prop; int offset; int maxlen; - bool is_sendprop = false; edict_t *pEdict = GetEntity(params[1], &pEntity); if (!pEdict || !pEntity) @@ -765,7 +1701,6 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params) } offset = pSend->GetOffset(); maxlen = DT_MAX_STRING_BUFFERSIZE; - is_sendprop = true; break; } default: @@ -780,7 +1715,7 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params) pContext->LocalToString(params[4], &src); size_t len = strncopy(dest, src, maxlen); - if (is_sendprop) + if (params[2] == Prop_Send) { g_HL2.SetEdictStateChanged(pEdict, offset); } @@ -792,13 +1727,22 @@ REGISTER_NATIVES(entityNatives) { {"ChangeEdictState", ChangeEdictState}, {"CreateEdict", CreateEdict}, + {"FindDataMapOffs", FindDataMapOffs}, + {"FindSendPropInfo", FindSendPropInfo}, {"FindSendPropOffs", FindSendPropOffs}, {"GetEdictClassname", GetEdictClassname}, {"GetEdictFlags", GetEdictFlags}, {"GetEntData", GetEntData}, - {"GetEntDataEnt", GetEntDataEnt}, + {"GetEntDataEnt", GetEntDataEnt}, /** DEPRECATED */ + {"GetEntDataEnt2", GetEntDataEnt2}, {"GetEntDataFloat", GetEntDataFloat}, + {"GetEntDataString", GetEntDataString}, {"GetEntDataVector", GetEntDataVector}, + {"GetEntProp", GetEntProp}, + {"GetEntPropEnt", GetEntPropEnt}, + {"GetEntPropFloat", GetEntPropFloat}, + {"GetEntPropString", GetEntPropString}, + {"GetEntPropVector", GetEntPropVector}, {"GetEntityCount", GetEntityCount}, {"GetEntityNetClass", GetEntityNetClass}, {"GetMaxEntities", GetMaxEntities}, @@ -808,13 +1752,15 @@ REGISTER_NATIVES(entityNatives) {"RemoveEdict", RemoveEdict}, {"SetEdictFlags", SetEdictFlags}, {"SetEntData", SetEntData}, - {"SetEntDataEnt", SetEntDataEnt}, + {"SetEntDataEnt", SetEntDataEnt}, /** DEPRECATED */ + {"SetEntDataEnt2", SetEntDataEnt2}, {"SetEntDataFloat", SetEntDataFloat}, {"SetEntDataVector", SetEntDataVector}, - {"FindDataMapOffs", FindDataMapOffs}, - {"GetEntDataString", GetEntDataString}, {"SetEntDataString", SetEntDataString}, - {"GetEntPropString", GetEntPropString}, + {"SetEntProp", SetEntProp}, + {"SetEntPropEnt", SetEntPropEnt}, + {"SetEntPropFloat", SetEntPropFloat}, {"SetEntPropString", SetEntPropString}, + {"SetEntPropVector", SetEntPropVector}, {NULL, NULL} }; diff --git a/plugins/include/entity.inc b/plugins/include/entity.inc index d3f3a33a..79d90fc4 100644 --- a/plugins/include/entity.inc +++ b/plugins/include/entity.inc @@ -41,7 +41,7 @@ enum PropType { Prop_Send = 0, /**< This property is networked. */ - Prop_Data = 1, /**< This property is for saved game files. */ + Prop_Data = 1, /**< This property is for save game data fields. */ }; /** @@ -59,6 +59,16 @@ enum PropType #define FL_EDICT_DIRTY_PVS_INFORMATION (1<<7) #define FL_FULL_EDICT_CHANGED (1<<8) +enum PropFieldType +{ + PropField_Unsupported, /**< The type is unsupported. */ + PropField_Integer, /**< Valid for SendProp and Data fields */ + PropField_Float, /**< Valid for SendProp and Data fields */ + PropField_Entity, /**< Valid for Data fields only (SendProp shows as int) */ + PropField_Vector, /**< Valid for SendProp and Data fields */ + PropField_String, /**< Valid for SendProp and Data fields */ +}; + /** * @endsection */ @@ -244,8 +254,14 @@ native Float:GetEntDataFloat(entity, offset); native SetEntDataFloat(entity, offset, Float:value, bool:changeState=false); /** - * Peeks into an entity's object data and retrieves the entity handle - * info at the given offset. + * This function is deprecated. Use GetEntDataEnt2 instead, for + * reasons explained in the notes. + * + * Note: This function returns 0 on failure, which may be misleading, + * as the number 0 is also used for the world entity index. + * + * Note: This function makes no attempt to validate the returned + * entity, and in fact, it could be garbage or completely unexpected. * * @param entity Edict index. * @param offset Offset to use. @@ -255,8 +271,12 @@ native SetEntDataFloat(entity, offset, Float:value, bool:changeState=false); native GetEntDataEnt(entity, offset); /** - * Peeks into an entity's object data and sets the entity handle info - * at the given offset. + * This function is deprecated. Use GetEntDataEnt2 instead, for + * reasons explained in the notes. + * + * Note: This function uses 0 as an indicator to unset data, but + * 0 is also the world entity index. Thus, the a property cannot + * be set to the world entity using this native. * * @param entity Edict index. * @param offset Offset to use. @@ -267,6 +287,39 @@ native GetEntDataEnt(entity, offset); */ native SetEntDataEnt(entity, offset, other, bool:changeState=false); +/** + * Peeks into an entity's object data and retrieves the entity index + * at the given offset. + * + * Note: This will only work on offsets that are stored as "entity + * handles" (which usually looks like m_h* in properties). These + * are not SourceMod Handles, but internal Source structures. + * + * @param entity Edict index. + * @param offset Offset to use. + * @return Entity index at the given location. If there is no entity, + * or the stored entity is invalid, then -1 is returned. + * @error Invalid input entity, or offset out of reasonable bounds. + */ +native GetEntDataEnt2(entity, offset); + +/** + * Peeks into an entity's object data and sets the entity index at the + * given offset. + * + * Note: This will only work on offsets that are stored as "entity + * handles" (which usually looks like m_h* in properties). These + * are not SourceMod Handles, but internal Source structures. + * + * @param entity Edict index. + * @param offset Offset to use. + * @param other Entity index to set, or -1 to clear. + * @param changeState If true, change will be sent over the network. + * @noreturn + * @error Invalid input entity, or offset out of reasonable bounds. + */ +native SetEntDataEnt2(entity, offset, other, bool:changeState=false); + /** * Peeks into an entity's object data and retrieves the vector at the * given offset. @@ -331,30 +384,74 @@ native SetEntDataString(entity, offset, const String:buffer[], maxlen, bool:chan * Given a ServerClass name, finds a networkable send property offset. * This information is cached for future calls. * + * Note, this function may return offsets that do not work! + * If a property is nested beneath a parent object, the resulting offset + * will be invalid for direct use with data functions. Therefore, you + * should use FindSendPropInfo() instead. An example of such a property is + * CTFPlayer::DT_LocalPlayer.m_nDisguiseClass on Team Fortress. + * * @param cls Classname. * @param prop Property name. * @return An offset, or -1 on failure. */ native FindSendPropOffs(const String:cls[], const String:prop[]); +/** + * Given a ServerClass name, finds a networkable send property offset. + * This information is cached for future calls. + * + * Note: This function will correctly compute nested offsets, unlike + * FindSendPropOffs(). YOU SHOULD NOT use this function to self-compute + * nested offsets. For example, it is okay to add indexes for arrays, + * but not to add DT_LocalPlayer to m_nDisguiseClass. + * + * @param cls Classname. + * @param prop Property name. + * @param type Optional parameter to store the type. + * @param num_bits Optional parameter to store the number of bits the field + * uses, if applicable (otherwise 0 is stored). The number + * of bits varies for integers and floats, and is always 0 + * for strings. + * @param local_offset Optional parameter to store the local offset, as + * FindSendPropOffs() would return. + * @return On success, returns an absolutely computed offset. + * If no offset is available, 0 is returned. + * If the property is not found, -1 is returned. + */ +native FindSendPropInfo(const String:cls[], + const String:prop[], + &PropFieldType:type=PropFieldType:0, + &num_bits=0, + &local_offset=0); + /** * Given an entity, finds a datamap property offset. * This information is cached for future calls. * * @param entity Entity index. * @param prop Property name. + * @param type Optional parameter to store the type. + * @param num_bits Optional parameter to store the number of bits the field + * uses. The bit count will either be 1 (for boolean) or + * divisible by 8 (including 0 if unknown). * @return An offset, or -1 on failure. */ -native FindDataMapOffs(entity, const String:prop[]); +native FindDataMapOffs(entity, + const String:prop[], + &PropFieldType:type=PropFieldType:0, + &num_bits=0); /** * Wrapper function for finding a send property for a particular entity. * * @param ent Entity index. * @param prop Property name. + * @param actual Defaults to false for backwards compatibility. + * If true, the newer FindSendPropInfo() function + * is used instead. * @return An offset, or -1 on failure. */ -stock GetEntSendPropOffs(ent, const String:prop[]) +stock GetEntSendPropOffs(ent, const String:prop[], bool:actual=false) { decl String:cls[64]; @@ -363,314 +460,149 @@ stock GetEntSendPropOffs(ent, const String:prop[]) return -1; } - return FindSendPropOffs(cls, prop); + if (actual) + { + return FindSendPropInfo(cls, prop); + } + else + { + return FindSendPropOffs(cls, prop); + } } /** - * Gets a network property as an integer; wrapper around GetEntData(). + * Retrieves an integer value from an entity's property. + * + * This function is considered safer and more robust over GetEntData, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. * - * @param entity Edict index. + * @param entity Entity/edict index. * @param type Property type. - * @param prop Property to use. - * @param size Number of bytes to read (valid values are 1, 2, or 4). + * @param prop Property name. + * @param size Number of bytes to write (valid values are 1, 2, or 4). + * This value is auto-detected, and the size parameter is + * only used as a fallback in case detection fails. * @return Value at the given property offset. * @error Invalid entity or property not found. */ -stock GetEntProp(entity, PropType:type, const String:prop[], size=4) -{ - new offs; - - switch (type) - { - case Prop_Send: - { - offs = GetEntSendPropOffs(entity, prop); - } - case Prop_Data: - { - offs = FindDataMapOffs(entity, prop); - } - default: - { - ThrowError("Invalid Property type %d", type); - } - } - - if (offs == -1) - { - ThrowError("Property \"%s\" not found for entity %d", prop, entity); - } - - return GetEntData(entity, offs, size); -} +native GetEntProp(entity, PropType:type, const String:prop[], size=4); /** - * Sets a network property as an integer; wrapper around GetEntData(). + * Sets an integer value in an entity's property. * - * @param entity Edict index. + * This function is considered safer and more robust over SetEntData, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. + * + * @param entity Entity/edict index. * @param type Property type. - * @param prop Property to use. + * @param prop Property name. * @param size Number of bytes to write (valid values are 1, 2, or 4). + * This value is auto-detected, and the size parameter is + * only used as a fallback in case detection fails. * @error Invalid entity or offset out of reasonable bounds. * @noreturn */ -stock SetEntProp(entity, PropType:type, const String:prop[], any:value, size=4) -{ - new offs; - - switch (type) - { - case Prop_Send: - { - offs = GetEntSendPropOffs(entity, prop); - } - case Prop_Data: - { - offs = FindDataMapOffs(entity, prop); - } - default: - { - ThrowError("Invalid Property type %d", type); - } - } - - if (offs == -1) - { - ThrowError("Property \"%s\" not found for entity %d", prop, entity); - } - - return SetEntData(entity, offs, value, size, true); -} +native SetEntProp(entity, PropType:type, const String:prop[], any:value, size=4); /** - * Gets a network property as a float; wrapper around GetEntDataFloat(). + * Retrieves a float value from an entity's property. + * + * This function is considered safer and more robust over GetEntDataFloat, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. * - * @param entity Edict index. + * @param entity Entity/edict index. * @param type Property type. - * @param prop Property to use. + * @param prop Property name. * @return Value at the given property offset. * @error Invalid entity or offset out of reasonable bounds. */ -stock Float:GetEntPropFloat(entity, PropType:type, const String:prop[]) -{ - new offs; - - switch (type) - { - case Prop_Send: - { - offs = GetEntSendPropOffs(entity, prop); - } - case Prop_Data: - { - offs = FindDataMapOffs(entity, prop); - } - default: - { - ThrowError("Invalid Property type %d", type); - } - } - - if (offs == -1) - { - ThrowError("Property \"%s\" not found for entity %d", prop, entity); - } - - return GetEntDataFloat(entity, offs); -} +native Float:GetEntPropFloat(entity, PropType:type, const String:prop[]); /** - * Sets a network property as a float; wrapper around SetEntDataFloat(). + * Sets a float value in an entity's property. + * + * This function is considered safer and more robust over SetEntDataFloat, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. * - * @param entity Edict index. + * @param entity Entity/edict index. * @param type Property type. - * @param prop Property to use. + * @param prop Property name. * @param value Value to set. * @noreturn * @error Invalid entity or offset out of reasonable bounds. */ -stock SetEntPropFloat(entity, PropType:type, const String:prop[], Float:value) -{ - new offs; - - switch (type) - { - case Prop_Send: - { - offs = GetEntSendPropOffs(entity, prop); - } - case Prop_Data: - { - offs = FindDataMapOffs(entity, prop); - } - default: - { - ThrowError("Invalid Property type %d", type); - } - } - - if (offs == -1) - { - ThrowError("Property \"%s\" not found for entity %d", prop, entity); - } - - return SetEntDataFloat(entity, offs, value, true); -} +native SetEntPropFloat(entity, PropType:type, const String:prop[], Float:value); /** - * Gets a network property as a handle entity; wrapper around GetEntDataEnt(). + * Retrieves an entity index from an entity's property. + * + * This function is considered safer and more robust over GetEntDataEnt*, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. * - * @param entity Edict index. + * @param entity Entity/edict index. * @param type Property type. - * @param prop Property to use. - * @return Entity index at the given property, or 0 if none. + * @param prop Property name. + * @return Entity index at the given property. + * If there is no entity, or the entity is not valid, + * then -1 is returned. * @error Invalid entity or offset out of reasonable bounds. */ -stock GetEntPropEnt(entity, PropType:type, const String:prop[]) -{ - new offs; - - switch (type) - { - case Prop_Send: - { - offs = GetEntSendPropOffs(entity, prop); - } - case Prop_Data: - { - offs = FindDataMapOffs(entity, prop); - } - default: - { - ThrowError("Invalid Property type %d", type); - } - } - - if (offs == -1) - { - ThrowError("Property \"%s\" not found for entity %d", prop, entity); - } - - return GetEntDataEnt(entity, offs); -} +native GetEntPropEnt(entity, PropType:type, const String:prop[]); /** - * Sets a network property as a handle entity; wrapper around SetEntDataEnt(). + * Sets an entity index in an entity's property. + * + * This function is considered safer and more robust over SetEntDataEnt*, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. * - * @param entity Edict index. + * @param entity Entity/edict index. * @param type Property type. - * @param prop Property to use. - * @param other Entity index to set, or 0 to unset. + * @param prop Property name. + * @param other Entity index to set, or -1 to unset. * @noreturn * @error Invalid entity or offset out of reasonable bounds. */ -stock SetEntPropEnt(entity, PropType:type, const String:prop[], other) -{ - new offs; - - switch (type) - { - case Prop_Send: - { - offs = GetEntSendPropOffs(entity, prop); - } - case Prop_Data: - { - offs = FindDataMapOffs(entity, prop); - } - default: - { - ThrowError("Invalid Property type %d", type); - } - } - - if (offs == -1) - { - ThrowError("Property \"%s\" not found for entity %d", prop, entity); - } - - return SetEntDataEnt(entity, offs, other, true); -} +native SetEntPropEnt(entity, PropType:type, const String:prop[], other); /** - * Gets a network property as a vector; wrapper around GetEntDataVector(). - * @note Both a Vector and a QAngle are three floats. This is a - * convenience function and will work with both types. + * Retrieves a vector of floats from an entity, given a named network property. + * + * This function is considered safer and more robust over GetEntDataVector, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. * - * @param entity Edict index. + * @param entity Entity/edict index. * @param type Property type. - * @param prop Property to use. + * @param prop Property name. * @param vec Vector buffer to store data in. * @noreturn - * @error Invalid entity or offset out of reasonable bounds. + * @error Invalid entity, property not found, or property not + * actually a vector data type. */ -stock GetEntPropVector(entity, PropType:type, const String:prop[], Float:vec[3]) -{ - new offs; - - switch (type) - { - case Prop_Send: - { - offs = GetEntSendPropOffs(entity, prop); - } - case Prop_Data: - { - offs = FindDataMapOffs(entity, prop); - } - default: - { - ThrowError("Invalid Property type %d", type); - } - } - - if (offs == -1) - { - ThrowError("Property \"%s\" not found for entity %d", prop, entity); - } - - return GetEntDataVector(entity, offs, vec); -} +native GetEntPropVector(entity, PropType:type, const String:prop[], Float:vec[3]); /** - * Sets a network property as a vector; wrapper around SetEntDataVector(). - * @note Both a Vector and a QAngle are three floats. This is a - * convenience function and will work with both types. + * Sets a vector of floats in an entity, given a named network property. + * + * This function is considered safer and more robust over SetEntDataVector, + * because it performs strict offset checking and typing rules. There is a + * very minor performance hit from this. * - * @param entity Edict index. + * @param entity Entity/edict index. * @param type Property type. - * @param prop Property to use. + * @param prop Property name. * @param vec Vector to set. * @noreturn - * @error Invalid entity or offset out of reasonable bounds. + * @error Invalid entity, property not found, or property not + * actually a vector data type. */ -stock SetEntPropVector(entity, PropType:type, const String:prop[], const Float:vec[3]) -{ - new offs; - - switch (type) - { - case Prop_Send: - { - offs = GetEntSendPropOffs(entity, prop); - } - case Prop_Data: - { - offs = FindDataMapOffs(entity, prop); - } - default: - { - ThrowError("Invalid Property type %d", type); - } - } - - if (offs == -1) - { - ThrowError("Property \"%s\" not found for entity %d", prop, entity); - } - - return SetEntDataVector(entity, offs, vec, true); -} +native SetEntPropVector(entity, PropType:type, const String:prop[], const Float:vec[3]); /** * Gets a network property as a string. @@ -716,7 +648,6 @@ stock GetEntDataArray(entity, offset, array[], arraySize, dataSize=4) } } - /** * Copies an array of cells to an entity at a given offset. * diff --git a/public/IGameHelpers.h b/public/IGameHelpers.h index 25d1d5c6..1467ed6c 100644 --- a/public/IGameHelpers.h +++ b/public/IGameHelpers.h @@ -48,6 +48,15 @@ namespace SourceMod { + /** + * @brief Maps the heirarchy of a SendProp. + */ + struct sm_sendprop_info_t + { + SendProp *prop; /**< Property instance. */ + unsigned int actual_offset; /**< Actual computed offset. */ + }; + class IGameHelpers : public SMInterface { public: @@ -61,11 +70,11 @@ namespace SourceMod } public: /** - * @brief Finds a send property in a named send table. + * @brief Deprecated; use FindSendPropInfo() instead. * - * @param classname Top-level sendtable name. - * @param offset Property name. - * @return SendProp pointer on success, NULL on failure. + * @param classname Do not use. + * @param offset Do not use. + * @return Do not use. */ virtual SendProp *FindInSendTable(const char *classname, const char *offset) =0; @@ -118,6 +127,21 @@ namespace SourceMod * @return True if LAN server, false otherwise. */ virtual bool IsLANServer() =0; + + /** + * @brief Finds a send property in a named ServerClass. + * + * This version, unlike FindInSendTable(), correctly deduces the + * offsets of nested tables. + * + * @param classname ServerClass name (such as CBasePlayer). + * @param offset Offset name (such as m_iAmmo). + * @param info Buffer to store sm_sendprop_info_t data. + * @return True on success, false on failure. + */ + virtual bool FindSendPropInfo(const char *classname, + const char *offset, + sm_sendprop_info_t *info) =0; }; }