Support reading legacy sendprop arrays (#1550)
This rounds out the work started in #1548 to complete support for reading the older SendPropArray type array netprops, along with bringing SDKTools' GameRule netprop code in sync with core to add string array support. There aren't many SendPropArray type props around but this opens up a few interesting opportunities for plugin developers, particularly in L4D2 with manipulation of the EMS HUD. Tested reading the `m_vCPPositions` array in TF2, and reading/writing the `m_szScriptedHUDStringSet` EMS HUD netprop in L4D2. Closes #1386.
This commit is contained in:
parent
f4ff2ad45a
commit
5ea096e61e
@ -1217,6 +1217,37 @@ static cell_t SetEntDataString(IPluginContext *pContext, const cell_t *params)
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case DPT_Array: \
|
||||
{ \
|
||||
int elementCount = pProp->GetNumElements(); \
|
||||
int elementStride = pProp->GetElementStride(); \
|
||||
if (element < 0 || element >= elementCount) \
|
||||
{ \
|
||||
return pContext->ThrowNativeError("Element %d is out of bounds (Prop %s has %d elements).", \
|
||||
element, \
|
||||
prop, \
|
||||
elementCount); \
|
||||
} \
|
||||
\
|
||||
pProp = pProp->GetArrayProp(); \
|
||||
if (!pProp) { \
|
||||
return pContext->ThrowNativeError("Error looking up ArrayProp for prop %s", \
|
||||
prop); \
|
||||
} \
|
||||
\
|
||||
if (pProp->GetType() != type) \
|
||||
{ \
|
||||
return pContext->ThrowNativeError("SendProp %s type is not " type_name " ([%d,%d] != %d)", \
|
||||
prop, \
|
||||
pProp->GetType(), \
|
||||
pProp->m_nBits, \
|
||||
type); \
|
||||
} \
|
||||
\
|
||||
offset += pProp->GetOffset() + (elementStride * element); \
|
||||
bit_count = pProp->m_nBits; \
|
||||
break; \
|
||||
} \
|
||||
case DPT_DataTable: \
|
||||
{ \
|
||||
FIND_PROP_SEND_IN_SENDTABLE(info, pProp, element, type, type_name); \
|
||||
|
@ -108,7 +108,7 @@ enum PropFieldType
|
||||
{ \
|
||||
case type: \
|
||||
{ \
|
||||
if (element > 0) \
|
||||
if (element != 0) \
|
||||
{ \
|
||||
return pContext->ThrowNativeError("SendProp %s is not an array. Element %d is invalid.", \
|
||||
prop, \
|
||||
@ -116,6 +116,37 @@ enum PropFieldType
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case DPT_Array: \
|
||||
{ \
|
||||
int elementCount = pProp->GetNumElements(); \
|
||||
int elementStride = pProp->GetElementStride(); \
|
||||
if (element < 0 || element >= elementCount) \
|
||||
{ \
|
||||
return pContext->ThrowNativeError("Element %d is out of bounds (Prop %s has %d elements).", \
|
||||
element, \
|
||||
prop, \
|
||||
elementCount); \
|
||||
} \
|
||||
\
|
||||
pProp = pProp->GetArrayProp(); \
|
||||
if (!pProp) { \
|
||||
return pContext->ThrowNativeError("Error looking up ArrayProp for prop %s", \
|
||||
prop); \
|
||||
} \
|
||||
\
|
||||
if (pProp->GetType() != type) \
|
||||
{ \
|
||||
return pContext->ThrowNativeError("SendProp %s type is not " type_name " ([%d,%d] != %d)", \
|
||||
prop, \
|
||||
pProp->GetType(), \
|
||||
pProp->m_nBits, \
|
||||
type); \
|
||||
} \
|
||||
\
|
||||
offset += pProp->GetOffset() + (elementStride * element); \
|
||||
bit_count = pProp->m_nBits; \
|
||||
break; \
|
||||
} \
|
||||
case DPT_DataTable: \
|
||||
{ \
|
||||
FIND_PROP_SEND_IN_SENDTABLE(info, pProp, element, type, type_name); \
|
||||
@ -142,7 +173,7 @@ enum PropFieldType
|
||||
} \
|
||||
\
|
||||
int elementCount = pTable->GetNumProps(); \
|
||||
if (element >= elementCount) \
|
||||
if (element < 0 || element >= elementCount) \
|
||||
{ \
|
||||
return pContext->ThrowNativeError("Element %d is out of bounds (Prop %s has %d elements).", \
|
||||
element, \
|
||||
@ -507,6 +538,13 @@ static cell_t GameRules_GetPropString(IPluginContext *pContext, const cell_t *pa
|
||||
{
|
||||
char *prop;
|
||||
int offset;
|
||||
int bit_count;
|
||||
|
||||
int element = 0;
|
||||
if (params[0] >= 4)
|
||||
{
|
||||
element = params[4];
|
||||
}
|
||||
|
||||
void *pGameRules = GameRules();
|
||||
|
||||
@ -515,56 +553,43 @@ static cell_t GameRules_GetPropString(IPluginContext *pContext, const cell_t *pa
|
||||
|
||||
pContext->LocalToString(params[1], &prop);
|
||||
|
||||
sm_sendprop_info_t info;
|
||||
if (!gamehelpers->FindSendPropInfo(g_szGameRulesProxy, prop, &info))
|
||||
{
|
||||
return pContext->ThrowNativeError("Property \"%s\" not found on the gamerules proxy", prop);
|
||||
}
|
||||
|
||||
offset = info.actual_offset;
|
||||
|
||||
if (info.prop->GetType() != DPT_String)
|
||||
{
|
||||
return pContext->ThrowNativeError("SendProp %s type is not a string (%d != %d)",
|
||||
prop,
|
||||
info.prop->GetType(),
|
||||
DPT_String);
|
||||
}
|
||||
FIND_PROP_SEND(DPT_String, "string");
|
||||
|
||||
size_t len;
|
||||
const char *src;
|
||||
|
||||
src = (char *)((intptr_t)pGameRules + offset);
|
||||
|
||||
pContext->StringToLocalUTF8(params[2], params[3], src, &len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// From sm_stringutil
|
||||
inline int strncopy(char *dest, const char *src, size_t count)
|
||||
{
|
||||
if (!count)
|
||||
if (pProp->GetProxyFn())
|
||||
{
|
||||
return 0;
|
||||
DVariant var;
|
||||
pProp->GetProxyFn()(pProp, pGameRules, (const void *)((intptr_t)pGameRules + offset), &var, element, 0 /* TODO */);
|
||||
src = var.m_pString;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = *(char **)((uint8_t *)pGameRules + offset);
|
||||
}
|
||||
|
||||
char *start = dest;
|
||||
while ((*src) && (--count))
|
||||
if (src)
|
||||
{
|
||||
*dest++ = *src++;
|
||||
size_t len;
|
||||
pContext->StringToLocalUTF8(params[2], params[3], src, &len);
|
||||
return len;
|
||||
}
|
||||
*dest = '\0';
|
||||
|
||||
return (dest - start);
|
||||
pContext->StringToLocal(params[2], params[3], "");
|
||||
return 0;
|
||||
}
|
||||
//
|
||||
|
||||
static cell_t GameRules_SetPropString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *prop;
|
||||
int offset;
|
||||
int maxlen;
|
||||
int bit_count;
|
||||
|
||||
int element = 0;
|
||||
if (params[0] >= 4)
|
||||
{
|
||||
element = params[4];
|
||||
}
|
||||
|
||||
void *pGameRules = GameRules();
|
||||
|
||||
@ -577,29 +602,46 @@ static cell_t GameRules_SetPropString(IPluginContext *pContext, const cell_t *pa
|
||||
|
||||
pContext->LocalToString(params[1], &prop);
|
||||
|
||||
sm_sendprop_info_t info;
|
||||
if (!gamehelpers->FindSendPropInfo(g_szGameRulesProxy, prop, &info))
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
if (!g_SdkTools.CanSetCSGOEntProp(prop))
|
||||
{
|
||||
return pContext->ThrowNativeError("Property \"%s\" not found on the gamerules proxy", prop);
|
||||
return pContext->ThrowNativeError("Cannot set ent prop %s with core.cfg option \"FollowCSGOServerGuidelines\" enabled.", prop);
|
||||
}
|
||||
|
||||
offset = info.actual_offset;
|
||||
|
||||
if (info.prop->GetType() != DPT_String)
|
||||
#endif
|
||||
|
||||
FIND_PROP_SEND(DPT_String, "string");
|
||||
|
||||
bool bIsStringIndex = false;
|
||||
if (pProp->GetProxyFn())
|
||||
{
|
||||
return pContext->ThrowNativeError("SendProp %s type is not a string (%d != %d)",
|
||||
prop,
|
||||
info.prop->GetType(),
|
||||
DPT_String);
|
||||
DVariant var;
|
||||
pProp->GetProxyFn()(pProp, pGameRules, (const void *)((intptr_t)pGameRules + offset), &var, element, 0 /* TODO */);
|
||||
if (var.m_pString == ((string_t *)((intptr_t)pGameRules + offset))->ToCStr())
|
||||
{
|
||||
bIsStringIndex = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Only used if not string index.
|
||||
// TODO: If we're writing to a DPT_Array, we should use the element stride here.
|
||||
maxlen = DT_MAX_STRING_BUFFERSIZE;
|
||||
|
||||
char *src;
|
||||
char *dest = (char *)((intptr_t)pGameRules + offset);
|
||||
|
||||
size_t len;
|
||||
pContext->LocalToString(params[2], &src);
|
||||
size_t len = strncopy(dest, src, maxlen);
|
||||
|
||||
if (bIsStringIndex)
|
||||
{
|
||||
return pContext->ThrowNativeError("Setting string_t gamerules prop %s not supported yet.", prop);
|
||||
|
||||
// *(string_t *)((intptr_t)pGameRules + offset) = g_HL2.AllocPooledString(src);
|
||||
// len = strlen(src);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *dest = (char *)((uint8_t *)pGameRules + offset);
|
||||
len = ke::SafeStrcpy(dest, maxlen, src);
|
||||
}
|
||||
|
||||
edict_t *proxyEdict = gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy));
|
||||
if (proxyEdict != NULL)
|
||||
|
@ -169,10 +169,11 @@ native void GameRules_SetPropVector(const char[] prop, const float vec[3], int e
|
||||
* @param prop Property to use.
|
||||
* @param buffer Destination string buffer.
|
||||
* @param maxlen Maximum length of output string buffer.
|
||||
* @param element Element # (starting from 0) if property is an array.
|
||||
* @return Number of non-null bytes written.
|
||||
* @error Prop type is not a string, or lack of mod support.
|
||||
*/
|
||||
native int GameRules_GetPropString(const char[] prop, char[] buffer, int maxlen);
|
||||
native int GameRules_GetPropString(const char[] prop, char[] buffer, int maxlen, int element=0);
|
||||
|
||||
/**
|
||||
* Sets a gamerules property as a string.
|
||||
@ -180,10 +181,11 @@ native int GameRules_GetPropString(const char[] prop, char[] buffer, int maxlen)
|
||||
* @param prop Property to use.
|
||||
* @param buffer String to set.
|
||||
* @param changeState This parameter is ignored.
|
||||
* @param element Element # (starting from 0) if property is an array.
|
||||
* @return Number of non-null bytes written.
|
||||
* @error Prop type is not a string, or lack of mod support.
|
||||
*/
|
||||
native int GameRules_SetPropString(const char[] prop, const char[] buffer, bool changeState=false);
|
||||
native int GameRules_SetPropString(const char[] prop, const char[] buffer, bool changeState=false, int element=0);
|
||||
|
||||
/**
|
||||
* Gets the current round state.
|
||||
|
Loading…
Reference in New Issue
Block a user