3 * =============================================================================
4 * SourceMod SDKTools Extension
5 * Copyright (C) 2004-2011 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
32 #include "extension.h"
33 #include "gamerulesnatives.h"
36 const char *g_szGameRulesProxy;
38 void GameRulesNativesInit()
40 g_szGameRulesProxy = g_pGameConf->GetKeyValue("GameRulesProxy");
43 static CBaseEntity *FindEntityByNetClass(int start, const char *classname)
45 int maxEntities = gpGlobals->maxEntities;
46 for (int i = start; i < maxEntities; i++)
48 edict_t *current = gamehelpers->EdictOfIndex(i);
52 IServerNetworkable *network = current->GetNetworkable();
56 ServerClass *sClass = network->GetServerClass();
57 const char *name = sClass->GetName();
59 if (!strcmp(name, classname))
60 return gamehelpers->ReferenceToEntity(gamehelpers->IndexOfEdict(current));
66 static CBaseEntity* GetGameRulesProxyEnt()
68 static cell_t proxyEntRef = -1;
70 if (proxyEntRef == -1 || (pProxy = gamehelpers->ReferenceToEntity(proxyEntRef)) == NULL)
72 pProxy = FindEntityByNetClass(playerhelpers->GetMaxClients(), g_szGameRulesProxy);
73 proxyEntRef = gamehelpers->EntityToReference(pProxy);
81 PropField_Unsupported, /**< The type is unsupported. */
82 PropField_Integer, /**< Valid for SendProp and Data fields */
83 PropField_Float, /**< Valid for SendProp and Data fields */
84 PropField_Entity, /**< Valid for Data fields only (SendProp shows as int) */
85 PropField_Vector, /**< Valid for SendProp and Data fields */
86 PropField_String, /**< Valid for SendProp and Data fields */
87 PropField_String_T, /**< Valid for Data fields. Read only! */
90 #define FIND_PROP_SEND(type, type_name) \
91 sm_sendprop_info_t info;\
92 if (!gamehelpers->FindSendPropInfo(g_szGameRulesProxy, prop, &info)) \
94 return pContext->ThrowNativeError("Property \"%s\" not found on the gamerules proxy", prop); \
97 offset = info.actual_offset; \
98 bit_count = info.prop->m_nBits; \
100 switch (info.prop->GetType()) \
106 return pContext->ThrowNativeError("SendProp %s is not an array. Element %d is invalid.", \
112 case DPT_DataTable: \
115 FIND_PROP_SEND_IN_SENDTABLE(info, pProp, element, type, type_name); \
117 offset += pProp->GetOffset(); \
118 bit_count = pProp->m_nBits; \
123 return pContext->ThrowNativeError("SendProp %s type is not " type_name " (%d != %d)", \
125 info.prop->GetType(), \
130 #define FIND_PROP_SEND_IN_SENDTABLE(info, pProp, element, type, type_name) \
131 SendTable *pTable = info.prop->GetDataTable(); \
134 return pContext->ThrowNativeError("Error looking up DataTable for prop %s", \
138 int elementCount = pTable->GetNumProps(); \
139 if (element >= elementCount) \
141 return pContext->ThrowNativeError("Element %d is out of bounds (Prop %s has %d elements).", \
147 pProp = pTable->GetProp(element); \
148 if (pProp->GetType() != type) \
150 return pContext->ThrowNativeError("SendProp %s type is not " type_name " ([%d,%d] != %d)", \
152 info.prop->GetType(), \
153 info.prop->m_nBits, \
157 static cell_t GameRules_GetProp(IPluginContext *pContext, const cell_t *params)
160 int element = params[3];
164 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
165 return pContext->ThrowNativeError("Gamerules lookup failed.");
167 pContext->LocalToString(params[1], &prop);
169 int elementCount = 1;
171 FIND_PROP_SEND(DPT_Int, "integer");
175 bit_count = params[2] * 8;
178 void *pGameRules = *g_pGameRules;
182 return *(int32_t *)((intptr_t)pGameRules + offset);
184 else if (bit_count >= 9)
186 return *(int16_t *)((intptr_t)pGameRules + offset);
188 else if (bit_count >= 2)
190 return *(int8_t *)((intptr_t)pGameRules + offset);
194 return *(bool *)((intptr_t)pGameRules + offset) ? 1 : 0;
200 static cell_t GameRules_SetProp(IPluginContext *pContext, const cell_t *params)
203 int element = params[4];
207 bool sendChange = true;
211 CBaseEntity *pProxy = NULL;
212 if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
213 return pContext->ThrowNativeError("Couldn't find gamerules proxy entity");
215 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
216 return pContext->ThrowNativeError("Gamerules lookup failed");
218 pContext->LocalToString(params[1], &prop);
220 FIND_PROP_SEND(DPT_Int, "integer");
222 void *pGameRules = *g_pGameRules;
226 bit_count = params[3] * 8;
231 *(int32_t *)((intptr_t)pGameRules + offset) = params[2];
234 *(int32_t *)((intptr_t)pProxy + offset) = params[2];
235 gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
238 else if (bit_count >= 9)
240 *(int16_t *)((intptr_t)pGameRules + offset) = (int16_t)params[2];
243 *(int16_t *)((intptr_t)pProxy + offset) = (int16_t)params[2];
244 gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
247 else if (bit_count >= 2)
249 *(int8_t *)((intptr_t)pGameRules + offset) = (int8_t)params[2];
252 *(int8_t *)((intptr_t)pProxy + offset) = (int8_t)params[2];
253 gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
258 *(bool *)((intptr_t)pGameRules + offset) = (params[2] == 0) ? false : true;
261 *(bool *)((intptr_t)pProxy + offset) = (params[2] == 0) ? false : true;
262 gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
269 static cell_t GameRules_GetPropFloat(IPluginContext *pContext, const cell_t *params)
272 int element = params[2];
276 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
277 return pContext->ThrowNativeError("Gamerules lookup failed.");
279 pContext->LocalToString(params[1], &prop);
281 FIND_PROP_SEND(DPT_Float, "float");
283 void *pGameRules = *g_pGameRules;
285 float val = *(float *)((intptr_t)pGameRules + offset);
290 static cell_t GameRules_SetPropFloat(IPluginContext *pContext, const cell_t *params)
293 int element = params[3];
297 bool sendChange = true;
301 CBaseEntity *pProxy = NULL;
302 if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
303 return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
305 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
306 return pContext->ThrowNativeError("Gamerules lookup failed.");
308 pContext->LocalToString(params[1], &prop);
310 FIND_PROP_SEND(DPT_Float, "float");
312 void *pGameRules = *g_pGameRules;
313 float newVal = sp_ctof(params[2]);
315 *(float *)((intptr_t)pGameRules + offset) = newVal;
319 *(float *)((intptr_t)pProxy + offset) = newVal;
320 gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
326 static cell_t GameRules_GetPropEnt(IPluginContext *pContext, const cell_t *params)
329 int element = params[2];
333 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
334 return pContext->ThrowNativeError("Gamerules lookup failed.");
336 pContext->LocalToString(params[1], &prop);
338 FIND_PROP_SEND(DPT_Int, "Integer");
340 void *pGameRules = *g_pGameRules;
342 CBaseHandle &hndl = *(CBaseHandle *)((intptr_t)pGameRules + offset);
344 int ref = gamehelpers->IndexToReference(hndl.GetEntryIndex());
345 return gamehelpers->ReferenceToBCompatRef(ref);
348 static cell_t GameRules_SetPropEnt(IPluginContext *pContext, const cell_t *params)
351 int element = params[3];
355 bool sendChange = true;
359 CBaseEntity *pProxy = NULL;
360 if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
361 return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
363 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
364 return pContext->ThrowNativeError("Gamerules lookup failed.");
366 pContext->LocalToString(params[1], &prop);
368 FIND_PROP_SEND(DPT_Int, "integer");
370 void *pGameRules = *g_pGameRules;
372 CBaseHandle &hndl = *(CBaseHandle *)((intptr_t)pGameRules + offset);
381 pOther = gamehelpers->ReferenceToEntity(params[2]);
385 return pContext->ThrowNativeError("Entity %d (%d) is invalid", gamehelpers->ReferenceToIndex(params[4]), params[4]);
388 IHandleEntity *pHandleEnt = (IHandleEntity *)pOther;
389 hndl.Set(pHandleEnt);
394 CBaseHandle &hndl = *(CBaseHandle *)((intptr_t)pProxy + offset);
401 IHandleEntity *pHandleEnt = (IHandleEntity *)pOther;
402 hndl.Set(pHandleEnt);
405 gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
411 static cell_t GameRules_GetPropVector(IPluginContext *pContext, const cell_t *params)
414 int element = params[3];
418 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
419 return pContext->ThrowNativeError("Gamerules lookup failed.");
421 pContext->LocalToString(params[1], &prop);
423 FIND_PROP_SEND(DPT_Vector, "vector");
425 void *pGameRules = *g_pGameRules;
427 Vector *v = (Vector *)((intptr_t)pGameRules + offset);
430 pContext->LocalToPhysAddr(params[2], &vec);
432 vec[0] = sp_ftoc(v->x);
433 vec[1] = sp_ftoc(v->y);
434 vec[2] = sp_ftoc(v->z);
439 static cell_t GameRules_SetPropVector(IPluginContext *pContext, const cell_t *params)
442 int element = params[3];
446 bool sendChange = true;
450 CBaseEntity *pProxy = NULL;
451 if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
452 return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
454 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
455 return pContext->ThrowNativeError("Gamerules lookup failed.");
457 pContext->LocalToString(params[1], &prop);
459 FIND_PROP_SEND(DPT_Vector, "vector");
461 void *pGameRules = *g_pGameRules;
463 Vector *v = (Vector *)((intptr_t)pGameRules + offset);
466 pContext->LocalToPhysAddr(params[2], &vec);
468 v->x = sp_ctof(vec[0]);
469 v->y = sp_ctof(vec[1]);
470 v->z = sp_ctof(vec[2]);
474 v = (Vector *)((intptr_t)g_pGameRules + offset);
475 v->x = sp_ctof(vec[0]);
476 v->y = sp_ctof(vec[1]);
477 v->z = sp_ctof(vec[2]);
479 gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
485 static cell_t GameRules_GetPropString(IPluginContext *pContext, const cell_t *params)
490 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
491 return pContext->ThrowNativeError("Gamerules lookup failed.");
493 pContext->LocalToString(params[1], &prop);
495 sm_sendprop_info_t info;
496 if (!gamehelpers->FindSendPropInfo(g_szGameRulesProxy, prop, &info))
498 return pContext->ThrowNativeError("Property \"%s\" not found on the gamerules proxy", prop);
501 offset = info.actual_offset;
503 if (info.prop->GetType() != DPT_String)
505 return pContext->ThrowNativeError("SendProp %s type is not a string (%d != %d)",
507 info.prop->GetType(),
511 void *pGameRules = *g_pGameRules;
516 src = (char *)((intptr_t)pGameRules + offset);
518 pContext->StringToLocalUTF8(params[2], params[3], src, &len);
523 // From sm_stringutil
524 inline int strncopy(char *dest, const char *src, size_t count)
532 while ((*src) && (--count))
538 return (dest - start);
542 static cell_t GameRules_SetPropString(IPluginContext *pContext, const cell_t *params)
548 bool sendChange = true;
552 CBaseEntity *pProxy = NULL;
553 if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
554 return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
556 if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
557 return pContext->ThrowNativeError("Gamerules lookup failed.");
559 pContext->LocalToString(params[1], &prop);
561 sm_sendprop_info_t info;
562 if (!gamehelpers->FindSendPropInfo(g_szGameRulesProxy, prop, &info))
564 return pContext->ThrowNativeError("Property \"%s\" not found on the gamerules proxy", prop);
567 offset = info.actual_offset;
569 if (info.prop->GetType() != DPT_String)
571 return pContext->ThrowNativeError("SendProp %s type is not a string (%d != %d)",
573 info.prop->GetType(),
577 void *pGameRules = *g_pGameRules;
578 maxlen = DT_MAX_STRING_BUFFERSIZE;
581 char *dest = (char *)((intptr_t)pGameRules + offset);
583 pContext->LocalToString(params[2], &src);
584 size_t len = strncopy(dest, src, maxlen);
588 dest = (char *)((intptr_t)pProxy + offset);
589 strncopy(dest, src, maxlen);
590 gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
596 sp_nativeinfo_t g_GameRulesNatives[] =
598 {"GameRules_GetProp", GameRules_GetProp},
599 {"GameRules_SetProp", GameRules_SetProp},
600 {"GameRules_GetPropFloat", GameRules_GetPropFloat},
601 {"GameRules_SetPropFloat", GameRules_SetPropFloat},
602 {"GameRules_GetPropEnt", GameRules_GetPropEnt},
603 {"GameRules_SetPropEnt", GameRules_SetPropEnt},
604 {"GameRules_GetPropVector", GameRules_GetPropVector},
605 {"GameRules_SetPropVector", GameRules_SetPropVector},
606 {"GameRules_GetPropString", GameRules_GetPropString},
607 {"GameRules_SetPropString", GameRules_SetPropString},