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