Skip to content

Commit

Permalink
Merge pull request #353 from alliedmodders/getentpropstringt.
Browse files Browse the repository at this point in the history
  • Loading branch information
psychonic committed Jul 8, 2015
1 parent 7e3bd9c commit 22045bd
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 34 deletions.
34 changes: 34 additions & 0 deletions core/HalfLife2.cpp
Expand Up @@ -1256,3 +1256,37 @@ bool CHalfLife2::IsMapValid(const char *map)
#endif // SE_TF2
return ret;
}

// TODO: Add ep1 support for this. (No IServerTools available there)
#if SOURCE_ENGINE >= SE_ORANGEBOX
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 *pDataMap = GetDataMap(pEntity);
assert(pDataMap);

static int offset = -1;
if (offset == -1)
{
sm_datatable_info_t info;
bool found = FindDataMapInfo(pDataMap, "m_iName", &info);
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;
}
#endif
5 changes: 3 additions & 2 deletions core/HalfLife2.h
Expand Up @@ -47,9 +47,7 @@
#include <datamap.h>
#include <ihandleentity.h>
#include <tier0/icommandline.h>
#if SOURCE_ENGINE >= SE_PORTAL2
#include <string_t.h>
#endif

class CCommand;

Expand Down Expand Up @@ -174,6 +172,9 @@ class CHalfLife2 :
const char *GetEntityClassname(edict_t *pEdict);
const char *GetEntityClassname(CBaseEntity *pEntity);
bool IsMapValid(const char *map);
#if SOURCE_ENGINE >= SE_ORANGEBOX
string_t AllocPooledString(const char *pszValue);
#endif
public:
void AddToFakeCliCmdQueue(int client, int userid, const char *cmd);
void ProcessFakeCliCmdQueue();
Expand Down
115 changes: 87 additions & 28 deletions core/smn_entities.cpp
Expand Up @@ -1884,7 +1884,6 @@ static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params)
char *prop;
int offset;
edict_t *pEdict;
bool bIsStringIndex;

int element = 0;
if (params[0] >= 6)
Expand All @@ -1899,12 +1898,13 @@ static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params)

pContext->LocalToString(params[3], &prop);

bIsStringIndex = false;
const char *src;

switch (params[2])
{
case Prop_Data:
{
bool bIsStringIndex = false;
typedescription_t *td;

FIND_PROP_DATA(td);
Expand Down Expand Up @@ -1940,6 +1940,15 @@ static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params)
if (bIsStringIndex)
{
offset += (element * (td->fieldSizeInBytes / td->fieldSize));

string_t idx;

idx = *(string_t *) ((uint8_t *) pEntity + offset);
src = (idx == NULL_STRING) ? "" : STRING(idx);
}
else
{
src = (char *) ((uint8_t *) pEntity + offset);
}
break;
}
Expand Down Expand Up @@ -1977,6 +1986,17 @@ static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params)
prop,
element);
}

if (info.prop->GetProxyFn())
{
DVariant var;
info.prop->GetProxyFn()(info.prop, pEntity, (const void *) ((intptr_t) pEntity + offset), &var, element, params[1]);
src = var.m_pString;
}
else
{
src = (char *) ((uint8_t *) pEntity + offset);
}

break;
}
Expand All @@ -1987,22 +2007,7 @@ static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params)
}

size_t len;
const char *src;

if (!bIsStringIndex)
{
src = (char *)((uint8_t *)pEntity + offset);
}
else
{
string_t idx;

idx = *(string_t *)((uint8_t *)pEntity + offset);
src = (idx == NULL_STRING) ? "" : STRING(idx);
}

pContext->StringToLocalUTF8(params[4], params[5], src, &len);

return len;
}

Expand All @@ -2013,6 +2018,13 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
int offset;
int maxlen;
edict_t *pEdict;
bool bIsStringIndex;

int element = 0;
if (params[0] >= 5)
{
element = params[5];
}

if (!IndexToAThings(params[1], &pEntity, &pEdict))
{
Expand All @@ -2036,35 +2048,69 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
}

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

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;
}
case Prop_Send:
{
char *prop;
sm_sendprop_info_t info;
IServerUnknown *pUnk = (IServerUnknown *)pEntity;
IServerNetworkable *pNet = pUnk->GetNetworkable();
if (!pNet)
{
return pContext->ThrowNativeError("The edict is not networkable");
}
pContext->LocalToString(params[3], &prop);
SendProp *pSend = g_HL2.FindInSendTable(pNet->GetServerClass()->GetName(), prop);
if (!pSend)
if (!g_HL2.FindSendPropInfo(pNet->GetServerClass()->GetName(), prop, &info))
{
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);
}
offset = pSend->GetOffset();
maxlen = DT_MAX_STRING_BUFFERSIZE;
else if (element != 0)
{
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;
}
default:
Expand All @@ -2074,10 +2120,23 @@ static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
}

char *src;
char *dest = (char *)((uint8_t *)pEntity + offset);

size_t len;
pContext->LocalToString(params[4], &src);
size_t len = strncopy(dest, src, maxlen);

if (bIsStringIndex)
{
#if SOURCE_ENGINE < SE_ORANGEBOX
return pContext->ThrowNativeError("Cannot set %s. Setting string_t values not supported on this game.", prop);
#else
*(string_t *) ((intptr_t) pEntity + offset) = g_HL2.AllocPooledString(src);
len = strlen(src);
#endif
}
else
{
char *dest = (char *) ((uint8_t *) pEntity + offset);
len = strncopy(dest, src, maxlen);
}

if (params[2] == Prop_Send && (pEdict != NULL))
{
Expand Down
6 changes: 2 additions & 4 deletions plugins/include/entity.inc
Expand Up @@ -650,18 +650,16 @@ native GetEntPropString(entity, PropType:type, const String:prop[], String:buffe

/**
* 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 type Property type.
* @param prop Property to use.
* @param buffer String to set.
* @param element Element # (starting from 0) if property is an array.
* @return Number of non-null bytes written.
* @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.
Expand Down

0 comments on commit 22045bd

Please sign in to comment.