extensions/sdktools/gamerulesnatives.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Fri Jul 08 07:53:31 2011 -0400 (2011-07-08)
changeset 3348 fcae86e8cb8f
parent 3333 e247490894a1
child 3710 5559ea22b6b5
permissions -rw-r--r--
Backed out changeset: 9007dacc012d
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     5  * Copyright (C) 2004-2011 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     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.
    11  * 
    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
    15  * details.
    16  *
    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/>.
    19  *
    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>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "extension.h"
    33 #include "gamerulesnatives.h"
    34 #include "vglobals.h"
    35 
    36 const char *g_szGameRulesProxy;
    37 
    38 void GameRulesNativesInit()
    39 {
    40 	g_szGameRulesProxy = g_pGameConf->GetKeyValue("GameRulesProxy");
    41 }
    42 
    43 static CBaseEntity *FindEntityByNetClass(int start, const char *classname)
    44 {
    45 	int maxEntities = gpGlobals->maxEntities;
    46 	for (int i = start; i < maxEntities; i++)
    47 	{
    48 		edict_t *current = gamehelpers->EdictOfIndex(i);
    49 		if (current == NULL)
    50 			continue;
    51 
    52 		IServerNetworkable *network = current->GetNetworkable();
    53 		if (network == NULL)
    54 			continue;
    55 
    56 		ServerClass *sClass = network->GetServerClass();
    57 		const char *name = sClass->GetName();
    58 
    59 		if (!strcmp(name, classname))
    60 			return gamehelpers->ReferenceToEntity(gamehelpers->IndexOfEdict(current));		
    61 	}
    62 
    63 	return NULL;
    64 }
    65 
    66 static CBaseEntity* GetGameRulesProxyEnt()
    67 {
    68 	static cell_t proxyEntRef = -1;
    69 	CBaseEntity *pProxy;
    70 	if (proxyEntRef == -1 || (pProxy = gamehelpers->ReferenceToEntity(proxyEntRef)) == NULL)
    71 	{
    72 		pProxy = FindEntityByNetClass(playerhelpers->GetMaxClients(), g_szGameRulesProxy);
    73 		proxyEntRef = gamehelpers->EntityToReference(pProxy);
    74 	}
    75 	
    76 	return pProxy;
    77 }
    78 
    79 enum PropFieldType
    80 {
    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! */
    88 };
    89 
    90 #define FIND_PROP_SEND(type, type_name) \
    91 	sm_sendprop_info_t info;\
    92 	if (!gamehelpers->FindSendPropInfo(g_szGameRulesProxy, prop, &info)) \
    93 	{ \
    94 		return pContext->ThrowNativeError("Property \"%s\" not found on the gamerules proxy", prop); \
    95 	} \
    96 	\
    97 	offset = info.actual_offset; \
    98 	bit_count = info.prop->m_nBits; \
    99 	\
   100 	switch (info.prop->GetType()) \
   101 	{ \
   102 	case type: \
   103 		{ \
   104 			if (element > 0) \
   105 			{ \
   106 				return pContext->ThrowNativeError("SendProp %s is not an array. Element %d is invalid.", \
   107 					prop, \
   108 					element); \
   109 			} \
   110 			break; \
   111 		} \
   112 	case DPT_DataTable: \
   113 		{ \
   114 			SendProp *pProp; \
   115 			FIND_PROP_SEND_IN_SENDTABLE(info, pProp, element, type, type_name); \
   116 			\
   117 			offset += pProp->GetOffset(); \
   118 			bit_count = pProp->m_nBits; \
   119 			break; \
   120 		} \
   121 	default: \
   122 		{ \
   123 			return pContext->ThrowNativeError("SendProp %s type is not " type_name " (%d != %d)", \
   124 				prop, \
   125 				info.prop->GetType(), \
   126 				type); \
   127 		} \
   128 	} \
   129 
   130 #define FIND_PROP_SEND_IN_SENDTABLE(info, pProp, element, type, type_name) \
   131 	SendTable *pTable = info.prop->GetDataTable(); \
   132 	if (!pTable) \
   133 	{ \
   134 		return pContext->ThrowNativeError("Error looking up DataTable for prop %s", \
   135 			prop); \
   136 	} \
   137 	\
   138 	int elementCount = pTable->GetNumProps(); \
   139 	if (element >= elementCount) \
   140 	{ \
   141 		return pContext->ThrowNativeError("Element %d is out of bounds (Prop %s has %d elements).", \
   142 			element, \
   143 			prop, \
   144 			elementCount); \
   145 	} \
   146 	\
   147 	pProp = pTable->GetProp(element); \
   148 	if (pProp->GetType() != type) \
   149 	{ \
   150 		return pContext->ThrowNativeError("SendProp %s type is not " type_name " ([%d,%d] != %d)", \
   151 			prop, \
   152 			info.prop->GetType(), \
   153 			info.prop->m_nBits, \
   154 			type); \
   155 	}
   156 
   157 static cell_t GameRules_GetProp(IPluginContext *pContext, const cell_t *params)
   158 {
   159 	char *prop;
   160 	int element = params[3];
   161 	int offset;
   162 	int bit_count;
   163 
   164 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   165 		return pContext->ThrowNativeError("Gamerules lookup failed.");
   166 
   167 	pContext->LocalToString(params[1], &prop);
   168 
   169 	int elementCount = 1;
   170 
   171 	FIND_PROP_SEND(DPT_Int, "integer");
   172 
   173 	if (bit_count < 1)
   174 	{
   175 		bit_count = params[2] * 8;
   176 	}
   177 
   178 	void *pGameRules = *g_pGameRules;
   179 
   180 	if (bit_count >= 17)
   181 	{
   182 		return *(int32_t *)((intptr_t)pGameRules + offset);
   183 	}
   184 	else if (bit_count >= 9)
   185 	{
   186 		return *(int16_t *)((intptr_t)pGameRules + offset);
   187 	}
   188 	else if (bit_count >= 2)
   189 	{
   190 		return *(int8_t *)((intptr_t)pGameRules + offset);
   191 	}
   192 	else
   193 	{
   194 		return *(bool *)((intptr_t)pGameRules + offset) ? 1 : 0;
   195 	}
   196 
   197 	return -1;
   198 }
   199 
   200 static cell_t GameRules_SetProp(IPluginContext *pContext, const cell_t *params)
   201 {
   202 	char *prop;
   203 	int element = params[4];
   204 	int offset;
   205 	int bit_count;
   206 
   207 	bool sendChange = true;
   208 	if (params[5] == 0)
   209 		sendChange = false;
   210 
   211 	CBaseEntity *pProxy = NULL;
   212 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
   213 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity");
   214 	
   215 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   216 		return pContext->ThrowNativeError("Gamerules lookup failed");
   217 
   218 	pContext->LocalToString(params[1], &prop);
   219 
   220 	FIND_PROP_SEND(DPT_Int, "integer");
   221 
   222 	void *pGameRules = *g_pGameRules;
   223 
   224 	if (bit_count < 1)
   225 	{
   226 		bit_count = params[3] * 8;
   227 	}
   228 
   229 	if (bit_count >= 17)
   230 	{
   231 		*(int32_t *)((intptr_t)pGameRules + offset) = params[2];
   232 		if (sendChange)
   233 		{
   234 			*(int32_t *)((intptr_t)pProxy + offset) = params[2];
   235 			gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
   236 		}
   237 	}
   238 	else if (bit_count >= 9)
   239 	{
   240 		*(int16_t *)((intptr_t)pGameRules + offset) = (int16_t)params[2];
   241 		if (sendChange)
   242 		{
   243 			*(int16_t *)((intptr_t)pProxy + offset) = (int16_t)params[2];
   244 			gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
   245 		}
   246 	}
   247 	else if (bit_count >= 2)
   248 	{
   249 		*(int8_t *)((intptr_t)pGameRules + offset) = (int8_t)params[2];
   250 		if (sendChange)
   251 		{
   252 			*(int8_t *)((intptr_t)pProxy + offset) = (int8_t)params[2];
   253 			gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
   254 		}
   255 	}
   256 	else
   257 	{
   258 		*(bool *)((intptr_t)pGameRules + offset) = (params[2] == 0) ? false : true;
   259 		if (sendChange)
   260 		{
   261 			*(bool *)((intptr_t)pProxy + offset) = (params[2] == 0) ? false : true;
   262 			gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
   263 		}
   264 	}
   265 
   266 	return 0;
   267 }
   268 
   269 static cell_t GameRules_GetPropFloat(IPluginContext *pContext, const cell_t *params)
   270 {
   271 	char *prop;
   272 	int element = params[2];
   273 	int offset;
   274 	int bit_count;
   275 
   276 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   277 		return pContext->ThrowNativeError("Gamerules lookup failed.");
   278 
   279 	pContext->LocalToString(params[1], &prop);
   280 
   281 	FIND_PROP_SEND(DPT_Float, "float");
   282 
   283 	void *pGameRules = *g_pGameRules;
   284 
   285 	float val = *(float *)((intptr_t)pGameRules + offset);
   286 
   287 	return sp_ftoc(val);
   288 }
   289 
   290 static cell_t GameRules_SetPropFloat(IPluginContext *pContext, const cell_t *params)
   291 {
   292 	char *prop;
   293 	int element = params[3];
   294 	int offset;
   295 	int bit_count;
   296 
   297 	bool sendChange = true;
   298 	if (params[4] == 0)
   299 		sendChange = false;
   300 
   301 	CBaseEntity *pProxy = NULL;
   302 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
   303 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
   304 	
   305 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   306 		return pContext->ThrowNativeError("Gamerules lookup failed.");
   307 
   308 	pContext->LocalToString(params[1], &prop);
   309 
   310 	FIND_PROP_SEND(DPT_Float, "float");
   311 
   312 	void *pGameRules = *g_pGameRules;
   313 	float newVal = sp_ctof(params[2]);
   314 
   315 	*(float *)((intptr_t)pGameRules + offset) = newVal;
   316 
   317 	if (sendChange)
   318 	{
   319 		*(float *)((intptr_t)pProxy + offset) = newVal;
   320 		gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
   321 	}
   322 
   323 	return 0;
   324 }
   325 
   326 static cell_t GameRules_GetPropEnt(IPluginContext *pContext, const cell_t *params)
   327 {
   328 	char *prop;
   329 	int element = params[2];
   330 	int offset;
   331 	int bit_count;
   332 
   333 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   334 		return pContext->ThrowNativeError("Gamerules lookup failed.");
   335 
   336 	pContext->LocalToString(params[1], &prop);
   337 
   338 	FIND_PROP_SEND(DPT_Int, "Integer");
   339 
   340 	void *pGameRules = *g_pGameRules;
   341 
   342 	CBaseHandle &hndl = *(CBaseHandle *)((intptr_t)pGameRules + offset);
   343 
   344 	int ref = gamehelpers->IndexToReference(hndl.GetEntryIndex());
   345 	return gamehelpers->ReferenceToBCompatRef(ref);
   346 }
   347 
   348 static cell_t GameRules_SetPropEnt(IPluginContext *pContext, const cell_t *params)
   349 {
   350 	char *prop;
   351 	int element = params[3];
   352 	int offset;
   353 	int bit_count;
   354 
   355 	bool sendChange = true;
   356 	if (params[4] == 0)
   357 		sendChange = false;
   358 
   359 	CBaseEntity *pProxy = NULL;
   360 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
   361 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
   362 	
   363 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   364 		return pContext->ThrowNativeError("Gamerules lookup failed.");
   365 
   366 	pContext->LocalToString(params[1], &prop);
   367 
   368 	FIND_PROP_SEND(DPT_Int, "integer");
   369 
   370 	void *pGameRules = *g_pGameRules;
   371 
   372 	CBaseHandle &hndl = *(CBaseHandle *)((intptr_t)pGameRules + offset);
   373 	CBaseEntity *pOther;
   374 
   375 	if (params[2] == -1)
   376 	{
   377 		hndl.Set(NULL);
   378 	}
   379 	else
   380 	{
   381 		pOther = gamehelpers->ReferenceToEntity(params[2]);
   382 
   383 		if (!pOther)
   384 		{
   385 			return pContext->ThrowNativeError("Entity %d (%d) is invalid", gamehelpers->ReferenceToIndex(params[4]), params[4]);
   386 		}
   387 
   388 		IHandleEntity *pHandleEnt = (IHandleEntity *)pOther;
   389 		hndl.Set(pHandleEnt);
   390 	}
   391 
   392 	if (sendChange)
   393 	{
   394 		CBaseHandle &hndl = *(CBaseHandle *)((intptr_t)pProxy + offset);
   395 		if (params[2] == -1)
   396 		{
   397 			hndl.Set(NULL);
   398 		}
   399 		else
   400 		{
   401 			IHandleEntity *pHandleEnt = (IHandleEntity *)pOther;
   402 			hndl.Set(pHandleEnt);
   403 		}
   404 
   405 		gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
   406 	}
   407 
   408 	return 0;
   409 }
   410 
   411 static cell_t GameRules_GetPropVector(IPluginContext *pContext, const cell_t *params)
   412 {
   413 	char *prop;
   414 	int element = params[3];
   415 	int offset;
   416 	int bit_count;
   417 
   418 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   419 		return pContext->ThrowNativeError("Gamerules lookup failed.");
   420 
   421 	pContext->LocalToString(params[1], &prop);
   422 
   423 	FIND_PROP_SEND(DPT_Vector, "vector");
   424 
   425 	void *pGameRules = *g_pGameRules;
   426 
   427 	Vector *v = (Vector *)((intptr_t)pGameRules + offset);
   428 
   429 	cell_t *vec;
   430 	pContext->LocalToPhysAddr(params[2], &vec);
   431 
   432 	vec[0] = sp_ftoc(v->x);
   433 	vec[1] = sp_ftoc(v->y);
   434 	vec[2] = sp_ftoc(v->z);
   435 
   436 	return 1;
   437 }
   438 
   439 static cell_t GameRules_SetPropVector(IPluginContext *pContext, const cell_t *params)
   440 {
   441 	char *prop;
   442 	int element = params[3];
   443 	int offset;
   444 	int bit_count;
   445 
   446 	bool sendChange = true;
   447 	if (params[4] == 0)
   448 		sendChange = false;
   449 
   450 	CBaseEntity *pProxy = NULL;
   451 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
   452 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
   453 	
   454 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   455 		return pContext->ThrowNativeError("Gamerules lookup failed.");
   456 
   457 	pContext->LocalToString(params[1], &prop);
   458 
   459 	FIND_PROP_SEND(DPT_Vector, "vector");
   460 
   461 	void *pGameRules = *g_pGameRules;
   462 
   463 	Vector *v = (Vector *)((intptr_t)pGameRules + offset);
   464 
   465 	cell_t *vec;
   466 	pContext->LocalToPhysAddr(params[2], &vec);
   467 
   468 	v->x = sp_ctof(vec[0]);
   469 	v->y = sp_ctof(vec[1]);
   470 	v->z = sp_ctof(vec[2]);
   471 
   472 	if (sendChange)
   473 	{
   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]);
   478 
   479 		gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
   480 	}
   481 
   482 	return 1;
   483 }
   484 
   485 static cell_t GameRules_GetPropString(IPluginContext *pContext, const cell_t *params)
   486 {
   487 	char *prop;
   488 	int offset;
   489 
   490 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   491 		return pContext->ThrowNativeError("Gamerules lookup failed.");
   492 
   493 	pContext->LocalToString(params[1], &prop);
   494 
   495 	sm_sendprop_info_t info;
   496 	if (!gamehelpers->FindSendPropInfo(g_szGameRulesProxy, prop, &info))
   497 	{
   498 		return pContext->ThrowNativeError("Property \"%s\" not found on the gamerules proxy", prop);
   499 	}
   500 	
   501 	offset = info.actual_offset;
   502 	
   503 	if (info.prop->GetType() != DPT_String)
   504 	{
   505 		return pContext->ThrowNativeError("SendProp %s type is not a string (%d != %d)",
   506 				prop,
   507 				info.prop->GetType(),
   508 				DPT_String);
   509 	}
   510 
   511 	void *pGameRules = *g_pGameRules;
   512 
   513 	size_t len;
   514 	const char *src;
   515 
   516 	src = (char *)((intptr_t)pGameRules + offset);
   517 
   518 	pContext->StringToLocalUTF8(params[2], params[3], src, &len);
   519 
   520 	return len;
   521 }
   522 
   523 // From sm_stringutil
   524 inline int strncopy(char *dest, const char *src, size_t count)
   525 {
   526 	if (!count)
   527 	{
   528 		return 0;
   529 	}
   530 
   531 	char *start = dest;
   532 	while ((*src) && (--count))
   533 	{
   534 		*dest++ = *src++;
   535 	}
   536 	*dest = '\0';
   537 
   538 	return (dest - start);
   539 }
   540 //
   541 
   542 static cell_t GameRules_SetPropString(IPluginContext *pContext, const cell_t *params)
   543 {
   544 	char *prop;
   545 	int offset;
   546 	int maxlen;
   547 
   548 	bool sendChange = true;
   549 	if (params[3] == 0)
   550 		sendChange = false;
   551 
   552 	CBaseEntity *pProxy = NULL;
   553 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
   554 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
   555 	
   556 	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
   557 		return pContext->ThrowNativeError("Gamerules lookup failed.");
   558 
   559 	pContext->LocalToString(params[1], &prop);
   560 
   561 	sm_sendprop_info_t info;
   562 	if (!gamehelpers->FindSendPropInfo(g_szGameRulesProxy, prop, &info))
   563 	{
   564 		return pContext->ThrowNativeError("Property \"%s\" not found on the gamerules proxy", prop);
   565 	}
   566 	
   567 	offset = info.actual_offset;
   568 	
   569 	if (info.prop->GetType() != DPT_String)
   570 	{
   571 		return pContext->ThrowNativeError("SendProp %s type is not a string (%d != %d)",
   572 				prop,
   573 				info.prop->GetType(),
   574 				DPT_String);
   575 	}
   576 
   577 	void *pGameRules = *g_pGameRules;
   578 	maxlen = DT_MAX_STRING_BUFFERSIZE;
   579 
   580 	char *src;
   581 	char *dest = (char *)((intptr_t)pGameRules + offset);
   582 
   583 	pContext->LocalToString(params[2], &src);
   584 	size_t len = strncopy(dest, src, maxlen);
   585 
   586 	if (sendChange)
   587 	{
   588 		dest = (char *)((intptr_t)pProxy + offset);
   589 		strncopy(dest, src, maxlen);
   590 		gamehelpers->SetEdictStateChanged(gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pProxy)), offset);
   591 	}
   592 
   593 	return len;
   594 }
   595 
   596 sp_nativeinfo_t g_GameRulesNatives[] = 
   597 {
   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},
   608 	{NULL,							NULL},
   609 };
   610