Add support for setting string_t values with SetEntPropString.

Adds new AllowPooledString func to CHalfLife2 to allocate a string in the game's string pool.
Also fixes SetEntPropString using incorrect offset for nested sendprops.
This commit is contained in:
Nicholas Hastings 2015-07-02 14:47:31 -04:00
parent 93bec920cf
commit e0a83ff7b2
4 changed files with 98 additions and 17 deletions

View File

@ -1277,3 +1277,37 @@ bool CHalfLife2::IsMapValid(const char *map)
return FindMap(szTmp, sizeof(szTmp)) != SMFindMapResult::NotFound; return FindMap(szTmp, sizeof(szTmp)) != SMFindMapResult::NotFound;
} }
string_t CHalfLife2::AllocPooledString(const char *pszValue)
{
// This is admittedly a giant hack, but it's a relatively safe method for
// inserting a string into the game's string pool that isn't likely to break.
//
// We find the first valid ent (should always be worldspawn), save off it's
// current targetname string_t, set it to our string to insert via SetKeyValue,
// read back the new targetname value, restore the old value, and return the new one.
CBaseEntity *pEntity = ((IServerUnknown *) servertools->FirstEntity())->GetBaseEntity();
auto *pNetworkable = ((IServerUnknown *) pEntity)->GetNetworkable();
assert(pNetworkable);
auto pServerClass = pNetworkable->GetServerClass();
assert(pServerClass);
static int offset = -1;
if (offset == -1)
{
sm_sendprop_info_t info;
bool found = UTIL_FindInSendTable(pServerClass->m_pTable, "m_iName", &info, 0);
assert(found);
offset = info.actual_offset;
}
string_t *pProp = (string_t *) ((intp) pEntity + offset);
string_t backup = *pProp;
servertools->SetKeyValue(pEntity, "targetname", pszValue);
string_t newString = *pProp;
*pProp = backup;
return newString;
}

View File

@ -47,9 +47,7 @@
#include <datamap.h> #include <datamap.h>
#include <ihandleentity.h> #include <ihandleentity.h>
#include <tier0/icommandline.h> #include <tier0/icommandline.h>
#if SOURCE_ENGINE >= SE_PORTAL2
#include <string_t.h> #include <string_t.h>
#endif
class CCommand; class CCommand;
@ -185,6 +183,7 @@ public: //IGameHelpers
const char *GetEntityClassname(CBaseEntity *pEntity); const char *GetEntityClassname(CBaseEntity *pEntity);
bool IsMapValid(const char *map); bool IsMapValid(const char *map);
SMFindMapResult FindMap(char *pMapName, int nMapNameMax); SMFindMapResult FindMap(char *pMapName, int nMapNameMax);
string_t AllocPooledString(const char *pszValue);
public: public:
void AddToFakeCliCmdQueue(int client, int userid, const char *cmd); void AddToFakeCliCmdQueue(int client, int userid, const char *cmd);
void ProcessFakeCliCmdQueue(); void ProcessFakeCliCmdQueue();

View File

@ -2020,6 +2020,13 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
int offset; int offset;
int maxlen; int maxlen;
edict_t *pEdict; edict_t *pEdict;
bool bIsStringIndex;
int element = 0;
if (params[0] >= 5)
{
element = params[5];
}
if (!IndexToAThings(params[1], &pEntity, &pEdict)) if (!IndexToAThings(params[1], &pEntity, &pEdict))
{ {
@ -2043,17 +2050,31 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
} }
typedescription_t *td = info.prop; typedescription_t *td = info.prop;
if (td->fieldType != FIELD_CHARACTER) if (td->fieldType != FIELD_CHARACTER
&& td->fieldType != FIELD_STRING
&& td->fieldType != FIELD_MODELNAME
&& td->fieldType != FIELD_SOUNDNAME)
{ {
return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop); return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
} }
offset = info.actual_offset; offset = info.actual_offset;
maxlen = td->fieldSize;
bIsStringIndex = (td->fieldType != FIELD_CHARACTER);
if (bIsStringIndex)
{
offset += (element * (td->fieldSizeInBytes / td->fieldSize));
}
else
{
maxlen = td->fieldSize;
}
break; break;
} }
case Prop_Send: case Prop_Send:
{ {
char *prop; sm_sendprop_info_t info;
IServerUnknown *pUnk = (IServerUnknown *)pEntity; IServerUnknown *pUnk = (IServerUnknown *)pEntity;
IServerNetworkable *pNet = pUnk->GetNetworkable(); IServerNetworkable *pNet = pUnk->GetNetworkable();
if (!pNet) if (!pNet)
@ -2061,17 +2082,37 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("The edict is not networkable"); return pContext->ThrowNativeError("The edict is not networkable");
} }
pContext->LocalToString(params[3], &prop); pContext->LocalToString(params[3], &prop);
SendProp *pSend = g_HL2.FindInSendTable(pNet->GetServerClass()->GetName(), prop); if (!g_HL2.FindSendPropInfo(pNet->GetServerClass()->GetName(), prop, &info))
if (!pSend)
{ {
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]);
} }
if (pSend->GetType() != DPT_String)
offset = info.prop->GetOffset();
if (info.prop->GetType() != DPT_String)
{ {
return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop); return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
} }
offset = pSend->GetOffset(); else if (element != 0)
maxlen = DT_MAX_STRING_BUFFERSIZE; {
return pContext->ThrowNativeError("SendProp %s is not an array. Element %d is invalid.",
prop,
element);
}
if (info.prop->GetProxyFn())
{
DVariant var;
info.prop->GetProxyFn()(info.prop, pEntity, (const void *) ((intptr_t) pEntity + offset), &var, element, params[1]);
if (var.m_pString == ((string_t *) ((intptr_t) pEntity + offset))->ToCStr())
{
bIsStringIndex = true;
}
}
else
{
maxlen = DT_MAX_STRING_BUFFERSIZE;
}
break; break;
} }
default: default:
@ -2081,10 +2122,19 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
} }
char *src; char *src;
char *dest = (char *)((uint8_t *)pEntity + offset); size_t len;
pContext->LocalToString(params[4], &src); pContext->LocalToString(params[4], &src);
size_t len = strncopy(dest, src, maxlen);
if (bIsStringIndex)
{
*(string_t *) ((intptr_t) pEntity + offset) = g_HL2.AllocPooledString(src);
len = strlen(src);
}
else
{
char *dest = (char *) ((uint8_t *) pEntity + offset);
len = strncopy(dest, src, maxlen);
}
if (params[2] == Prop_Send && (pEdict != NULL)) if (params[2] == Prop_Send && (pEdict != NULL))
{ {

View File

@ -651,17 +651,15 @@ native GetEntPropString(entity, PropType:type, const String:prop[], String:buffe
/** /**
* Sets a network property as a string. * Sets a network property as a string.
* *
* This cannot set property fields of type PropField_String_T (such as "m_target").
* To set such fields, you should use DispatchKeyValue() from SDKTools.
*
* @param entity Edict index. * @param entity Edict index.
* @param type Property type. * @param type Property type.
* @param prop Property to use. * @param prop Property to use.
* @param buffer String to set. * @param buffer String to set.
* @param element Element # (starting from 0) if property is an array.
* @return Number of non-null bytes written. * @return Number of non-null bytes written.
* @error Invalid entity, offset out of reasonable bounds, or property is not a valid string. * @error Invalid entity, offset out of reasonable bounds, or property is not a valid string.
*/ */
native SetEntPropString(entity, PropType:type, const String:prop[], const String:buffer[]); native SetEntPropString(entity, PropType:type, const String:prop[], const String:buffer[], element=0);
/** /**
* Retrieves the count of values that an entity property's array can store. * Retrieves the count of values that an entity property's array can store.