diff --git a/core/HalfLife2.cpp b/core/HalfLife2.cpp index eea6c8a9..2e58b4b1 100644 --- a/core/HalfLife2.cpp +++ b/core/HalfLife2.cpp @@ -115,7 +115,7 @@ CHalfLife2::~CHalfLife2() { if (h_iter->val.trie) { - sm_trie_destroy(h_iter->val.trie); + delete h_iter->val.trie; h_iter->val.trie = NULL; } } @@ -343,16 +343,11 @@ bool UTIL_FindInSendTable(SendTable *pTable, return false; } -typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name, bool *isNested) +bool UTIL_FindDataMapInfo(datamap_t *pMap, const char *name, sm_datatable_info_t *pDataTable) { - if (isNested) - { - *isNested = false; - } - while (pMap) { - for (int i=0; idataNumFields; i++) + for (int i = 0; i < pMap->dataNumFields; ++i) { if (pMap->dataDesc[i].fieldName == NULL) { @@ -360,32 +355,23 @@ typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name, bool *i } if (strcmp(name, pMap->dataDesc[i].fieldName) == 0) { - return &(pMap->dataDesc[i]); + pDataTable->prop = &(pMap->dataDesc[i]); + pDataTable->actual_offset = GetTypeDescOffs(pDataTable->prop); + return true; } - if (pMap->dataDesc[i].td) + if (pMap->dataDesc[i].td == NULL || !UTIL_FindDataMapInfo(pMap->dataDesc[i].td, name, pDataTable)) { - if (isNested) - { - *isNested = (UTIL_FindInDataMap(pMap->dataDesc[i].td, name, NULL) != NULL); - if (*isNested) - { - return NULL; - } else { - continue; - } - } else { // Use the old behaviour, we dont want to spring this on extensions - even if they're doing bad things. - typedescription_t *_td; - if ((_td=UTIL_FindInDataMap(pMap->dataDesc[i].td, name, NULL)) != NULL) - { - return _td; - } - } + continue; } + + pDataTable->actual_offset += GetTypeDescOffs(&(pMap->dataDesc[i])); + return true; } + pMap = pMap->baseMap; } - return NULL; + return false; } ServerClass *CHalfLife2::FindServerClass(const char *classname) @@ -472,27 +458,44 @@ SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset) typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset) { - return this->FindInDataMap(pMap, offset, NULL); + sm_datatable_info_t dt_info; + + if (!(this->FindDataMapInfo(pMap, offset, &dt_info))) + { + return NULL; + } + + return dt_info.prop; } -typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset, bool *isNested) +bool CHalfLife2::FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatable_info_t *pDataTable) { - typedescription_t *td = NULL; DataMapTrie &val = m_Maps[pMap]; if (!val.trie) { - val.trie = sm_trie_create(); + val.trie = new KTrie; } - if (!sm_trie_retrieve(val.trie, offset, (void **)&td)) + + sm_datatable_info_t * pNewTable = val.trie->retrieve(offset); + + if (!pNewTable) { - if ((td = UTIL_FindInDataMap(pMap, offset, isNested)) != NULL) + if (UTIL_FindDataMapInfo(pMap, offset, pDataTable)) { - sm_trie_insert(val.trie, offset, td); + val.trie->insert(offset, *pDataTable); + } + else + { + pDataTable->prop = NULL; } } + else + { + *pDataTable = *pNewTable; + } - return td; + return (pDataTable->prop != NULL); } void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset) @@ -1221,8 +1224,14 @@ const char *CHalfLife2::GetEntityClassname(CBaseEntity *pEntity) { CBaseEntity *pGetterEnt = ReferenceToEntity(0); datamap_t *pMap = GetDataMap(pGetterEnt); - typedescription_t *pDesc = FindInDataMap(pMap, "m_iClassname"); - offset = GetTypeDescOffs(pDesc); + + sm_datatable_info_t info; + if (!FindDataMapInfo(pMap, "m_iClassname", &info)) + { + return NULL; + } + + offset = info.actual_offset; } return *(const char **)(((unsigned char *)pEntity) + offset); diff --git a/core/HalfLife2.h b/core/HalfLife2.h index 3c9eb4e0..be4fc4ec 100644 --- a/core/HalfLife2.h +++ b/core/HalfLife2.h @@ -66,7 +66,7 @@ struct DataTableInfo struct DataMapTrie { DataMapTrie() : trie(NULL) {} - Trie *trie; + KTrie *trie; }; struct DelayedFakeCliCmd @@ -123,7 +123,7 @@ public: //IGameHelpers datamap_t *GetDataMap(CBaseEntity *pEntity); ServerClass *FindServerClass(const char *classname); typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset); - typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset, bool *isNested); + bool FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatable_info_t *pDataTable); void SetEdictStateChanged(edict_t *pEdict, unsigned short offset); bool TextMsg(int client, int dest, const char *msg); bool HintTextMsg(int client, const char *msg); diff --git a/core/smn_entities.cpp b/core/smn_entities.cpp index d724e423..b2aeb894 100644 --- a/core/smn_entities.cpp +++ b/core/smn_entities.cpp @@ -796,6 +796,82 @@ static cell_t FindSendPropInfo(IPluginContext *pContext, const cell_t *params) return info.actual_offset; } +static void GuessDataPropTypes(typedescription_t *td, cell_t * pSize, cell_t * pType) +{ + 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_MODELNAME: + case FIELD_SOUNDNAME: + case FIELD_STRING: + { + *pSize = sizeof(string_t); + *pType = PropField_String_T; + 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; + } + } +} + static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params) { CBaseEntity *pEntity; @@ -815,14 +891,13 @@ static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params) } pContext->LocalToString(params[2], &offset); - bool isNested = false; - if ((td=g_HL2.FindInDataMap(pMap, offset, &isNested)) == NULL) + sm_datatable_info_t info; + if (!g_HL2.FindDataMapInfo(pMap, offset, &info)) { - if (isNested) - return pContext->ThrowNativeError("Property \"%s\" is not safe to access for entity %d", offset, params[1]); - else - return -1; + return -1; } + + td = info.prop; if (params[0] == 4) { @@ -830,84 +905,62 @@ static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params) 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_MODELNAME: - case FIELD_SOUNDNAME: - case FIELD_STRING: - { - *pSize = sizeof(string_t); - *pType = PropField_String_T; - 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; - } - } + + GuessDataPropTypes(td, pSize, pType); } return GetTypeDescOffs(td); } +static cell_t FindDataMapInfo(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + datamap_t *pMap; + typedescription_t *td; + char *offset; + pEntity = GetEntity(params[1]); + + if (!pEntity) + { + return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]); + } + + if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL) + { + return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset"); + } + + pContext->LocalToString(params[2], &offset); + sm_datatable_info_t info; + if (!g_HL2.FindDataMapInfo(pMap, offset, &info)) + { + return -1; + } + + td = info.prop; + + if (params[0] >= 4) + { + cell_t *pType, *pSize; + + pContext->LocalToPhysAddr(params[3], &pType); + pContext->LocalToPhysAddr(params[4], &pSize); + + GuessDataPropTypes(td, pSize, pType); + + if (params[0] == 5) + { + cell_t *pLocalOffs; + + pContext->LocalToPhysAddr(params[5], &pLocalOffs); + + *pLocalOffs = GetTypeDescOffs(info.prop); + } + } + + return info.actual_offset; +} + static cell_t GetEntDataString(IPluginContext *pContext, const cell_t *params) { CBaseEntity *pEntity = GetEntity(params[1]); @@ -966,23 +1019,16 @@ static cell_t SetEntDataString(IPluginContext *pContext, const cell_t *params) { \ return pContext->ThrowNativeError("Could not retrieve datamap"); \ } \ - bool isNested = false; \ - if ((td = g_HL2.FindInDataMap(pMap, prop, &isNested)) == NULL) \ + sm_datatable_info_t info; \ + if (!g_HL2.FindDataMapInfo(pMap, prop, &info)) \ { \ const char *class_name = g_HL2.GetEntityClassname(pEntity); \ - if (isNested) \ - { \ - return pContext->ThrowNativeError("Property \"%s\" not safe to access (entity %d/%s)", \ - prop, \ - params[1], \ - ((class_name) ? class_name : "")); \ - } else { \ - return pContext->ThrowNativeError("Property \"%s\" not found (entity %d/%s)", \ - prop, \ - params[1], \ - ((class_name) ? class_name : "")); \ - } \ - } + return pContext->ThrowNativeError("Property \"%s\" not found (entity %d/%s)", \ + prop, \ + params[1], \ + ((class_name) ? class_name : "")); \ + } \ + td = info.prop; #define CHECK_SET_PROP_DATA_OFFSET() \ if (element < 0 || element >= td->fieldSize) \ @@ -993,7 +1039,7 @@ static cell_t SetEntDataString(IPluginContext *pContext, const cell_t *params) td->fieldSize); \ } \ \ - offset = GetTypeDescOffs(td) + (element * (td->fieldSizeInBytes / td->fieldSize)); + offset = info.actual_offset + (element * (td->fieldSizeInBytes / td->fieldSize)); #define FIND_PROP_SEND(type, type_name) \ sm_sendprop_info_t info;\ @@ -1808,7 +1854,7 @@ static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params) element); } - offset = GetTypeDescOffs(td); + offset = info.actual_offset; if (bIsStringIndex) { offset += (element * (td->fieldSizeInBytes / td->fieldSize)); @@ -1896,25 +1942,23 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params) case Prop_Data: { datamap_t *pMap; - typedescription_t *td; if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL) { return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset"); } pContext->LocalToString(params[3], &prop); - bool isNested = false; - if ((td=g_HL2.FindInDataMap(pMap, prop, &isNested)) == NULL) + sm_datatable_info_t info; + if (!g_HL2.FindDataMapInfo(pMap, prop, &info)) { - if (isNested) - return pContext->ThrowNativeError("Property \"%s\" is not safe to access for entity %d", prop, params[1]); - else - return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]); + return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]); } + + typedescription_t *td = info.prop; if (td->fieldType != FIELD_CHARACTER) { return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop); } - offset = GetTypeDescOffs(td); + offset = info.actual_offset; maxlen = td->fieldSize; break; } @@ -2138,14 +2182,16 @@ static cell_t GetEntityFlags(IPluginContext *pContext, const cell_t *params) { return pContext->ThrowNativeError("Could not retrieve datamap"); } - if ((td = g_HL2.FindInDataMap(pMap, prop)) == NULL) + + sm_datatable_info_t info; + if (!g_HL2.FindDataMapInfo(pMap, prop, &info)) { return pContext->ThrowNativeError("Property \"%s\" not found (entity %d)", prop, params[1]); } - int offset = GetTypeDescOffs(td); + int offset = info.actual_offset; int32_t actual_flags = *(int32_t *)((uint8_t *)pEntity + offset); int32_t sm_flags = 0; @@ -2176,21 +2222,22 @@ static cell_t SetEntityFlags(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Could not find m_fFlags prop in gamedata"); } - typedescription_t *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) + + sm_datatable_info_t info; + if (!g_HL2.FindDataMapInfo(pMap, prop, &info)) { return pContext->ThrowNativeError("Property \"%s\" not found (entity %d)", prop, params[1]); } - int offset = GetTypeDescOffs(td); + int offset = info.actual_offset; int32_t sm_flags = params[2]; int32_t actual_flags = 0; @@ -2262,5 +2309,6 @@ REGISTER_NATIVES(entityNatives) {"SetEntPropString", SetEntPropString}, {"SetEntPropVector", SetEntPropVector}, {"GetEntityAddress", GetEntityAddress}, + {"FindDataMapInfo", FindDataMapInfo}, {NULL, NULL} }; diff --git a/extensions/sdktools/output.cpp b/extensions/sdktools/output.cpp index 969ccf15..08075610 100644 --- a/extensions/sdktools/output.cpp +++ b/extensions/sdktools/output.cpp @@ -356,8 +356,13 @@ const char *EntityOutputManager::GetEntityClassname(CBaseEntity *pEntity) if (offset == -1) { datamap_t *pMap = gamehelpers->GetDataMap(pEntity); - typedescription_t *pDesc = gamehelpers->FindInDataMap(pMap, "m_iClassname"); - offset = GetTypeDescOffs(pDesc); + sm_datatable_info_t info; + if (!gamehelpers->FindDataMapInfo(pMap, "m_iClassname", &info)) + { + return NULL; + } + + offset = info.actual_offset; } return *(const char **)(((unsigned char *)pEntity) + offset); diff --git a/extensions/sdktools/vhelpers.cpp b/extensions/sdktools/vhelpers.cpp index aa6ae60e..383deebf 100644 --- a/extensions/sdktools/vhelpers.cpp +++ b/extensions/sdktools/vhelpers.cpp @@ -585,14 +585,17 @@ CON_COMMAND(sm_dump_classes, "Dumps the class list as a text file") fprintf(fp, "// Dump of all classes for \"%s\" as at %s\n//\n\n", g_pSM->GetGameFolderName(), buffer); + sm_datatable_info_t info; for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) ) { IServerNetworkable *entity = dict->Create(dict->m_Factories.GetElementName(i)); ServerClass *sclass = entity->GetServerClass(); fprintf(fp,"%s - %s\n",sclass->GetName(), dict->m_Factories.GetElementName(i)); - - typedescription_t *datamap = gamehelpers->FindInDataMap(gamehelpers->GetDataMap(entity->GetBaseEntity()), "m_iEFlags"); - int *eflags = (int *)((char *)entity->GetBaseEntity() + GetTypeDescOffs(datamap)); + + if (!gamehelpers->FindDataMapInfo(gamehelpers->GetDataMap(entity->GetBaseEntity()), "m_iEFlags", &info)) + continue; + + int *eflags = (int *)((char *)entity->GetBaseEntity() + info.actual_offset); *eflags |= (1<<0); // EFL_KILLME } @@ -819,14 +822,13 @@ CON_COMMAND(sm_dump_datamaps, "Dumps the data map list as a text file") if (offsEFlags == -1) { - typedescription_t *datamap = gamehelpers->FindInDataMap(pMap, "m_iEFlags"); - - if (!datamap) + sm_datatable_info_t info; + if (!gamehelpers->FindDataMapInfo(pMap, "m_iEFlags", &info)) { continue; } - offsEFlags = GetTypeDescOffs(datamap); + offsEFlags = info.actual_offset; } int *eflags = (int *)((char *)entity->GetBaseEntity() + offsEFlags); diff --git a/extensions/sdktools/vnatives.cpp b/extensions/sdktools/vnatives.cpp index b130850a..af1a4752 100644 --- a/extensions/sdktools/vnatives.cpp +++ b/extensions/sdktools/vnatives.cpp @@ -548,10 +548,10 @@ static cell_t SlapPlayer(IPluginContext *pContext, const cell_t *params) if (frag_prop) { datamap_t *pMap = gamehelpers->GetDataMap(pEntity); - typedescription_t *pType = gamehelpers->FindInDataMap(pMap, frag_prop); - if (pType != NULL) + sm_datatable_info_t info; + if (gamehelpers->FindDataMapInfo(pMap, frag_prop, &info)) { - s_frag_offs = GetTypeDescOffs(pType); + s_frag_offs = info.actual_offset; } } if (!s_frag_offs) @@ -684,10 +684,13 @@ static cell_t NativeFindEntityByClassname(IPluginContext *pContext, const cell_t static int offset = -1; if (offset == -1) { - offset = GetTypeDescOffs( - gamehelpers->FindInDataMap(gamehelpers->GetDataMap(pEntity), - "m_iClassname") - ); + sm_datatable_info_t info; + if (!gamehelpers->FindDataMapInfo(gamehelpers->GetDataMap(pEntity), "m_iClassname", &info)) + { + return -1; + } + + offset = info.actual_offset; } string_t s; diff --git a/plugins/include/entity.inc b/plugins/include/entity.inc index 950ef07e..a1743de2 100644 --- a/plugins/include/entity.inc +++ b/plugins/include/entity.inc @@ -449,6 +449,26 @@ native FindDataMapOffs(entity, const String:prop[], &PropFieldType:type=PropFieldType:0, &num_bits=0); + +/** + * Given an entity, finds a nested 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). + * @param local_offset Optional parameter to store the local offset, as + * FindDataMapOffs() would return. + * @return An offset, or -1 on failure. + */ +native FindDataMapInfo(entity, + const String:prop[], + &PropFieldType:type=PropFieldType:0, + &num_bits=0, + &local_offset=0); /** * Wrapper function for finding a send property for a particular entity. diff --git a/public/IGameHelpers.h b/public/IGameHelpers.h index a04de922..443a0866 100644 --- a/public/IGameHelpers.h +++ b/public/IGameHelpers.h @@ -40,7 +40,7 @@ */ #define SMINTERFACE_GAMEHELPERS_NAME "IGameHelpers" -#define SMINTERFACE_GAMEHELPERS_VERSION 9 +#define SMINTERFACE_GAMEHELPERS_VERSION 10 class CBaseEntity; class CBaseHandle; @@ -66,6 +66,12 @@ namespace SourceMod SendProp *prop; /**< Property instance. */ unsigned int actual_offset; /**< Actual computed offset. */ }; + + struct sm_datatable_info_t + { + typedescription_t *prop; /**< Property instance. */ + unsigned int actual_offset; /**< Actual computed offset. */ + }; class IGameHelpers : public SMInterface { @@ -318,6 +324,17 @@ namespace SourceMod * @return True if valid, otherwise false. */ virtual bool IsMapValid(const char *map) =0; + + /** + * @brief Finds a datamap_t definition. + * + * @param pMap datamap_t pointer. + * @param offset Property name. + * @param pDataTable Buffer to store sm_datatable_info_t data. + * @return typedescription_t pointer on success, NULL + * on failure. + */ + virtual bool FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatable_info_t *pDataTable) =0; }; }