core/smn_entities.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jun 01 02:30:29 2007 +0000 (2007-06-01)
changeset 868 bdbf922e246f
parent 694 b9b2cbe6352e
child 965 327edbb1357a
permissions -rw-r--r--
-initial import of database natives + config
-initial import of completed database layer
-CreateIdentity now requires a non-optional second pointer. This breaks backwards compatibility for CreateIdentity(), however, this is not a function extension authors are supposed to be calling
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include "sm_globals.h"
    16 #include "sourcemod.h"
    17 #include "sourcemm_api.h"
    18 #include "server_class.h"
    19 #include "PlayerManager.h"
    20 #include "HalfLife2.h"
    21 #include "GameConfigs.h"
    22 #include "sm_stringutil.h"
    23 
    24 enum PropType
    25 {
    26 	Prop_Send = 0,
    27 	Prop_Data
    28 };
    29 
    30 inline edict_t *GetEdict(cell_t num)
    31 {
    32 	edict_t *pEdict = engine->PEntityOfEntIndex(num);
    33 	if (!pEdict || pEdict->IsFree())
    34 	{
    35 		return NULL;
    36 	}
    37 	if (num > 0 && num < g_Players.GetMaxClients())
    38 	{
    39 		CPlayer *pPlayer = g_Players.GetPlayerByIndex(num);
    40 		if (!pPlayer || !pPlayer->IsConnected())
    41 		{
    42 			return NULL;
    43 		}
    44 	}
    45 	return pEdict;
    46 }
    47 
    48 inline edict_t *GetEntity(cell_t num, CBaseEntity **pData)
    49 {
    50 	edict_t *pEdict = engine->PEntityOfEntIndex(num);
    51 	if (!pEdict || pEdict->IsFree())
    52 	{
    53 		return NULL;
    54 	}
    55 	if (num > 0 && num < g_Players.GetMaxClients())
    56 	{
    57 		CPlayer *pPlayer = g_Players.GetPlayerByIndex(num);
    58 		if (!pPlayer || !pPlayer->IsConnected())
    59 		{
    60 			return NULL;
    61 		}
    62 	}
    63 	IServerUnknown *pUnk;
    64 	if ((pUnk=pEdict->GetUnknown()) == NULL)
    65 	{
    66 		return NULL;
    67 	}
    68 	*pData = pUnk->GetBaseEntity();
    69 	return pEdict;
    70 }
    71 
    72 class VEmptyClass {};
    73 datamap_t *VGetDataDescMap(CBaseEntity *pThisPtr, int offset)
    74 {
    75 	void **this_ptr = *reinterpret_cast<void ***>(&pThisPtr);
    76 	void **vtable = *reinterpret_cast<void ***>(pThisPtr);
    77 	void *vfunc = vtable[offset];
    78 
    79 	union
    80 	{
    81 		datamap_t *(VEmptyClass::*mfpnew)();
    82 #ifndef PLATFORM_POSIX
    83 		void *addr;
    84 	} u;
    85 	u.addr = vfunc;
    86 #else
    87 		struct  
    88 		{
    89 			void *addr;
    90 			intptr_t adjustor;
    91 		} s;
    92 	} u;
    93 	u.s.addr = vfunc;
    94 	u.s.adjustor = 0;
    95 #endif
    96 
    97 	return (datamap_t *)(reinterpret_cast<VEmptyClass *>(this_ptr)->*u.mfpnew)();
    98 }
    99 
   100 inline datamap_t *CBaseEntity_GetDataDescMap(CBaseEntity *pEntity)
   101 {
   102 	int offset;
   103 
   104 	if (!g_pGameConf->GetOffset("GetDataDescMap", &offset) || !offset)
   105 	{
   106 		return NULL;
   107 	}
   108 
   109 	return VGetDataDescMap(pEntity, offset);
   110 }
   111 
   112 static cell_t GetMaxEntities(IPluginContext *pContext, const cell_t *params)
   113 {
   114 	return gpGlobals->maxEntities;
   115 }
   116 
   117 static cell_t CreateEdict(IPluginContext *pContext, const cell_t *params)
   118 {
   119 	edict_t *pEdict = engine->CreateEdict();
   120 
   121 	if (!pEdict)
   122 	{
   123 		return 0;
   124 	}
   125 
   126 	return engine->IndexOfEdict(pEdict);
   127 }
   128 
   129 static cell_t RemoveEdict(IPluginContext *pContext, const cell_t *params)
   130 {
   131 	edict_t *pEdict = engine->PEntityOfEntIndex(params[1]);
   132 
   133 	if (!pEdict)
   134 	{
   135 		return pContext->ThrowNativeError("Edict %d is not a valid edict", params[1]);
   136 	}
   137 
   138 	engine->RemoveEdict(pEdict);
   139 
   140 	return 1;
   141 }
   142 
   143 static cell_t IsValidEdict(IPluginContext *pContext, const cell_t *params)
   144 {
   145 	edict_t *pEdict = GetEdict(params[1]);
   146 
   147 	if (!pEdict)
   148 	{
   149 		return 0;
   150 	}
   151 
   152 	/* Shouldn't be necessary... not sure though */
   153 	return pEdict->IsFree() ? 0 : 1;
   154 }
   155 
   156 static cell_t IsValidEntity(IPluginContext *pContext, const cell_t *params)
   157 {
   158 	edict_t *pEdict = GetEdict(params[1]);
   159 
   160 	if (!pEdict)
   161 	{
   162 		return 0;
   163 	}
   164 
   165 	IServerUnknown *pUnknown = pEdict->GetUnknown();
   166 	if (!pUnknown)
   167 	{
   168 		return 0;
   169 	}
   170 
   171 	CBaseEntity *pEntity = pUnknown->GetBaseEntity();
   172 	return (pEntity != NULL) ? 1 : 0;
   173 }
   174 
   175 static cell_t IsEntNetworkable(IPluginContext *pContext, const cell_t *params)
   176 {
   177 	edict_t *pEdict = GetEdict(params[1]);
   178 
   179 	if (!pEdict)
   180 	{
   181 		return 0;
   182 	}
   183 
   184 	return (pEdict->GetNetworkable() != NULL) ? 1 : 0;
   185 }
   186 
   187 static cell_t GetEntityCount(IPluginContext *pContext, const cell_t *params)
   188 {
   189 	return engine->GetEntityCount();
   190 }
   191 
   192 static cell_t GetEdictFlags(IPluginContext *pContext, const cell_t *params)
   193 {
   194 	edict_t *pEdict = GetEdict(params[1]);
   195 
   196 	if (!pEdict)
   197 	{
   198 		return pContext->ThrowNativeError("Invalid edict (%d)", params[1]);
   199 	}
   200 
   201 	return pEdict->m_fStateFlags;
   202 }
   203 
   204 static cell_t SetEdictFlags(IPluginContext *pContext, const cell_t *params)
   205 {
   206 	edict_t *pEdict = GetEdict(params[1]);
   207 
   208 	if (!pEdict)
   209 	{
   210 		return pContext->ThrowNativeError("Invalid edict (%d)", params[1]);
   211 	}
   212 
   213 	pEdict->m_fStateFlags = params[2];
   214 
   215 	return 1;
   216 }
   217 
   218 static cell_t GetEdictClassname(IPluginContext *pContext, const cell_t *params)
   219 {
   220 	edict_t *pEdict = GetEdict(params[1]);
   221 
   222 	if (!pEdict)
   223 	{
   224 		return pContext->ThrowNativeError("Invalid edict (%d)", params[1]);
   225 	}
   226 
   227 	const char *cls = pEdict->GetClassName();
   228 
   229 	if (!cls || cls[0] == '\0')
   230 	{
   231 		return 0;
   232 	}
   233 
   234 	pContext->StringToLocal(params[2], params[3], cls);
   235 
   236 	return 1;
   237 }
   238 
   239 static cell_t GetEntityNetClass(IPluginContext *pContext, const cell_t *params)
   240 {
   241 	edict_t *pEdict = GetEdict(params[1]);
   242 
   243 	if (!pEdict)
   244 	{
   245 		return pContext->ThrowNativeError("Invalid edict (%d)", params[1]);
   246 	}
   247 
   248 	IServerNetworkable *pNet = pEdict->GetNetworkable();
   249 	if (!pNet)
   250 	{
   251 		return 0;
   252 	}
   253 
   254 	ServerClass *pClass = pNet->GetServerClass();
   255 
   256 	pContext->StringToLocal(params[2], params[3], pClass->GetName());
   257 
   258 	return 1;
   259 }
   260 
   261 static cell_t GetEntData(IPluginContext *pContext, const cell_t *params)
   262 {
   263 	CBaseEntity *pEntity;
   264 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   265 
   266 	if (!pEdict || !pEntity)
   267 	{
   268 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   269 	}
   270 
   271 	int offset = params[2];
   272 	if (offset < 0 || offset > 32768)
   273 	{
   274 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   275 	}
   276 
   277 	switch (params[3])
   278 	{
   279 	case 4:
   280 		return *(int *)((uint8_t *)pEntity + offset);
   281 	case 2:
   282 		return *(short *)((uint8_t *)pEntity + offset);
   283 	case 1:
   284 		return *((uint8_t *)pEntity + offset);
   285 	default:
   286 		return pContext->ThrowNativeError("Integer size %d is invalid", params[3]);
   287 	}
   288 }
   289 
   290 static cell_t SetEntData(IPluginContext *pContext, const cell_t *params)
   291 {
   292 	CBaseEntity *pEntity;
   293 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   294 
   295 	if (!pEdict || !pEntity)
   296 	{
   297 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   298 	}
   299 
   300 	int offset = params[2];
   301 	if (offset < 0 || offset > 32768)
   302 	{
   303 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   304 	}
   305 
   306 	if (params[5])
   307 	{
   308 		g_HL2.SetEdictStateChanged(pEdict, offset);
   309 	}
   310 
   311 	switch (params[4])
   312 	{
   313 	case 4:
   314 		{
   315 			*(int *)((uint8_t *)pEntity + offset) = params[3];
   316 			break;
   317 		}
   318 	case 2:
   319 		{
   320 			*(short *)((uint8_t *)pEntity + offset) = params[3];
   321 			break;
   322 		}
   323 	case 1:
   324 		{
   325 			*((uint8_t *)pEntity + offset) = params[3];
   326 			break;
   327 		}
   328 	default:
   329 		return pContext->ThrowNativeError("Integer size %d is invalid", params[4]);
   330 	}
   331 
   332 	return 1;
   333 }
   334 
   335 static cell_t GetEntDataFloat(IPluginContext *pContext, const cell_t *params)
   336 {
   337 	CBaseEntity *pEntity;
   338 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   339 
   340 	if (!pEdict || !pEntity)
   341 	{
   342 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   343 	}
   344 
   345 	int offset = params[2];
   346 	if (offset < 0 || offset > 32768)
   347 	{
   348 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   349 	}
   350 
   351 	float f = *(float *)((uint8_t *)pEntity + offset);
   352 
   353 	return sp_ftoc(f);
   354 }
   355 
   356 static cell_t SetEntDataFloat(IPluginContext *pContext, const cell_t *params)
   357 {
   358 	CBaseEntity *pEntity;
   359 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   360 
   361 	if (!pEdict || !pEntity)
   362 	{
   363 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   364 	}
   365 
   366 	int offset = params[2];
   367 	if (offset < 0 || offset > 32768)
   368 	{
   369 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   370 	}
   371 
   372 	*(float *)((uint8_t *)pEntity + offset) = sp_ctof(params[3]);
   373 
   374 	if (params[4])
   375 	{
   376 		g_HL2.SetEdictStateChanged(pEdict, offset);
   377 	}
   378 
   379 	return 1;
   380 }
   381 
   382 static cell_t GetEntDataVector(IPluginContext *pContext, const cell_t *params)
   383 {
   384 	CBaseEntity *pEntity;
   385 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   386 
   387 	if (!pEdict || !pEntity)
   388 	{
   389 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   390 	}
   391 
   392 	int offset = params[2];
   393 	if (offset < 0 || offset > 32768)
   394 	{
   395 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   396 	}
   397 
   398 	Vector *v = (Vector *)((uint8_t *)pEntity + offset);
   399 
   400 	cell_t *vec;
   401 	pContext->LocalToPhysAddr(params[3], &vec);
   402 
   403 	vec[0] = sp_ftoc(v->x);
   404 	vec[1] = sp_ftoc(v->y);
   405 	vec[2] = sp_ftoc(v->z);
   406 
   407 	return 1;
   408 }
   409 
   410 static cell_t SetEntDataVector(IPluginContext *pContext, const cell_t *params)
   411 {
   412 	CBaseEntity *pEntity;
   413 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   414 
   415 	if (!pEdict || !pEntity)
   416 	{
   417 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   418 	}
   419 
   420 	int offset = params[2];
   421 	if (offset < 0 || offset > 32768)
   422 	{
   423 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   424 	}
   425 
   426 	Vector *v = (Vector *)((uint8_t *)pEntity + offset);
   427 
   428 	cell_t *vec;
   429 	pContext->LocalToPhysAddr(params[3], &vec);
   430 
   431 	v->x = sp_ctof(vec[0]);
   432 	v->y = sp_ctof(vec[1]);
   433 	v->z = sp_ctof(vec[2]);
   434 
   435 	if (params[4])
   436 	{
   437 		g_HL2.SetEdictStateChanged(pEdict, offset);
   438 	}
   439 
   440 	return 1;
   441 }
   442 
   443 static cell_t GetEntDataEnt(IPluginContext *pContext, const cell_t *params)
   444 {
   445 	CBaseEntity *pEntity;
   446 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   447 
   448 	if (!pEdict || !pEntity)
   449 	{
   450 		return pContext->ThrowNativeError("Entity %d is invalid", 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 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
   460 
   461 	if (!hndl.IsValid())
   462 	{
   463 		return 0;
   464 	}
   465 
   466 	/* :TODO: check serial no.? */
   467 
   468 	return hndl.GetEntryIndex();
   469 }
   470 
   471 static cell_t SetEntDataEnt(IPluginContext *pContext, const cell_t *params)
   472 {
   473 	CBaseEntity *pEntity;
   474 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   475 
   476 	if (!pEdict || !pEntity)
   477 	{
   478 		return pContext->ThrowNativeError("Entity %d is invalid", 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 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
   488 
   489 	if (params[3] == 0)
   490 	{
   491 		hndl.Set(NULL);
   492 	} else {
   493 		edict_t *pEdict = GetEdict(params[3]);
   494 		if (!pEdict)
   495 		{
   496 			return pContext->ThrowNativeError("Entity %d is invalid", params[3]);
   497 		}
   498 		IServerEntity *pEntOther = pEdict->GetIServerEntity();
   499 		hndl.Set(pEntOther);
   500 	}
   501 
   502 	if (params[4])
   503 	{
   504 		g_HL2.SetEdictStateChanged(pEdict, offset);
   505 	}
   506 
   507 	return 1;
   508 }
   509 
   510 static cell_t ChangeEdictState(IPluginContext *pContext, const cell_t *params)
   511 {
   512 	edict_t *pEdict = GetEdict(params[1]);
   513 
   514 	if (!pEdict)
   515 	{
   516 		return pContext->ThrowNativeError("Edict %d is invalid", params[1]);
   517 	}
   518 
   519 	g_HL2.SetEdictStateChanged(pEdict, params[2]);
   520 
   521 	return 1;
   522 }
   523 
   524 static cell_t FindSendPropOffs(IPluginContext *pContext, const cell_t *params)
   525 {
   526 	char *cls, *prop;
   527 	pContext->LocalToString(params[1], &cls);
   528 	pContext->LocalToString(params[2], &prop);
   529 
   530 	SendProp *pSend = g_HL2.FindInSendTable(cls, prop);
   531 
   532 	if (!pSend)
   533 	{
   534 		return -1;
   535 	}
   536 
   537 	return pSend->GetOffset();
   538 }
   539 
   540 static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params)
   541 {
   542 	CBaseEntity *pEntity;
   543 	datamap_t *pMap;
   544 	typedescription_t *td;
   545 	char *offset;
   546 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   547 
   548 	if (!pEdict || !pEntity)
   549 	{
   550 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   551 	}
   552 
   553 	if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   554 	{
   555 		return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
   556 	}
   557 
   558 	pContext->LocalToString(params[2], &offset);
   559 	if ((td=g_HL2.FindInDataMap(pMap, offset)) == NULL)
   560 	{
   561 		return -1;
   562 	}
   563 
   564 	return td->fieldOffset[TD_OFFSET_NORMAL];
   565 }
   566 
   567 static cell_t GetEntDataString(IPluginContext *pContext, const cell_t *params)
   568 {
   569 	CBaseEntity *pEntity;
   570 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   571 
   572 	if (!pEdict || !pEntity)
   573 	{
   574 		return pContext->ThrowNativeError("Entity %d is invalid", 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 	size_t len;
   584 	char *src = (char *)((uint8_t *)pEntity + offset);
   585 	pContext->StringToLocalUTF8(params[3], params[4], src, &len);
   586 
   587 	return len;
   588 }
   589 
   590 static cell_t SetEntDataString(IPluginContext *pContext, const cell_t *params)
   591 {
   592 	CBaseEntity *pEntity;
   593 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   594 
   595 	if (!pEdict || !pEntity)
   596 	{
   597 		return pContext->ThrowNativeError("Entity %d is invalid", 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 	char *src;
   607 	char *dest = (char *)((uint8_t *)pEntity + offset);
   608 
   609 	pContext->LocalToString(params[3], &src);
   610 	size_t len = strncopy(dest, src, params[4]);
   611 
   612 	if (params[5])
   613 	{
   614 		g_HL2.SetEdictStateChanged(pEdict, offset);
   615 	}
   616 
   617 	return len;
   618 }
   619 
   620 static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params)
   621 {
   622 	CBaseEntity *pEntity;
   623 	char *prop;
   624 	int offset;
   625 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   626 
   627 	if (!pEdict || !pEntity)
   628 	{
   629 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   630 	}
   631 
   632 	switch (params[2])
   633 	{
   634 	case Prop_Data:
   635 		{
   636 			datamap_t *pMap;
   637 			typedescription_t *td;
   638 			if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   639 			{
   640 				return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
   641 			}
   642 			pContext->LocalToString(params[3], &prop);
   643 			if ((td=g_HL2.FindInDataMap(pMap, prop)) == NULL)
   644 			{
   645 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   646 			}
   647 			if (td->fieldType != FIELD_CHARACTER)
   648 			{
   649 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
   650 			}
   651 			offset = td->fieldOffset[TD_OFFSET_NORMAL];
   652 			break;
   653 		}
   654 	case Prop_Send:
   655 		{
   656 			char *prop;
   657 			IServerNetworkable *pNet = pEdict->GetNetworkable();
   658 			if (!pNet)
   659 			{
   660 				return pContext->ThrowNativeError("The edict is not networkable");
   661 			}
   662 			pContext->LocalToString(params[3], &prop);
   663 			SendProp *pSend = g_HL2.FindInSendTable(pNet->GetServerClass()->GetName(), prop);
   664 			if (!pSend)
   665 			{
   666 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   667 			}
   668 			if (pSend->GetType() != DPT_String)
   669 			{
   670 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
   671 			}
   672 			offset = pSend->GetOffset();
   673 			break;
   674 		}
   675 	default:
   676 		{
   677 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
   678 		}
   679 	}
   680 
   681 	size_t len;
   682 	char *src = (char *)((uint8_t *)pEntity + offset);
   683 	pContext->StringToLocalUTF8(params[4], params[5], src, &len);
   684 
   685 	return len;
   686 }
   687 
   688 static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
   689 {
   690 	CBaseEntity *pEntity;
   691 	char *prop;
   692 	int offset;
   693 	int maxlen;
   694 	bool is_sendprop = false;
   695 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   696 
   697 	if (!pEdict || !pEntity)
   698 	{
   699 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   700 	}
   701 
   702 	switch (params[2])
   703 	{
   704 	case Prop_Data:
   705 		{
   706 			datamap_t *pMap;
   707 			typedescription_t *td;
   708 			if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   709 			{
   710 				return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
   711 			}
   712 			pContext->LocalToString(params[3], &prop);
   713 			if ((td=g_HL2.FindInDataMap(pMap, prop)) == NULL)
   714 			{
   715 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   716 			}
   717 			if (td->fieldType != FIELD_CHARACTER)
   718 			{
   719 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
   720 			}
   721 			offset = td->fieldOffset[TD_OFFSET_NORMAL];
   722 			maxlen = td->fieldSize;
   723 			break;
   724 		}
   725 	case Prop_Send:
   726 		{
   727 			char *prop;
   728 			IServerNetworkable *pNet = pEdict->GetNetworkable();
   729 			if (!pNet)
   730 			{
   731 				return pContext->ThrowNativeError("The edict is not networkable");
   732 			}
   733 			pContext->LocalToString(params[3], &prop);
   734 			SendProp *pSend = g_HL2.FindInSendTable(pNet->GetServerClass()->GetName(), prop);
   735 			if (!pSend)
   736 			{
   737 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   738 			}
   739 			if (pSend->GetType() != DPT_String)
   740 			{
   741 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
   742 			}
   743 			offset = pSend->GetOffset();
   744 			maxlen = DT_MAX_STRING_BUFFERSIZE;
   745 			is_sendprop = true;
   746 			break;
   747 		}
   748 	default:
   749 		{
   750 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
   751 		}
   752 	}
   753 
   754 	char *src;
   755 	char *dest = (char *)((uint8_t *)pEntity + offset);
   756 
   757 	pContext->LocalToString(params[4], &src);
   758 	size_t len = strncopy(dest, src, maxlen);
   759 
   760 	if (is_sendprop)
   761 	{
   762 		g_HL2.SetEdictStateChanged(pEdict, offset);
   763 	}
   764 
   765 	return len;
   766 }
   767 
   768 REGISTER_NATIVES(entityNatives)
   769 {
   770 	{"ChangeEdictState",		ChangeEdictState},
   771 	{"CreateEdict",				CreateEdict},
   772 	{"FindSendPropOffs",		FindSendPropOffs},
   773 	{"GetEdictClassname",		GetEdictClassname},
   774 	{"GetEdictFlags",			GetEdictFlags},
   775 	{"GetEntData",				GetEntData},
   776 	{"GetEntDataEnt",			GetEntDataEnt},
   777 	{"GetEntDataFloat",			GetEntDataFloat},
   778 	{"GetEntDataVector",		GetEntDataVector},
   779 	{"GetEntityCount",			GetEntityCount},
   780 	{"GetEntityNetClass",		GetEntityNetClass},
   781 	{"GetMaxEntities",			GetMaxEntities},
   782 	{"IsEntNetworkable",		IsEntNetworkable},
   783 	{"IsValidEdict",			IsValidEdict},
   784 	{"IsValidEntity",			IsValidEntity},
   785 	{"RemoveEdict",				RemoveEdict},
   786 	{"SetEdictFlags",			SetEdictFlags},
   787 	{"SetEntData",				SetEntData},
   788 	{"SetEntDataEnt",			SetEntDataEnt},
   789 	{"SetEntDataFloat",			SetEntDataFloat},
   790 	{"SetEntDataVector",		SetEntDataVector},
   791 	{"FindDataMapOffs",			FindDataMapOffs},
   792 	{"GetEntDataString",		GetEntDataString},
   793 	{"SetEntDataString",		SetEntDataString},
   794 	{"GetEntPropString",		GetEntPropString},
   795 	{"SetEntPropString",		SetEntPropString},
   796 	{NULL,						NULL}
   797 };