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