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