diff --git a/core/smn_entities.cpp b/core/smn_entities.cpp index 836ed757..fc54d7a6 100644 --- a/core/smn_entities.cpp +++ b/core/smn_entities.cpp @@ -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); \ diff --git a/extensions/sdktools/gamerulesnatives.cpp b/extensions/sdktools/gamerulesnatives.cpp index d44801d5..0e5059f6 100644 --- a/extensions/sdktools/gamerulesnatives.cpp +++ b/extensions/sdktools/gamerulesnatives.cpp @@ -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) diff --git a/plugins/include/sdktools_gamerules.inc b/plugins/include/sdktools_gamerules.inc index 66ba3b5c..0f1f89b6 100644 --- a/plugins/include/sdktools_gamerules.inc +++ b/plugins/include/sdktools_gamerules.inc @@ -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.