core/smn_entities.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Tue Nov 30 13:09:30 2010 -0500 (2010-11-30)
changeset 3162 d8f90b56529f
parent 3099 1b7bf8509625
child 3220 5579fb7db416
permissions -rw-r--r--
Fixed BanClient always banning by IP address (bug 4626, r=fyren).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2010 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 "sm_globals.h"
    33 #include "sourcemod.h"
    34 #include "sourcemm_api.h"
    35 #include "PlayerManager.h"
    36 #include "HalfLife2.h"
    37 #include <IGameConfigs.h>
    38 #include "sm_stringutil.h"
    39 #include "logic_bridge.h"
    40 
    41 enum PropType
    42 {
    43 	Prop_Send = 0,
    44 	Prop_Data
    45 };
    46 
    47 enum PropFieldType
    48 {
    49 	PropField_Unsupported,		/**< The type is unsupported. */
    50 	PropField_Integer,			/**< Valid for SendProp and Data fields */
    51 	PropField_Float,			/**< Valid for SendProp and Data fields */
    52 	PropField_Entity,			/**< Valid for Data fields only (SendProp shows as int) */
    53 	PropField_Vector,			/**< Valid for SendProp and Data fields */
    54 	PropField_String,			/**< Valid for SendProp and Data fields */
    55 	PropField_String_T,			/**< Valid for Data fields.  Read only! */
    56 };
    57 
    58 inline edict_t *BaseEntityToEdict(CBaseEntity *pEntity)
    59 {
    60 	IServerUnknown *pUnk = (IServerUnknown *)pEntity;
    61 	IServerNetworkable *pNet = pUnk->GetNetworkable();
    62 
    63 	if (!pNet)
    64 	{
    65 		return NULL;
    66 	}
    67 
    68 	return pNet->GetEdict();
    69 }
    70 
    71 inline edict_t *GetEdict(cell_t num)
    72 {
    73 	edict_t *pEdict;
    74 	if (!IndexToAThings(num, NULL, &pEdict))
    75 	{
    76 		return NULL;
    77 	}
    78 	
    79 	return pEdict;
    80 }
    81 
    82 inline CBaseEntity *GetEntity(cell_t num)
    83 {
    84 	CBaseEntity *pEntity;
    85 	if (!IndexToAThings(num, &pEntity, NULL))
    86 	{
    87 		return NULL;
    88 	}
    89 
    90 	return pEntity;
    91 }
    92 
    93 /* Given an entity reference or index, fill out a CBaseEntity and/or edict.
    94    If lookup is successful, returns true and writes back the two parameters.
    95    If lookup fails, returns false and doesn't touch the params.  */
    96 bool IndexToAThings(cell_t num, CBaseEntity **pEntData, edict_t **pEdictData)
    97 {
    98 	CBaseEntity *pEntity = g_HL2.ReferenceToEntity(num);
    99 
   100 	if (!pEntity)
   101 	{
   102 		return false;
   103 	}
   104 
   105 	int index = g_HL2.ReferenceToIndex(num);
   106 	if (index > 0 && index <= g_Players.GetMaxClients())
   107 	{
   108 		CPlayer *pPlayer = g_Players.GetPlayerByIndex(index);
   109 		if (!pPlayer || !pPlayer->IsConnected())
   110 		{
   111 			return false;
   112 		}
   113 	}
   114 
   115 	if (pEntData)
   116 	{
   117 		*pEntData = pEntity;
   118 	}
   119 
   120 	if (pEdictData)
   121 	{
   122 		edict_t *pEdict = ::BaseEntityToEdict(pEntity);
   123 		if (!pEdict || pEdict->IsFree())
   124 		{
   125 			pEdict = NULL;
   126 		}
   127 
   128 		*pEdictData = pEdict;
   129 	}
   130 
   131 	return true;
   132 }
   133 /*
   134   <predcrab> AThings is like guaranteed r+ by dvander though
   135   <predcrab> he wont even read the rest of the patch to nit stuff
   136   <Fyren> Like I wasn't going to r? you anyway!
   137   <predcrab> he still sees!
   138   <predcrab> he's everywhere
   139 */
   140 
   141 class VEmptyClass {};
   142 datamap_t *VGetDataDescMap(CBaseEntity *pThisPtr, int offset)
   143 {
   144 	void **this_ptr = *reinterpret_cast<void ***>(&pThisPtr);
   145 	void **vtable = *reinterpret_cast<void ***>(pThisPtr);
   146 	void *vfunc = vtable[offset];
   147 
   148 	union
   149 	{
   150 		datamap_t *(VEmptyClass::*mfpnew)();
   151 #ifndef PLATFORM_POSIX
   152 		void *addr;
   153 	} u;
   154 	u.addr = vfunc;
   155 #else
   156 		struct  
   157 		{
   158 			void *addr;
   159 			intptr_t adjustor;
   160 		} s;
   161 	} u;
   162 	u.s.addr = vfunc;
   163 	u.s.adjustor = 0;
   164 #endif
   165 
   166 	return (datamap_t *)(reinterpret_cast<VEmptyClass *>(this_ptr)->*u.mfpnew)();
   167 }
   168 
   169 inline datamap_t *CBaseEntity_GetDataDescMap(CBaseEntity *pEntity)
   170 {
   171 	int offset;
   172 
   173 	if (!g_pGameConf->GetOffset("GetDataDescMap", &offset) || !offset)
   174 	{
   175 		return NULL;
   176 	}
   177 
   178 	return VGetDataDescMap(pEntity, offset);
   179 }
   180 
   181 /* :TODO: Move this! */
   182 datamap_t *CHalfLife2::GetDataMap(CBaseEntity *pEntity)
   183 {
   184 	return CBaseEntity_GetDataDescMap(pEntity);
   185 }
   186 
   187 static cell_t GetMaxEntities(IPluginContext *pContext, const cell_t *params)
   188 {
   189 	return gpGlobals->maxEntities;
   190 }
   191 
   192 static cell_t CreateEdict(IPluginContext *pContext, const cell_t *params)
   193 {
   194 	edict_t *pEdict = engine->CreateEdict();
   195 
   196 	if (!pEdict)
   197 	{
   198 		return 0;
   199 	}
   200 
   201 	return IndexOfEdict(pEdict);
   202 }
   203 
   204 static cell_t RemoveEdict(IPluginContext *pContext, const cell_t *params)
   205 {
   206 	edict_t *pEdict = GetEdict(params[1]);
   207 
   208 	if (!pEdict)
   209 	{
   210 		return pContext->ThrowNativeError("Edict %d (%d) is not a valid edict", g_HL2.ReferenceToIndex(params[1]), params[1]);
   211 	}
   212 
   213 	engine->RemoveEdict(pEdict);
   214 
   215 	return 1;
   216 }
   217 
   218 static cell_t IsValidEdict(IPluginContext *pContext, const cell_t *params)
   219 {
   220 	edict_t *pEdict = GetEdict(params[1]);
   221 
   222 	if (!pEdict)
   223 	{
   224 		return 0;
   225 	}
   226 
   227 	/* Shouldn't be necessary... not sure though */
   228 	return pEdict->IsFree() ? 0 : 1;
   229 }
   230 
   231 static cell_t IsValidEntity(IPluginContext *pContext, const cell_t *params)
   232 {
   233 	CBaseEntity *pEntity = g_HL2.ReferenceToEntity(params[1]);
   234 
   235 	return (pEntity != NULL) ? 1 : 0;
   236 }
   237 
   238 static cell_t IsEntNetworkable(IPluginContext *pContext, const cell_t *params)
   239 {
   240 	edict_t *pEdict = GetEdict(params[1]);
   241 
   242 	if (!pEdict)
   243 	{
   244 		return 0;
   245 	}
   246 
   247 	return (pEdict->GetNetworkable() != NULL) ? 1 : 0;
   248 }
   249 
   250 static cell_t GetEntityCount(IPluginContext *pContext, const cell_t *params)
   251 {
   252 	return engine->GetEntityCount();
   253 }
   254 
   255 static cell_t GetEdictFlags(IPluginContext *pContext, const cell_t *params)
   256 {
   257 	edict_t *pEdict = GetEdict(params[1]);
   258 
   259 	if (!pEdict)
   260 	{
   261 		return pContext->ThrowNativeError("Invalid edict (%d - %d)", g_HL2.ReferenceToIndex(params[1]), params[1]);
   262 	}
   263 
   264 	return pEdict->m_fStateFlags;
   265 }
   266 
   267 static cell_t SetEdictFlags(IPluginContext *pContext, const cell_t *params)
   268 {
   269 	edict_t *pEdict = GetEdict(params[1]);
   270 
   271 	if (!pEdict)
   272 	{
   273 		return pContext->ThrowNativeError("Invalid edict (%d - %d)", g_HL2.ReferenceToIndex(params[1]), params[1]);
   274 	}
   275 
   276 	pEdict->m_fStateFlags = params[2];
   277 
   278 	return 1;
   279 }
   280 
   281 static cell_t GetEdictClassname(IPluginContext *pContext, const cell_t *params)
   282 {
   283 	edict_t *pEdict = GetEdict(params[1]);
   284 
   285 	if (!pEdict)
   286 	{
   287 		return pContext->ThrowNativeError("Invalid edict (%d - %d)", g_HL2.ReferenceToIndex(params[1]), params[1]);
   288 	}
   289 
   290 	const char *cls = pEdict->GetClassName();
   291 
   292 	if (!cls || cls[0] == '\0')
   293 	{
   294 		return 0;
   295 	}
   296 
   297 	pContext->StringToLocal(params[2], params[3], cls);
   298 
   299 	return 1;
   300 }
   301 
   302 static cell_t GetEntityNetClass(IPluginContext *pContext, const cell_t *params)
   303 {
   304 	CBaseEntity *pEntity = g_HL2.ReferenceToEntity(params[1]);
   305 
   306 	IServerUnknown *pUnk = (IServerUnknown *)pEntity;
   307 
   308 	if (!pEntity)
   309 	{
   310 		return pContext->ThrowNativeError("Invalid entity (%d - %d)", g_HL2.ReferenceToIndex(params[1]), params[1]);
   311 	}
   312 
   313 	IServerNetworkable *pNet = pUnk->GetNetworkable();
   314 	if (!pNet)
   315 	{
   316 		return 0;
   317 	}
   318 
   319 	ServerClass *pClass = pNet->GetServerClass();
   320 
   321 	pContext->StringToLocal(params[2], params[3], pClass->GetName());
   322 
   323 	return 1;
   324 }
   325 
   326 static cell_t GetEntData(IPluginContext *pContext, const cell_t *params)
   327 {
   328 	CBaseEntity *pEntity = GetEntity(params[1]);
   329 
   330 	if (!pEntity)
   331 	{
   332 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   333 	}
   334 
   335 	int offset = params[2];
   336 	if (offset <= 0 || offset > 32768)
   337 	{
   338 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   339 	}
   340 
   341 	switch (params[3])
   342 	{
   343 	case 4:
   344 		return *(int *)((uint8_t *)pEntity + offset);
   345 	case 2:
   346 		return *(short *)((uint8_t *)pEntity + offset);
   347 	case 1:
   348 		return *((uint8_t *)pEntity + offset);
   349 	default:
   350 		return pContext->ThrowNativeError("Integer size %d is invalid", params[3]);
   351 	}
   352 }
   353 
   354 static cell_t SetEntData(IPluginContext *pContext, const cell_t *params)
   355 {
   356 	CBaseEntity *pEntity;
   357 	edict_t *pEdict;
   358 
   359 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
   360 	{
   361 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   362 	}
   363 
   364 	int offset = params[2];
   365 	if (offset <= 0 || offset > 32768)
   366 	{
   367 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   368 	}
   369 
   370 	if (params[5] && (pEdict != NULL))
   371 	{
   372 		g_HL2.SetEdictStateChanged(pEdict, offset);
   373 	}
   374 
   375 	switch (params[4])
   376 	{
   377 	case 4:
   378 		{
   379 			*(int *)((uint8_t *)pEntity + offset) = params[3];
   380 			break;
   381 		}
   382 	case 2:
   383 		{
   384 			*(short *)((uint8_t *)pEntity + offset) = params[3];
   385 			break;
   386 		}
   387 	case 1:
   388 		{
   389 			*((uint8_t *)pEntity + offset) = params[3];
   390 			break;
   391 		}
   392 	default:
   393 		return pContext->ThrowNativeError("Integer size %d is invalid", params[4]);
   394 	}
   395 
   396 	return 1;
   397 }
   398 
   399 static cell_t GetEntDataFloat(IPluginContext *pContext, const cell_t *params)
   400 {
   401 	CBaseEntity *pEntity = GetEntity(params[1]);
   402 
   403 	if (!pEntity)
   404 	{
   405 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   406 	}
   407 
   408 	int offset = params[2];
   409 	if (offset <= 0 || offset > 32768)
   410 	{
   411 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   412 	}
   413 
   414 	float f = *(float *)((uint8_t *)pEntity + offset);
   415 
   416 	return sp_ftoc(f);
   417 }
   418 
   419 static cell_t SetEntDataFloat(IPluginContext *pContext, const cell_t *params)
   420 {
   421 	CBaseEntity *pEntity;
   422 	edict_t *pEdict;
   423 
   424 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
   425 	{
   426 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   427 	}
   428 
   429 	int offset = params[2];
   430 	if (offset <= 0 || offset > 32768)
   431 	{
   432 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   433 	}
   434 
   435 	*(float *)((uint8_t *)pEntity + offset) = sp_ctof(params[3]);
   436 
   437 	if (params[4] && (pEdict != NULL))
   438 	{
   439 		g_HL2.SetEdictStateChanged(pEdict, offset);
   440 	}
   441 
   442 	return 1;
   443 }
   444 
   445 static cell_t GetEntDataVector(IPluginContext *pContext, const cell_t *params)
   446 {
   447 	CBaseEntity *pEntity = GetEntity(params[1]);
   448 
   449 	if (!pEntity)
   450 	{
   451 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   452 	}
   453 
   454 	int offset = params[2];
   455 	if (offset <= 0 || offset > 32768)
   456 	{
   457 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   458 	}
   459 
   460 	Vector *v = (Vector *)((uint8_t *)pEntity + offset);
   461 
   462 	cell_t *vec;
   463 	pContext->LocalToPhysAddr(params[3], &vec);
   464 
   465 	vec[0] = sp_ftoc(v->x);
   466 	vec[1] = sp_ftoc(v->y);
   467 	vec[2] = sp_ftoc(v->z);
   468 
   469 	return 1;
   470 }
   471 
   472 static cell_t SetEntDataVector(IPluginContext *pContext, const cell_t *params)
   473 {
   474 	CBaseEntity *pEntity;
   475 	edict_t *pEdict;
   476 
   477 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
   478 	{
   479 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   480 	}
   481 
   482 	int offset = params[2];
   483 	if (offset <= 0 || offset > 32768)
   484 	{
   485 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   486 	}
   487 
   488 	Vector *v = (Vector *)((uint8_t *)pEntity + offset);
   489 
   490 	cell_t *vec;
   491 	pContext->LocalToPhysAddr(params[3], &vec);
   492 
   493 	v->x = sp_ctof(vec[0]);
   494 	v->y = sp_ctof(vec[1]);
   495 	v->z = sp_ctof(vec[2]);
   496 
   497 	if (params[4] && (pEdict != NULL))
   498 	{
   499 		g_HL2.SetEdictStateChanged(pEdict, offset);
   500 	}
   501 
   502 	return 1;
   503 }
   504 
   505 /* THIS GUY IS DEPRECATED. */
   506 static cell_t GetEntDataEnt(IPluginContext *pContext, const cell_t *params)
   507 {
   508 	CBaseEntity *pEntity = GetEntity(params[1]);
   509 
   510 	if (!pEntity)
   511 	{
   512 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   513 	}
   514 
   515 	int offset = params[2];
   516 	if (offset <= 0 || offset > 32768)
   517 	{
   518 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   519 	}
   520 
   521 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
   522 
   523 	if (!hndl.IsValid())
   524 	{
   525 		return 0;
   526 	}
   527 
   528 	int ref = g_HL2.IndexToReference(hndl.GetEntryIndex());
   529 	return g_HL2.ReferenceToBCompatRef(ref);
   530 }
   531 
   532 int CheckBaseHandle(CBaseHandle &hndl)
   533 {
   534 	if (!hndl.IsValid())
   535 	{
   536 		return -1;
   537 	}
   538 
   539 	int index = hndl.GetEntryIndex();
   540 
   541 	edict_t *pStoredEdict;
   542 	CBaseEntity *pStoredEntity;
   543 
   544 	if (!IndexToAThings(index, &pStoredEntity, &pStoredEdict))
   545 	{
   546 		return -1;
   547 	}
   548 
   549 	if (pStoredEdict == NULL || pStoredEntity == NULL)
   550 	{
   551 		return -1;
   552 	}
   553 
   554 	IServerEntity *pSE = pStoredEdict->GetIServerEntity();
   555 
   556 	if (pSE == NULL)
   557 	{
   558 		return -1;
   559 	}
   560 
   561 	if (pSE->GetRefEHandle() != hndl)
   562 	{
   563 		return -1;
   564 	}
   565 
   566 	return index;
   567 }
   568 
   569 static cell_t GetEntDataEnt2(IPluginContext *pContext, const cell_t *params)
   570 {
   571 	CBaseEntity *pEntity = GetEntity(params[1]);
   572 
   573 	if (pEntity == NULL)
   574 	{
   575 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   576 	}
   577 
   578 	int offset = params[2];
   579 	if (offset <= 0 || offset > 32768)
   580 	{
   581 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   582 	}
   583 
   584 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
   585 
   586 	int ref = g_HL2.IndexToReference(hndl.GetEntryIndex());
   587 	return g_HL2.ReferenceToBCompatRef(ref);
   588 }
   589 
   590 /* THIS GUY IS DEPRECATED. */
   591 static cell_t SetEntDataEnt(IPluginContext *pContext, const cell_t *params)
   592 {
   593 	CBaseEntity *pEntity;
   594 	edict_t *pEdict;
   595 
   596 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
   597 	{
   598 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   599 	}
   600 
   601 	int offset = params[2];
   602 	if (offset <= 0 || offset > 32768)
   603 	{
   604 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   605 	}
   606 
   607 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
   608 
   609 	if (params[3] == 0 || (unsigned)params[3] == INVALID_EHANDLE_INDEX)
   610 	{
   611 		hndl.Set(NULL);
   612 	} else {
   613 		CBaseEntity *pOther = GetEntity(params[3]);
   614 
   615 		if (!pOther)
   616 		{
   617 			return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[3]), params[3]);
   618 		}
   619 
   620 		IHandleEntity *pHandleEnt = (IHandleEntity *)pOther;
   621 		hndl.Set(pHandleEnt);
   622 	}
   623 
   624 	if (params[4] && (pEdict != NULL))
   625 	{
   626 		g_HL2.SetEdictStateChanged(pEdict, offset);
   627 	}
   628 
   629 	return 1;
   630 }
   631 
   632 static cell_t SetEntDataEnt2(IPluginContext *pContext, const cell_t *params)
   633 {
   634 	CBaseEntity *pEntity;
   635 	edict_t *pEdict;
   636 
   637 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
   638 	{
   639 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   640 	}
   641 
   642 	int offset = params[2];
   643 	if (offset <= 0 || offset > 32768)
   644 	{
   645 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   646 	}
   647 
   648 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
   649 
   650 	if ((unsigned)params[3] == INVALID_EHANDLE_INDEX)
   651 	{
   652 		hndl.Set(NULL);
   653 	}
   654 	else
   655 	{
   656 		CBaseEntity *pOther = GetEntity(params[3]);
   657 
   658 		if (!pOther)
   659 		{
   660 			return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[3]), params[3]);
   661 		}
   662 
   663 		IHandleEntity *pHandleEnt = (IHandleEntity *)pOther;
   664 		hndl.Set(pHandleEnt);
   665 	}
   666 
   667 	if (params[4] && (pEdict != NULL))
   668 	{
   669 		g_HL2.SetEdictStateChanged(pEdict, offset);
   670 	}
   671 
   672 	return 1;
   673 }
   674 
   675 static cell_t ChangeEdictState(IPluginContext *pContext, const cell_t *params)
   676 {
   677 	edict_t *pEdict = GetEdict(params[1]);
   678 
   679 	if (!pEdict)
   680 	{
   681 		return pContext->ThrowNativeError("Edict %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   682 	}
   683 
   684 	g_HL2.SetEdictStateChanged(pEdict, params[2]);
   685 
   686 	return 1;
   687 }
   688 
   689 static cell_t FindSendPropOffs(IPluginContext *pContext, const cell_t *params)
   690 {
   691 	char *cls, *prop;
   692 	pContext->LocalToString(params[1], &cls);
   693 	pContext->LocalToString(params[2], &prop);
   694 
   695 	SendProp *pSend = g_HL2.FindInSendTable(cls, prop);
   696 
   697 	if (!pSend)
   698 	{
   699 		return -1;
   700 	}
   701 
   702 	return pSend->GetOffset();
   703 }
   704 
   705 static cell_t FindSendPropInfo(IPluginContext *pContext, const cell_t *params)
   706 {
   707 	char *cls, *prop;
   708 	sm_sendprop_info_t info;
   709 	cell_t *pType, *pBits, *pLocal;
   710 
   711 	pContext->LocalToString(params[1], &cls);
   712 	pContext->LocalToString(params[2], &prop);
   713 
   714 	if (!g_HL2.FindSendPropInfo(cls, prop, &info))
   715 	{
   716 		return -1;
   717 	}
   718 
   719 	pContext->LocalToPhysAddr(params[3], &pType);
   720 	pContext->LocalToPhysAddr(params[4], &pBits);
   721 	pContext->LocalToPhysAddr(params[5], &pLocal);
   722 
   723 	switch (info.prop->GetType())
   724 	{
   725 	case DPT_Int:
   726 		{
   727 			*pType = PropField_Integer;
   728 			break;
   729 		}
   730 	case DPT_Float:
   731 		{
   732 			*pType = PropField_Float;
   733 			break;
   734 		}
   735 	case DPT_String:
   736 		{
   737 			*pType = PropField_String;
   738 			break;
   739 		}
   740 	case DPT_Vector:
   741 		{
   742 			*pType = PropField_Vector;
   743 			break;
   744 		}
   745 	default:
   746 		{
   747 			*pType = PropField_Unsupported;
   748 			break;
   749 		}
   750 	}
   751 
   752 	*pBits = info.prop->m_nBits;
   753 	*pLocal = info.prop->GetOffset();
   754 
   755 	return info.actual_offset;
   756 }
   757 
   758 static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params)
   759 {
   760 	CBaseEntity *pEntity;
   761 	datamap_t *pMap;
   762 	typedescription_t *td;
   763 	char *offset;
   764 	pEntity = GetEntity(params[1]);
   765 
   766 	if (!pEntity)
   767 	{
   768 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   769 	}
   770 
   771 	if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   772 	{
   773 		return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
   774 	}
   775 
   776 	pContext->LocalToString(params[2], &offset);
   777 	if ((td=g_HL2.FindInDataMap(pMap, offset)) == NULL)
   778 	{
   779 		return -1;
   780 	}
   781 
   782 	if (params[0] == 4)
   783 	{
   784 		cell_t *pType, *pSize;
   785 
   786 		pContext->LocalToPhysAddr(params[3], &pType);
   787 		pContext->LocalToPhysAddr(params[4], &pSize);
   788 
   789 		switch (td->fieldType)
   790 		{
   791 		case FIELD_TICK:
   792 		case FIELD_MODELINDEX:
   793 		case FIELD_MATERIALINDEX:
   794 		case FIELD_INTEGER:
   795 		case FIELD_COLOR32:
   796 			{
   797 				*pType = PropField_Integer;
   798 				*pSize = 32;
   799 				break;
   800 			}
   801 		case FIELD_VECTOR:
   802 		case FIELD_POSITION_VECTOR:
   803 			{
   804 				*pType = PropField_Vector;
   805 				*pSize = 12;
   806 				break;
   807 			}
   808 		case FIELD_SHORT:
   809 			{
   810 				*pType = PropField_Integer;
   811 				*pSize = 16;
   812 				break;
   813 			}
   814 		case FIELD_BOOLEAN:
   815 			{
   816 				*pType = PropField_Integer;
   817 				*pSize = 1;
   818 				break;
   819 			}
   820 		case FIELD_CHARACTER:
   821 			{
   822 				if (td->fieldSize == 1)
   823 				{
   824 					*pType = PropField_Integer;
   825 					*pSize = 8;
   826 				}
   827 				else
   828 				{
   829 					*pType = PropField_String;
   830 					*pSize = 8 * td->fieldSize;
   831 				}
   832 				break;
   833 			}
   834 		case FIELD_MODELNAME:
   835 		case FIELD_SOUNDNAME:
   836 		case FIELD_STRING:
   837 			{
   838 				*pSize = sizeof(string_t);
   839 				*pType = PropField_String_T;
   840 				break;
   841 			}
   842 		case FIELD_FLOAT:
   843 		case FIELD_TIME:
   844 			{
   845 				*pType = PropField_Float;
   846 				*pSize = 32;
   847 				break;
   848 			}
   849 		case FIELD_EHANDLE:
   850 			{
   851 				*pType = PropField_Entity;
   852 				*pSize = 32;
   853 				break;
   854 			}
   855 		default:
   856 			{
   857 				*pType = PropField_Unsupported;
   858 				*pSize = 0;
   859 			}
   860 		}
   861 	}
   862 
   863 	return GetTypeDescOffs(td);
   864 }
   865 
   866 static cell_t GetEntDataString(IPluginContext *pContext, const cell_t *params)
   867 {
   868 	CBaseEntity *pEntity = GetEntity(params[1]);
   869 
   870 	if (!pEntity)
   871 	{
   872 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   873 	}
   874 
   875 	int offset = params[2];
   876 	if (offset < 0 || offset > 32768)
   877 	{
   878 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   879 	}
   880 
   881 	size_t len;
   882 	char *src = (char *)((uint8_t *)pEntity + offset);
   883 	pContext->StringToLocalUTF8(params[3], params[4], src, &len);
   884 
   885 	return len;
   886 }
   887 
   888 static cell_t SetEntDataString(IPluginContext *pContext, const cell_t *params)
   889 {
   890 	CBaseEntity *pEntity;
   891 	edict_t *pEdict;
   892 
   893 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
   894 	{
   895 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   896 	}
   897 
   898 	int offset = params[2];
   899 	if (offset < 0 || offset > 32768)
   900 	{
   901 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   902 	}
   903 
   904 	char *src;
   905 	char *dest = (char *)((uint8_t *)pEntity + offset);
   906 
   907 	pContext->LocalToString(params[3], &src);
   908 	size_t len = strncopy(dest, src, params[4]);
   909 
   910 	if (params[5] && (pEdict != NULL))
   911 	{
   912 		g_HL2.SetEdictStateChanged(pEdict, offset);
   913 	}
   914 
   915 	return len;
   916 }
   917 
   918 #define FIND_PROP_DATA(td) \
   919 	datamap_t *pMap; \
   920 	if ((pMap = CBaseEntity_GetDataDescMap(pEntity)) == NULL) \
   921 	{ \
   922 		return pContext->ThrowNativeError("Could not retrieve datamap"); \
   923 	} \
   924 	if ((td = g_HL2.FindInDataMap(pMap, prop)) == NULL) \
   925 	{ \
   926 		return pContext->ThrowNativeError("Property \"%s\" not found (entity %d/%s)", \
   927 			prop, \
   928 			params[1], \
   929 			class_name); \
   930 	}
   931 
   932 #define FIND_PROP_SEND(pProp) \
   933 	IServerUnknown *pUnk = (IServerUnknown *)pEntity; \
   934 	IServerNetworkable *pNet = pUnk->GetNetworkable(); \
   935 	if (!pNet) \
   936 	{ \
   937 		return pContext->ThrowNativeError("Edict %d (%d) is not networkable", g_HL2.ReferenceToIndex(params[1]), params[1]); \
   938 	} \
   939 	if (!g_HL2.FindSendPropInfo(pNet->GetServerClass()->GetName(), prop, pProp)) \
   940 	{ \
   941 		return pContext->ThrowNativeError("Property \"%s\" not found (entity %d/%s)", \
   942 			prop, \
   943 			params[1], \
   944 			class_name); \
   945 	}
   946 
   947 inline int MatchFieldAsInteger(int field_type)
   948 {
   949 	switch (field_type)
   950 	{
   951 	case FIELD_TICK:
   952 	case FIELD_MODELINDEX:
   953 	case FIELD_MATERIALINDEX:
   954 	case FIELD_INTEGER:
   955 	case FIELD_COLOR32:
   956 		return 32;
   957 	case FIELD_SHORT:
   958 		return 16;
   959 	case FIELD_CHARACTER:
   960 		return 8;
   961 	case FIELD_BOOLEAN:
   962 		return 1;
   963 	default:
   964 		return 0;
   965 	}
   966 
   967 	return 0;
   968 }
   969 
   970 static cell_t GetEntProp(IPluginContext *pContext, const cell_t *params)
   971 {
   972 	CBaseEntity *pEntity;
   973 	char *prop;
   974 	int offset;
   975 	const char *class_name;
   976 	edict_t *pEdict;
   977 	int bit_count;
   978 
   979 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
   980 	{
   981 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   982 	}
   983 
   984 	/* TODO: Find a way to lookup classname without having an edict - Is this a guaranteed prop? */
   985 	if (!pEdict || (class_name = pEdict->GetClassName()) == NULL)
   986 	{
   987 		class_name = "";
   988 	}
   989 
   990 	pContext->LocalToString(params[3], &prop);
   991 
   992 	switch (params[2])
   993 	{
   994 	case Prop_Data:
   995 		{
   996 			typedescription_t *td;
   997 
   998 			FIND_PROP_DATA(td);
   999 
  1000 			if ((bit_count = MatchFieldAsInteger(td->fieldType)) == 0)
  1001 			{
  1002 				return pContext->ThrowNativeError("Data field %s is not an integer (%d)", 
  1003 					prop,
  1004 					td->fieldType);
  1005 			}
  1006 
  1007 			offset = GetTypeDescOffs(td);
  1008 			break;
  1009 		}
  1010 	case Prop_Send:
  1011 		{
  1012 			sm_sendprop_info_t info;
  1013 
  1014 			FIND_PROP_SEND(&info);
  1015 
  1016 			if (info.prop->GetType() != DPT_Int)
  1017 			{
  1018 				return pContext->ThrowNativeError("SendProp %s is not an integer ([%d,%d] != %d)",
  1019 					prop,
  1020 					info.prop->GetType(),
  1021 					info.prop->m_nBits,
  1022 					DPT_Int);
  1023 			}
  1024 
  1025 			bit_count = info.prop->m_nBits;
  1026 			offset = info.actual_offset;
  1027 			break;
  1028 		}
  1029 	default:
  1030 		{
  1031 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1032 		}
  1033 	}
  1034 
  1035 	if (bit_count < 1)
  1036 	{
  1037 		bit_count = params[4] * 8;
  1038 	}
  1039 
  1040 	if (bit_count >= 17)
  1041 	{
  1042 		return *(int32_t *)((uint8_t *)pEntity + offset);
  1043 	}
  1044 	else if (bit_count >= 9)
  1045 	{
  1046 		return *(int16_t *)((uint8_t *)pEntity + offset);
  1047 	}
  1048 	else if (bit_count >= 2)
  1049 	{
  1050 		return *(int8_t *)((uint8_t *)pEntity + offset);
  1051 	}
  1052 	else
  1053 	{
  1054 		return *(bool *)((uint8_t *)pEntity + offset) ? 1 : 0;
  1055 	}
  1056 
  1057 	return 0;
  1058 }
  1059 
  1060 static cell_t SetEntProp(IPluginContext *pContext, const cell_t *params)
  1061 {
  1062 	CBaseEntity *pEntity;
  1063 	char *prop;
  1064 	int offset;
  1065 	const char *class_name;
  1066 	edict_t *pEdict;
  1067 	int bit_count;
  1068 
  1069 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
  1070 	{
  1071 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
  1072 	}
  1073 
  1074 	if (!pEdict || (class_name = pEdict->GetClassName()) == NULL)
  1075 	{
  1076 		class_name = "";
  1077 	}
  1078 
  1079 	pContext->LocalToString(params[3], &prop);
  1080 
  1081 	switch (params[2])
  1082 	{
  1083 	case Prop_Data:
  1084 		{
  1085 			typedescription_t *td;
  1086 
  1087 			FIND_PROP_DATA(td);
  1088 
  1089 			if ((bit_count = MatchFieldAsInteger(td->fieldType)) == 0)
  1090 			{
  1091 				return pContext->ThrowNativeError("Data field %s is not an integer (%d)", 
  1092 					prop,
  1093 					td->fieldType);
  1094 			}
  1095 
  1096 			offset = GetTypeDescOffs(td);
  1097 			break;
  1098 		}
  1099 	case Prop_Send:
  1100 		{
  1101 			sm_sendprop_info_t info;
  1102 
  1103 			FIND_PROP_SEND(&info);
  1104 
  1105 			if (info.prop->GetType() != DPT_Int)
  1106 			{
  1107 				return pContext->ThrowNativeError("SendProp %s is not an integer ([%d,%d] != %d)",
  1108 					prop,
  1109 					info.prop->GetType(),
  1110 					info.prop->m_nBits,
  1111 					DPT_Int);
  1112 			}
  1113 
  1114 			bit_count = info.prop->m_nBits;
  1115 			offset = info.actual_offset;
  1116 			break;
  1117 		}
  1118 	default:
  1119 		{
  1120 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1121 		}
  1122 	}
  1123 
  1124 	if (bit_count < 1)
  1125 	{
  1126 		bit_count = params[5] * 8;
  1127 	}
  1128 
  1129 	if (bit_count >= 17)
  1130 	{
  1131 		*(int32_t *)((uint8_t *)pEntity + offset) = params[4];
  1132 	}
  1133 	else if (bit_count >= 9)
  1134 	{
  1135 		*(int16_t *)((uint8_t *)pEntity + offset) = (int16_t)params[4];
  1136 	}
  1137 	else if (bit_count >= 2)
  1138 	{
  1139 		*(int8_t *)((uint8_t *)pEntity + offset) = (int8_t)params[4];
  1140 	}
  1141 	else
  1142 	{
  1143 		*(bool *)((uint8_t *)pEntity + offset) = params[4] ? true : false;
  1144 	}
  1145 
  1146 	return 0;
  1147 }
  1148 
  1149 static cell_t GetEntPropFloat(IPluginContext *pContext, const cell_t *params)
  1150 {
  1151 	CBaseEntity *pEntity;
  1152 	char *prop;
  1153 	int offset;
  1154 	const char *class_name;
  1155 	edict_t *pEdict;
  1156 
  1157 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
  1158 	{
  1159 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
  1160 	}
  1161 
  1162 	if (!pEdict || (class_name = pEdict->GetClassName()) == NULL)
  1163 	{
  1164 		class_name = "";
  1165 	}
  1166 
  1167 	pContext->LocalToString(params[3], &prop);
  1168 
  1169 	switch (params[2])
  1170 	{
  1171 	case Prop_Data:
  1172 		{
  1173 			typedescription_t *td;
  1174 
  1175 			FIND_PROP_DATA(td);
  1176 
  1177 			if (td->fieldType != FIELD_FLOAT
  1178 				&& td->fieldType != FIELD_TIME)
  1179 			{
  1180 				return pContext->ThrowNativeError("Data field %s is not a float (%d != [%d,%d])", 
  1181 					prop,
  1182 					td->fieldType,
  1183 					FIELD_FLOAT,
  1184 					FIELD_TIME);
  1185 			}
  1186 
  1187 			offset = GetTypeDescOffs(td);
  1188 			break;
  1189 		}
  1190 	case Prop_Send:
  1191 		{
  1192 			sm_sendprop_info_t info;
  1193 
  1194 			FIND_PROP_SEND(&info);
  1195 
  1196 			if (info.prop->GetType() != DPT_Float)
  1197 			{
  1198 				return pContext->ThrowNativeError("SendProp %s is not a float (%d != %d)",
  1199 					prop,
  1200 					info.prop->GetType(),
  1201 					DPT_Float);
  1202 			}
  1203 
  1204 			offset = info.actual_offset;
  1205 			break;
  1206 		}
  1207 	default:
  1208 		{
  1209 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1210 		}
  1211 	}
  1212 
  1213 	float val = *(float *)((uint8_t *)pEntity + offset);
  1214 
  1215 	return sp_ftoc(val);
  1216 }
  1217 
  1218 static cell_t SetEntPropFloat(IPluginContext *pContext, const cell_t *params)
  1219 {
  1220 	CBaseEntity *pEntity;
  1221 	char *prop;
  1222 	int offset;
  1223 	const char *class_name;
  1224 	edict_t *pEdict;
  1225 
  1226 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
  1227 	{
  1228 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
  1229 	}
  1230 
  1231 	if (!pEdict || (class_name = pEdict->GetClassName()) == NULL)
  1232 	{
  1233 		class_name = "";
  1234 	}
  1235 
  1236 	pContext->LocalToString(params[3], &prop);
  1237 
  1238 	switch (params[2])
  1239 	{
  1240 	case Prop_Data:
  1241 		{
  1242 			typedescription_t *td;
  1243 
  1244 			FIND_PROP_DATA(td);
  1245 
  1246 			if (td->fieldType != FIELD_FLOAT
  1247 				&& td->fieldType != FIELD_TIME)
  1248 			{
  1249 				return pContext->ThrowNativeError("Data field %s is not a float (%d != [%d,%d])", 
  1250 					prop,
  1251 					td->fieldType,
  1252 					FIELD_FLOAT,
  1253 					FIELD_TIME);
  1254 			}
  1255 
  1256 			offset = GetTypeDescOffs(td);
  1257 			break;
  1258 		}
  1259 	case Prop_Send:
  1260 		{
  1261 			sm_sendprop_info_t info;
  1262 
  1263 			FIND_PROP_SEND(&info);
  1264 
  1265 			if (info.prop->GetType() != DPT_Float)
  1266 			{
  1267 				return pContext->ThrowNativeError("SendProp %s is not a float (%d != %d)",
  1268 					prop,
  1269 					info.prop->GetType(),
  1270 					DPT_Float);
  1271 			}
  1272 
  1273 			offset = info.actual_offset;
  1274 			break;
  1275 		}
  1276 	default:
  1277 		{
  1278 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1279 		}
  1280 	}
  1281 
  1282 	*(float *)((uint8_t *)pEntity + offset) = sp_ctof(params[4]);
  1283 
  1284 	if (params[2] == Prop_Send && (pEdict != NULL))
  1285 	{
  1286 		g_HL2.SetEdictStateChanged(pEdict, offset);
  1287 	}
  1288 
  1289 	return 1;
  1290 }
  1291 
  1292 static cell_t GetEntPropEnt(IPluginContext *pContext, const cell_t *params)
  1293 {
  1294 	CBaseEntity *pEntity;
  1295 	char *prop;
  1296 	int offset;
  1297 	const char *class_name;
  1298 	edict_t *pEdict;
  1299 
  1300 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
  1301 	{
  1302 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
  1303 	}
  1304 
  1305 	if (!pEdict || (class_name = pEdict->GetClassName()) == NULL)
  1306 	{
  1307 		class_name = "";
  1308 	}
  1309 
  1310 	pContext->LocalToString(params[3], &prop);
  1311 
  1312 	switch (params[2])
  1313 	{
  1314 	case Prop_Data:
  1315 		{
  1316 			typedescription_t *td;
  1317 
  1318 			FIND_PROP_DATA(td);
  1319 
  1320 			if (td->fieldType != FIELD_EHANDLE)
  1321 			{
  1322 				return pContext->ThrowNativeError("Data field %s is not an entity (%d != %d)", 
  1323 					prop,
  1324 					td->fieldType,
  1325 					FIELD_EHANDLE);
  1326 			}
  1327 
  1328 			offset = GetTypeDescOffs(td);
  1329 			break;
  1330 		}
  1331 	case Prop_Send:
  1332 		{
  1333 			sm_sendprop_info_t info;
  1334 
  1335 			FIND_PROP_SEND(&info);
  1336 
  1337 			if (info.prop->GetType() != DPT_Int)
  1338 			{
  1339 				return pContext->ThrowNativeError("SendProp %s is not an integer (%d != %d)",
  1340 					prop,
  1341 					info.prop->GetType(),
  1342 					DPT_Int);
  1343 			}
  1344 
  1345 			offset = info.actual_offset;
  1346 			break;
  1347 		}
  1348 	default:
  1349 		{
  1350 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1351 		}
  1352 	}
  1353 
  1354 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
  1355 
  1356 	int ref = g_HL2.IndexToReference(hndl.GetEntryIndex());
  1357 	return g_HL2.ReferenceToBCompatRef(ref);
  1358 }
  1359 
  1360 static cell_t SetEntPropEnt(IPluginContext *pContext, const cell_t *params)
  1361 {
  1362 	CBaseEntity *pEntity;
  1363 	char *prop;
  1364 	int offset;
  1365 	const char *class_name;
  1366 	edict_t *pEdict;
  1367 
  1368 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
  1369 	{
  1370 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
  1371 	}
  1372 
  1373 	if (!pEdict || (class_name = pEdict->GetClassName()) == NULL)
  1374 	{
  1375 		class_name = "";
  1376 	}
  1377 
  1378 	pContext->LocalToString(params[3], &prop);
  1379 
  1380 	switch (params[2])
  1381 	{
  1382 	case Prop_Data:
  1383 		{
  1384 			typedescription_t *td;
  1385 
  1386 			FIND_PROP_DATA(td);
  1387 
  1388 			if (td->fieldType != FIELD_EHANDLE)
  1389 			{
  1390 				return pContext->ThrowNativeError("Data field %s is not an entity (%d != %d)", 
  1391 					prop,
  1392 					td->fieldType,
  1393 					FIELD_EHANDLE);
  1394 			}
  1395 
  1396 			offset = GetTypeDescOffs(td);
  1397 			break;
  1398 		}
  1399 	case Prop_Send:
  1400 		{
  1401 			sm_sendprop_info_t info;
  1402 
  1403 			FIND_PROP_SEND(&info);
  1404 
  1405 			if (info.prop->GetType() != DPT_Int)
  1406 			{
  1407 				return pContext->ThrowNativeError("SendProp %s is not an integer (%d != %d)",
  1408 					prop,
  1409 					info.prop->GetType(),
  1410 					DPT_Int);
  1411 			}
  1412 
  1413 			offset = info.actual_offset;
  1414 			break;
  1415 		}
  1416 	default:
  1417 		{
  1418 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1419 		}
  1420 	}
  1421 
  1422 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
  1423 
  1424 	if (params[4] == -1)
  1425 	{
  1426 		hndl.Set(NULL);
  1427 	}
  1428 	else
  1429 	{
  1430 		CBaseEntity *pOther = GetEntity(params[4]);
  1431 
  1432 		if (!pOther)
  1433 		{
  1434 			return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[4]), params[4]);
  1435 		}
  1436 
  1437 		IHandleEntity *pHandleEnt = (IHandleEntity *)pOther;
  1438 		hndl.Set(pHandleEnt);
  1439 	}
  1440 
  1441 	if (params[2] == Prop_Send && (pEdict != NULL))
  1442 	{
  1443 		g_HL2.SetEdictStateChanged(pEdict, offset);
  1444 	}
  1445 
  1446 	return 1;
  1447 }
  1448 
  1449 static cell_t GetEntPropVector(IPluginContext *pContext, const cell_t *params)
  1450 {
  1451 	CBaseEntity *pEntity;
  1452 	char *prop;
  1453 	int offset;
  1454 	const char *class_name;
  1455 	edict_t *pEdict;
  1456 	
  1457 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
  1458 	{
  1459 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
  1460 	}
  1461 
  1462 	if (!pEdict || (class_name = pEdict->GetClassName()) == NULL)
  1463 	{
  1464 		class_name = "";
  1465 	}
  1466 
  1467 	pContext->LocalToString(params[3], &prop);
  1468 
  1469 	switch (params[2])
  1470 	{
  1471 	case Prop_Data:
  1472 		{
  1473 			typedescription_t *td;
  1474 			
  1475 			FIND_PROP_DATA(td);
  1476 
  1477 			if (td->fieldType != FIELD_VECTOR
  1478 				&& td->fieldType != FIELD_POSITION_VECTOR)
  1479 			{
  1480 				return pContext->ThrowNativeError("Data field %s is not a vector (%d != [%d,%d])", 
  1481 					prop,
  1482 					td->fieldType,
  1483 					FIELD_VECTOR,
  1484 					FIELD_POSITION_VECTOR);
  1485 			}
  1486 
  1487 			offset = GetTypeDescOffs(td);
  1488 			break;
  1489 		}
  1490 	case Prop_Send:
  1491 		{
  1492 			sm_sendprop_info_t info;
  1493 
  1494 			FIND_PROP_SEND(&info);
  1495 
  1496 			if (info.prop->GetType() != DPT_Vector)
  1497 			{
  1498 				return pContext->ThrowNativeError("SendProp %s is not a vector (%d != %d)",
  1499 					prop,
  1500 					info.prop->GetType(),
  1501 					DPT_Vector);
  1502 			}
  1503 
  1504 			offset = info.actual_offset;
  1505 			break;
  1506 		}
  1507 	default:
  1508 		{
  1509 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1510 		}
  1511 	}
  1512 
  1513 	Vector *v = (Vector *)((uint8_t *)pEntity + offset);
  1514 
  1515 	cell_t *vec;
  1516 	pContext->LocalToPhysAddr(params[4], &vec);
  1517 
  1518 	vec[0] = sp_ftoc(v->x);
  1519 	vec[1] = sp_ftoc(v->y);
  1520 	vec[2] = sp_ftoc(v->z);
  1521 
  1522 	return 1;
  1523 }
  1524 
  1525 static cell_t SetEntPropVector(IPluginContext *pContext, const cell_t *params)
  1526 {
  1527 	CBaseEntity *pEntity;
  1528 	char *prop;
  1529 	int offset;
  1530 	const char *class_name;
  1531 	edict_t *pEdict;
  1532 	
  1533 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
  1534 	{
  1535 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
  1536 	}
  1537 
  1538 	if (!pEdict || (class_name = pEdict->GetClassName()) == NULL)
  1539 	{
  1540 		class_name = "";
  1541 	}
  1542 
  1543 	pContext->LocalToString(params[3], &prop);
  1544 
  1545 	switch (params[2])
  1546 	{
  1547 	case Prop_Data:
  1548 		{
  1549 			typedescription_t *td;
  1550 			
  1551 			FIND_PROP_DATA(td);
  1552 
  1553 			if (td->fieldType != FIELD_VECTOR
  1554 				&& td->fieldType != FIELD_POSITION_VECTOR)
  1555 			{
  1556 				return pContext->ThrowNativeError("Data field %s is not a vector (%d != [%d,%d])", 
  1557 					prop,
  1558 					td->fieldType,
  1559 					FIELD_VECTOR,
  1560 					FIELD_POSITION_VECTOR);
  1561 			}
  1562 
  1563 			offset = GetTypeDescOffs(td);
  1564 			break;
  1565 		}
  1566 	case Prop_Send:
  1567 		{
  1568 			sm_sendprop_info_t info;
  1569 
  1570 			FIND_PROP_SEND(&info);
  1571 
  1572 			if (info.prop->GetType() != DPT_Vector)
  1573 			{
  1574 				return pContext->ThrowNativeError("SendProp %s is not a vector (%d != %d)",
  1575 					prop,
  1576 					info.prop->GetType(),
  1577 					DPT_Vector);
  1578 			}
  1579 
  1580 			offset = info.actual_offset;
  1581 			break;
  1582 		}
  1583 	default:
  1584 		{
  1585 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1586 		}
  1587 	}
  1588 
  1589 	Vector *v = (Vector *)((uint8_t *)pEntity + offset);
  1590 
  1591 	cell_t *vec;
  1592 	pContext->LocalToPhysAddr(params[4], &vec);
  1593 
  1594 	v->x = sp_ctof(vec[0]);
  1595 	v->y = sp_ctof(vec[1]);
  1596 	v->z = sp_ctof(vec[2]);
  1597 
  1598 	if (params[2] == Prop_Send && (pEdict != NULL))
  1599 	{
  1600 		g_HL2.SetEdictStateChanged(pEdict, offset);
  1601 	}
  1602 
  1603 	return 1;
  1604 }
  1605 
  1606 static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params)
  1607 {
  1608 	CBaseEntity *pEntity;
  1609 	char *prop;
  1610 	int offset;
  1611 	const char *class_name;
  1612 	edict_t *pEdict;
  1613 	bool bIsStringIndex;
  1614 	
  1615 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
  1616 	{
  1617 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
  1618 	}
  1619 
  1620 	if (!pEdict || (class_name = pEdict->GetClassName()) == NULL)
  1621 	{
  1622 		class_name = "";
  1623 	}
  1624 
  1625 	pContext->LocalToString(params[3], &prop);
  1626 
  1627 	bIsStringIndex = false;
  1628 
  1629 	switch (params[2])
  1630 	{
  1631 	case Prop_Data:
  1632 		{
  1633 			typedescription_t *td;
  1634 			
  1635 			FIND_PROP_DATA(td);
  1636 
  1637 			if (td->fieldType != FIELD_CHARACTER
  1638 				&& td->fieldType != FIELD_STRING
  1639 				&& td->fieldType != FIELD_MODELNAME 
  1640 				&& td->fieldType != FIELD_SOUNDNAME)
  1641 			{
  1642 				return pContext->ThrowNativeError("Data field %s is not a string (%d != %d)", 
  1643 					prop,
  1644 					td->fieldType,
  1645 					FIELD_CHARACTER);
  1646 			}
  1647 
  1648 			bIsStringIndex = (td->fieldType != FIELD_CHARACTER);
  1649 
  1650 			offset = GetTypeDescOffs(td);
  1651 			break;
  1652 		}
  1653 	case Prop_Send:
  1654 		{
  1655 			sm_sendprop_info_t info;
  1656 
  1657 			FIND_PROP_SEND(&info);
  1658 
  1659 			if (info.prop->GetType() != DPT_String)
  1660 			{
  1661 				return pContext->ThrowNativeError("SendProp %s is not a string (%d != %d)",
  1662 					prop,
  1663 					info.prop->GetType(),
  1664 					DPT_String);
  1665 			}
  1666 
  1667 			offset = info.actual_offset;
  1668 			break;
  1669 			break;
  1670 		}
  1671 	default:
  1672 		{
  1673 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1674 		}
  1675 	}
  1676 
  1677 	size_t len;
  1678 	const char *src; 
  1679 	
  1680 	if (!bIsStringIndex)
  1681 	{
  1682 		src = (char *)((uint8_t *)pEntity + offset);
  1683 	}
  1684 	else
  1685 	{
  1686 		string_t idx;
  1687 
  1688 		idx = *(string_t *)((uint8_t *)pEntity + offset);
  1689 		src = (idx == NULL_STRING) ? "" : STRING(idx);
  1690 	}
  1691 
  1692 	pContext->StringToLocalUTF8(params[4], params[5], src, &len);
  1693 
  1694 	return len;
  1695 }
  1696 
  1697 static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
  1698 {
  1699 	CBaseEntity *pEntity;
  1700 	char *prop;
  1701 	int offset;
  1702 	int maxlen;
  1703 	edict_t *pEdict;
  1704 
  1705 	if (!IndexToAThings(params[1], &pEntity, &pEdict))
  1706 	{
  1707 		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
  1708 	}
  1709 
  1710 	switch (params[2])
  1711 	{
  1712 	case Prop_Data:
  1713 		{
  1714 			datamap_t *pMap;
  1715 			typedescription_t *td;
  1716 			if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
  1717 			{
  1718 				return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
  1719 			}
  1720 			pContext->LocalToString(params[3], &prop);
  1721 			if ((td=g_HL2.FindInDataMap(pMap, prop)) == NULL)
  1722 			{
  1723 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
  1724 			}
  1725 			if (td->fieldType != FIELD_CHARACTER)
  1726 			{
  1727 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
  1728 			}
  1729 			offset = GetTypeDescOffs(td);
  1730 			maxlen = td->fieldSize;
  1731 			break;
  1732 		}
  1733 	case Prop_Send:
  1734 		{
  1735 			char *prop;
  1736 			IServerUnknown *pUnk = (IServerUnknown *)pEntity;
  1737 			IServerNetworkable *pNet = pUnk->GetNetworkable();
  1738 			if (!pNet)
  1739 			{
  1740 				return pContext->ThrowNativeError("The edict is not networkable");
  1741 			}
  1742 			pContext->LocalToString(params[3], &prop);
  1743 			SendProp *pSend = g_HL2.FindInSendTable(pNet->GetServerClass()->GetName(), prop);
  1744 			if (!pSend)
  1745 			{
  1746 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
  1747 			}
  1748 			if (pSend->GetType() != DPT_String)
  1749 			{
  1750 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
  1751 			}
  1752 			offset = pSend->GetOffset();
  1753 			maxlen = DT_MAX_STRING_BUFFERSIZE;
  1754 			break;
  1755 		}
  1756 	default:
  1757 		{
  1758 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
  1759 		}
  1760 	}
  1761 
  1762 	char *src;
  1763 	char *dest = (char *)((uint8_t *)pEntity + offset);
  1764 
  1765 	pContext->LocalToString(params[4], &src);
  1766 	size_t len = strncopy(dest, src, maxlen);
  1767 
  1768 	if (params[2] == Prop_Send && (pEdict != NULL))
  1769 	{
  1770 		g_HL2.SetEdictStateChanged(pEdict, offset);
  1771 	}
  1772 
  1773 	return len;
  1774 }
  1775 
  1776 REGISTER_NATIVES(entityNatives)
  1777 {
  1778 	{"ChangeEdictState",		ChangeEdictState},
  1779 	{"CreateEdict",				CreateEdict},
  1780 	{"FindDataMapOffs",			FindDataMapOffs},
  1781 	{"FindSendPropInfo",		FindSendPropInfo},
  1782 	{"FindSendPropOffs",		FindSendPropOffs},
  1783 	{"GetEdictClassname",		GetEdictClassname},
  1784 	{"GetEdictFlags",			GetEdictFlags},
  1785 	{"GetEntData",				GetEntData},
  1786 	{"GetEntDataEnt",			GetEntDataEnt},		/** DEPRECATED */
  1787 	{"GetEntDataEnt2",			GetEntDataEnt2},
  1788 	{"GetEntDataFloat",			GetEntDataFloat},
  1789 	{"GetEntDataString",		GetEntDataString},
  1790 	{"GetEntDataVector",		GetEntDataVector},
  1791 	{"GetEntProp",				GetEntProp},
  1792 	{"GetEntPropEnt",			GetEntPropEnt},
  1793 	{"GetEntPropFloat",			GetEntPropFloat},
  1794 	{"GetEntPropString",		GetEntPropString},
  1795 	{"GetEntPropVector",		GetEntPropVector},
  1796 	{"GetEntityCount",			GetEntityCount},
  1797 	{"GetEntityNetClass",		GetEntityNetClass},
  1798 	{"GetMaxEntities",			GetMaxEntities},
  1799 	{"IsEntNetworkable",		IsEntNetworkable},
  1800 	{"IsValidEdict",			IsValidEdict},
  1801 	{"IsValidEntity",			IsValidEntity},
  1802 	{"RemoveEdict",				RemoveEdict},
  1803 	{"SetEdictFlags",			SetEdictFlags},
  1804 	{"SetEntData",				SetEntData},
  1805 	{"SetEntDataEnt",			SetEntDataEnt},		/** DEPRECATED */
  1806 	{"SetEntDataEnt2",			SetEntDataEnt2},
  1807 	{"SetEntDataFloat",			SetEntDataFloat},
  1808 	{"SetEntDataVector",		SetEntDataVector},
  1809 	{"SetEntDataString",		SetEntDataString},
  1810 	{"SetEntProp",				SetEntProp},
  1811 	{"SetEntPropEnt",			SetEntPropEnt},
  1812 	{"SetEntPropFloat",			SetEntPropFloat},
  1813 	{"SetEntPropString",		SetEntPropString},
  1814 	{"SetEntPropVector",		SetEntPropVector},
  1815 	{NULL,						NULL}
  1816 };