Add support for nested datatables. (bug 5446, r=asherkin)

This commit is contained in:
Kyle Sanderson 2013-08-12 00:54:21 +01:00
parent 6af85409f1
commit b9cd424186
8 changed files with 269 additions and 165 deletions

View File

@ -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; i<pMap->dataNumFields; 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();
}
if (!sm_trie_retrieve(val.trie, offset, (void **)&td))
{
if ((td = UTIL_FindInDataMap(pMap, offset, isNested)) != NULL)
{
sm_trie_insert(val.trie, offset, td);
}
val.trie = new KTrie<sm_datatable_info_t>;
}
return td;
sm_datatable_info_t * pNewTable = val.trie->retrieve(offset);
if (!pNewTable)
{
if (UTIL_FindDataMapInfo(pMap, offset, pDataTable))
{
val.trie->insert(offset, *pDataTable);
}
else
{
pDataTable->prop = NULL;
}
}
else
{
*pDataTable = *pNewTable;
}
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);

View File

@ -66,7 +66,7 @@ struct DataTableInfo
struct DataMapTrie
{
DataMapTrie() : trie(NULL) {}
Trie *trie;
KTrie<sm_datatable_info_t> *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);

View File

@ -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,15 +891,14 @@ 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)
{
cell_t *pType, *pSize;
@ -831,83 +906,61 @@ 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}
};

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -450,6 +450,26 @@ native FindDataMapOffs(entity,
&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.
*

View File

@ -40,7 +40,7 @@
*/
#define SMINTERFACE_GAMEHELPERS_NAME "IGameHelpers"
#define SMINTERFACE_GAMEHELPERS_VERSION 9
#define SMINTERFACE_GAMEHELPERS_VERSION 10
class CBaseEntity;
class CBaseHandle;
@ -67,6 +67,12 @@ namespace SourceMod
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
{
public:
@ -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;
};
}