core/smn_entities.cpp
author Phong Dang <teame06@gmail.com>
Wed Aug 01 04:17:36 2007 +0000 (2007-08-01)
changeset 1235 88bb46f9731e
parent 1231 17badaf66ce4
child 1330 ab94316bb52b
permissions -rw-r--r--
Implemented requested amb603. Added voice natives.
     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 
     9  * modify it under the terms of the GNU General Public License, 
    10  * version 3.0, as published by the Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful,
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  * GNU General Public License for more details.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to 
    21  * link the code of this program (as well as its derivative works) to 
    22  * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
    23  * Game MODs that run on software by the Valve Corporation.  You must 
    24  * obey the GNU General Public License in all respects for all other 
    25  * code used. Additionally, AlliedModders LLC grants this exception 
    26  * to all derivative works. AlliedModders LLC defines further 
    27  * exceptions, found in LICENSE.txt (as of this writing, version 
    28  * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
    29  *
    30  * Version: $Id$
    31  */
    32 
    33 #include "sm_globals.h"
    34 #include "sourcemod.h"
    35 #include "sourcemm_api.h"
    36 #include "server_class.h"
    37 #include "PlayerManager.h"
    38 #include "HalfLife2.h"
    39 #include "GameConfigs.h"
    40 #include "sm_stringutil.h"
    41 
    42 enum PropType
    43 {
    44 	Prop_Send = 0,
    45 	Prop_Data
    46 };
    47 
    48 inline edict_t *GetEdict(cell_t num)
    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 	return pEdict;
    64 }
    65 
    66 inline edict_t *GetEntity(cell_t num, CBaseEntity **pData)
    67 {
    68 	edict_t *pEdict = engine->PEntityOfEntIndex(num);
    69 	if (!pEdict || pEdict->IsFree())
    70 	{
    71 		return NULL;
    72 	}
    73 	if (num > 0 && num < g_Players.GetMaxClients())
    74 	{
    75 		CPlayer *pPlayer = g_Players.GetPlayerByIndex(num);
    76 		if (!pPlayer || !pPlayer->IsConnected())
    77 		{
    78 			return NULL;
    79 		}
    80 	}
    81 	IServerUnknown *pUnk;
    82 	if ((pUnk=pEdict->GetUnknown()) == NULL)
    83 	{
    84 		return NULL;
    85 	}
    86 	*pData = pUnk->GetBaseEntity();
    87 	return pEdict;
    88 }
    89 
    90 class VEmptyClass {};
    91 datamap_t *VGetDataDescMap(CBaseEntity *pThisPtr, int offset)
    92 {
    93 	void **this_ptr = *reinterpret_cast<void ***>(&pThisPtr);
    94 	void **vtable = *reinterpret_cast<void ***>(pThisPtr);
    95 	void *vfunc = vtable[offset];
    96 
    97 	union
    98 	{
    99 		datamap_t *(VEmptyClass::*mfpnew)();
   100 #ifndef PLATFORM_POSIX
   101 		void *addr;
   102 	} u;
   103 	u.addr = vfunc;
   104 #else
   105 		struct  
   106 		{
   107 			void *addr;
   108 			intptr_t adjustor;
   109 		} s;
   110 	} u;
   111 	u.s.addr = vfunc;
   112 	u.s.adjustor = 0;
   113 #endif
   114 
   115 	return (datamap_t *)(reinterpret_cast<VEmptyClass *>(this_ptr)->*u.mfpnew)();
   116 }
   117 
   118 inline datamap_t *CBaseEntity_GetDataDescMap(CBaseEntity *pEntity)
   119 {
   120 	int offset;
   121 
   122 	if (!g_pGameConf->GetOffset("GetDataDescMap", &offset) || !offset)
   123 	{
   124 		return NULL;
   125 	}
   126 
   127 	return VGetDataDescMap(pEntity, offset);
   128 }
   129 
   130 /* :TODO: Move this! */
   131 datamap_t *CHalfLife2::GetDataMap(CBaseEntity *pEntity)
   132 {
   133 	return CBaseEntity_GetDataDescMap(pEntity);
   134 }
   135 
   136 static cell_t GetMaxEntities(IPluginContext *pContext, const cell_t *params)
   137 {
   138 	return gpGlobals->maxEntities;
   139 }
   140 
   141 static cell_t CreateEdict(IPluginContext *pContext, const cell_t *params)
   142 {
   143 	edict_t *pEdict = engine->CreateEdict();
   144 
   145 	if (!pEdict)
   146 	{
   147 		return 0;
   148 	}
   149 
   150 	return engine->IndexOfEdict(pEdict);
   151 }
   152 
   153 static cell_t RemoveEdict(IPluginContext *pContext, const cell_t *params)
   154 {
   155 	edict_t *pEdict = engine->PEntityOfEntIndex(params[1]);
   156 
   157 	if (!pEdict)
   158 	{
   159 		return pContext->ThrowNativeError("Edict %d is not a valid edict", params[1]);
   160 	}
   161 
   162 	engine->RemoveEdict(pEdict);
   163 
   164 	return 1;
   165 }
   166 
   167 static cell_t IsValidEdict(IPluginContext *pContext, const cell_t *params)
   168 {
   169 	edict_t *pEdict = GetEdict(params[1]);
   170 
   171 	if (!pEdict)
   172 	{
   173 		return 0;
   174 	}
   175 
   176 	/* Shouldn't be necessary... not sure though */
   177 	return pEdict->IsFree() ? 0 : 1;
   178 }
   179 
   180 static cell_t IsValidEntity(IPluginContext *pContext, const cell_t *params)
   181 {
   182 	edict_t *pEdict = GetEdict(params[1]);
   183 
   184 	if (!pEdict)
   185 	{
   186 		return 0;
   187 	}
   188 
   189 	IServerUnknown *pUnknown = pEdict->GetUnknown();
   190 	if (!pUnknown)
   191 	{
   192 		return 0;
   193 	}
   194 
   195 	CBaseEntity *pEntity = pUnknown->GetBaseEntity();
   196 	return (pEntity != NULL) ? 1 : 0;
   197 }
   198 
   199 static cell_t IsEntNetworkable(IPluginContext *pContext, const cell_t *params)
   200 {
   201 	edict_t *pEdict = GetEdict(params[1]);
   202 
   203 	if (!pEdict)
   204 	{
   205 		return 0;
   206 	}
   207 
   208 	return (pEdict->GetNetworkable() != NULL) ? 1 : 0;
   209 }
   210 
   211 static cell_t GetEntityCount(IPluginContext *pContext, const cell_t *params)
   212 {
   213 	return engine->GetEntityCount();
   214 }
   215 
   216 static cell_t GetEdictFlags(IPluginContext *pContext, const cell_t *params)
   217 {
   218 	edict_t *pEdict = GetEdict(params[1]);
   219 
   220 	if (!pEdict)
   221 	{
   222 		return pContext->ThrowNativeError("Invalid edict (%d)", params[1]);
   223 	}
   224 
   225 	return pEdict->m_fStateFlags;
   226 }
   227 
   228 static cell_t SetEdictFlags(IPluginContext *pContext, const cell_t *params)
   229 {
   230 	edict_t *pEdict = GetEdict(params[1]);
   231 
   232 	if (!pEdict)
   233 	{
   234 		return pContext->ThrowNativeError("Invalid edict (%d)", params[1]);
   235 	}
   236 
   237 	pEdict->m_fStateFlags = params[2];
   238 
   239 	return 1;
   240 }
   241 
   242 static cell_t GetEdictClassname(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)", params[1]);
   249 	}
   250 
   251 	const char *cls = pEdict->GetClassName();
   252 
   253 	if (!cls || cls[0] == '\0')
   254 	{
   255 		return 0;
   256 	}
   257 
   258 	pContext->StringToLocal(params[2], params[3], cls);
   259 
   260 	return 1;
   261 }
   262 
   263 static cell_t GetEntityNetClass(IPluginContext *pContext, const cell_t *params)
   264 {
   265 	edict_t *pEdict = GetEdict(params[1]);
   266 
   267 	if (!pEdict)
   268 	{
   269 		return pContext->ThrowNativeError("Invalid edict (%d)", params[1]);
   270 	}
   271 
   272 	IServerNetworkable *pNet = pEdict->GetNetworkable();
   273 	if (!pNet)
   274 	{
   275 		return 0;
   276 	}
   277 
   278 	ServerClass *pClass = pNet->GetServerClass();
   279 
   280 	pContext->StringToLocal(params[2], params[3], pClass->GetName());
   281 
   282 	return 1;
   283 }
   284 
   285 static cell_t GetEntData(IPluginContext *pContext, const cell_t *params)
   286 {
   287 	CBaseEntity *pEntity;
   288 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   289 
   290 	if (!pEdict || !pEntity)
   291 	{
   292 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   293 	}
   294 
   295 	int offset = params[2];
   296 	if (offset < 0 || offset > 32768)
   297 	{
   298 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   299 	}
   300 
   301 	switch (params[3])
   302 	{
   303 	case 4:
   304 		return *(int *)((uint8_t *)pEntity + offset);
   305 	case 2:
   306 		return *(short *)((uint8_t *)pEntity + offset);
   307 	case 1:
   308 		return *((uint8_t *)pEntity + offset);
   309 	default:
   310 		return pContext->ThrowNativeError("Integer size %d is invalid", params[3]);
   311 	}
   312 }
   313 
   314 static cell_t SetEntData(IPluginContext *pContext, const cell_t *params)
   315 {
   316 	CBaseEntity *pEntity;
   317 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   318 
   319 	if (!pEdict || !pEntity)
   320 	{
   321 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   322 	}
   323 
   324 	int offset = params[2];
   325 	if (offset < 0 || offset > 32768)
   326 	{
   327 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   328 	}
   329 
   330 	if (params[5])
   331 	{
   332 		g_HL2.SetEdictStateChanged(pEdict, offset);
   333 	}
   334 
   335 	switch (params[4])
   336 	{
   337 	case 4:
   338 		{
   339 			*(int *)((uint8_t *)pEntity + offset) = params[3];
   340 			break;
   341 		}
   342 	case 2:
   343 		{
   344 			*(short *)((uint8_t *)pEntity + offset) = params[3];
   345 			break;
   346 		}
   347 	case 1:
   348 		{
   349 			*((uint8_t *)pEntity + offset) = params[3];
   350 			break;
   351 		}
   352 	default:
   353 		return pContext->ThrowNativeError("Integer size %d is invalid", params[4]);
   354 	}
   355 
   356 	return 1;
   357 }
   358 
   359 static cell_t GetEntDataFloat(IPluginContext *pContext, const cell_t *params)
   360 {
   361 	CBaseEntity *pEntity;
   362 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   363 
   364 	if (!pEdict || !pEntity)
   365 	{
   366 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   367 	}
   368 
   369 	int offset = params[2];
   370 	if (offset < 0 || offset > 32768)
   371 	{
   372 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   373 	}
   374 
   375 	float f = *(float *)((uint8_t *)pEntity + offset);
   376 
   377 	return sp_ftoc(f);
   378 }
   379 
   380 static cell_t SetEntDataFloat(IPluginContext *pContext, const cell_t *params)
   381 {
   382 	CBaseEntity *pEntity;
   383 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   384 
   385 	if (!pEdict || !pEntity)
   386 	{
   387 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   388 	}
   389 
   390 	int offset = params[2];
   391 	if (offset < 0 || offset > 32768)
   392 	{
   393 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   394 	}
   395 
   396 	*(float *)((uint8_t *)pEntity + offset) = sp_ctof(params[3]);
   397 
   398 	if (params[4])
   399 	{
   400 		g_HL2.SetEdictStateChanged(pEdict, offset);
   401 	}
   402 
   403 	return 1;
   404 }
   405 
   406 static cell_t GetEntDataVector(IPluginContext *pContext, const cell_t *params)
   407 {
   408 	CBaseEntity *pEntity;
   409 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   410 
   411 	if (!pEdict || !pEntity)
   412 	{
   413 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   414 	}
   415 
   416 	int offset = params[2];
   417 	if (offset < 0 || offset > 32768)
   418 	{
   419 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   420 	}
   421 
   422 	Vector *v = (Vector *)((uint8_t *)pEntity + offset);
   423 
   424 	cell_t *vec;
   425 	pContext->LocalToPhysAddr(params[3], &vec);
   426 
   427 	vec[0] = sp_ftoc(v->x);
   428 	vec[1] = sp_ftoc(v->y);
   429 	vec[2] = sp_ftoc(v->z);
   430 
   431 	return 1;
   432 }
   433 
   434 static cell_t SetEntDataVector(IPluginContext *pContext, const cell_t *params)
   435 {
   436 	CBaseEntity *pEntity;
   437 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   438 
   439 	if (!pEdict || !pEntity)
   440 	{
   441 		return pContext->ThrowNativeError("Entity %d is invalid", 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 	v->x = sp_ctof(vec[0]);
   456 	v->y = sp_ctof(vec[1]);
   457 	v->z = sp_ctof(vec[2]);
   458 
   459 	if (params[4])
   460 	{
   461 		g_HL2.SetEdictStateChanged(pEdict, offset);
   462 	}
   463 
   464 	return 1;
   465 }
   466 
   467 static cell_t GetEntDataEnt(IPluginContext *pContext, const cell_t *params)
   468 {
   469 	CBaseEntity *pEntity;
   470 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   471 
   472 	if (!pEdict || !pEntity)
   473 	{
   474 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   475 	}
   476 
   477 	int offset = params[2];
   478 	if (offset < 0 || offset > 32768)
   479 	{
   480 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   481 	}
   482 
   483 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
   484 
   485 	if (!hndl.IsValid())
   486 	{
   487 		return 0;
   488 	}
   489 
   490 	/* :TODO: check serial no.? */
   491 
   492 	return hndl.GetEntryIndex();
   493 }
   494 
   495 static cell_t SetEntDataEnt(IPluginContext *pContext, const cell_t *params)
   496 {
   497 	CBaseEntity *pEntity;
   498 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   499 
   500 	if (!pEdict || !pEntity)
   501 	{
   502 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   503 	}
   504 
   505 	int offset = params[2];
   506 	if (offset < 0 || offset > 32768)
   507 	{
   508 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   509 	}
   510 
   511 	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEntity + offset);
   512 
   513 	if (params[3] == 0)
   514 	{
   515 		hndl.Set(NULL);
   516 	} else {
   517 		edict_t *pEdict = GetEdict(params[3]);
   518 		if (!pEdict)
   519 		{
   520 			return pContext->ThrowNativeError("Entity %d is invalid", params[3]);
   521 		}
   522 		IServerEntity *pEntOther = pEdict->GetIServerEntity();
   523 		hndl.Set(pEntOther);
   524 	}
   525 
   526 	if (params[4])
   527 	{
   528 		g_HL2.SetEdictStateChanged(pEdict, offset);
   529 	}
   530 
   531 	return 1;
   532 }
   533 
   534 static cell_t ChangeEdictState(IPluginContext *pContext, const cell_t *params)
   535 {
   536 	edict_t *pEdict = GetEdict(params[1]);
   537 
   538 	if (!pEdict)
   539 	{
   540 		return pContext->ThrowNativeError("Edict %d is invalid", params[1]);
   541 	}
   542 
   543 	g_HL2.SetEdictStateChanged(pEdict, params[2]);
   544 
   545 	return 1;
   546 }
   547 
   548 static cell_t FindSendPropOffs(IPluginContext *pContext, const cell_t *params)
   549 {
   550 	char *cls, *prop;
   551 	pContext->LocalToString(params[1], &cls);
   552 	pContext->LocalToString(params[2], &prop);
   553 
   554 	SendProp *pSend = g_HL2.FindInSendTable(cls, prop);
   555 
   556 	if (!pSend)
   557 	{
   558 		return -1;
   559 	}
   560 
   561 	return pSend->GetOffset();
   562 }
   563 
   564 static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params)
   565 {
   566 	CBaseEntity *pEntity;
   567 	datamap_t *pMap;
   568 	typedescription_t *td;
   569 	char *offset;
   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 	if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   578 	{
   579 		return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
   580 	}
   581 
   582 	pContext->LocalToString(params[2], &offset);
   583 	if ((td=g_HL2.FindInDataMap(pMap, offset)) == NULL)
   584 	{
   585 		return -1;
   586 	}
   587 
   588 	return td->fieldOffset[TD_OFFSET_NORMAL];
   589 }
   590 
   591 static cell_t GetEntDataString(IPluginContext *pContext, const cell_t *params)
   592 {
   593 	CBaseEntity *pEntity;
   594 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   595 
   596 	if (!pEdict || !pEntity)
   597 	{
   598 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   599 	}
   600 
   601 	int offset = params[2];
   602 	if (offset < 0 || offset > 32768)
   603 	{
   604 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   605 	}
   606 
   607 	size_t len;
   608 	char *src = (char *)((uint8_t *)pEntity + offset);
   609 	pContext->StringToLocalUTF8(params[3], params[4], src, &len);
   610 
   611 	return len;
   612 }
   613 
   614 static cell_t SetEntDataString(IPluginContext *pContext, const cell_t *params)
   615 {
   616 	CBaseEntity *pEntity;
   617 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   618 
   619 	if (!pEdict || !pEntity)
   620 	{
   621 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   622 	}
   623 
   624 	int offset = params[2];
   625 	if (offset < 0 || offset > 32768)
   626 	{
   627 		return pContext->ThrowNativeError("Offset %d is invalid", offset);
   628 	}
   629 
   630 	char *src;
   631 	char *dest = (char *)((uint8_t *)pEntity + offset);
   632 
   633 	pContext->LocalToString(params[3], &src);
   634 	size_t len = strncopy(dest, src, params[4]);
   635 
   636 	if (params[5])
   637 	{
   638 		g_HL2.SetEdictStateChanged(pEdict, offset);
   639 	}
   640 
   641 	return len;
   642 }
   643 
   644 static cell_t GetEntPropString(IPluginContext *pContext, const cell_t *params)
   645 {
   646 	CBaseEntity *pEntity;
   647 	char *prop;
   648 	int offset;
   649 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   650 
   651 	if (!pEdict || !pEntity)
   652 	{
   653 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   654 	}
   655 
   656 	switch (params[2])
   657 	{
   658 	case Prop_Data:
   659 		{
   660 			datamap_t *pMap;
   661 			typedescription_t *td;
   662 			if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   663 			{
   664 				return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
   665 			}
   666 			pContext->LocalToString(params[3], &prop);
   667 			if ((td=g_HL2.FindInDataMap(pMap, prop)) == NULL)
   668 			{
   669 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   670 			}
   671 			if (td->fieldType != FIELD_CHARACTER)
   672 			{
   673 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
   674 			}
   675 			offset = td->fieldOffset[TD_OFFSET_NORMAL];
   676 			break;
   677 		}
   678 	case Prop_Send:
   679 		{
   680 			char *prop;
   681 			IServerNetworkable *pNet = pEdict->GetNetworkable();
   682 			if (!pNet)
   683 			{
   684 				return pContext->ThrowNativeError("The edict is not networkable");
   685 			}
   686 			pContext->LocalToString(params[3], &prop);
   687 			SendProp *pSend = g_HL2.FindInSendTable(pNet->GetServerClass()->GetName(), prop);
   688 			if (!pSend)
   689 			{
   690 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   691 			}
   692 			if (pSend->GetType() != DPT_String)
   693 			{
   694 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
   695 			}
   696 			offset = pSend->GetOffset();
   697 			break;
   698 		}
   699 	default:
   700 		{
   701 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
   702 		}
   703 	}
   704 
   705 	size_t len;
   706 	char *src = (char *)((uint8_t *)pEntity + offset);
   707 	pContext->StringToLocalUTF8(params[4], params[5], src, &len);
   708 
   709 	return len;
   710 }
   711 
   712 static cell_t SetEntPropString(IPluginContext *pContext, const cell_t *params)
   713 {
   714 	CBaseEntity *pEntity;
   715 	char *prop;
   716 	int offset;
   717 	int maxlen;
   718 	bool is_sendprop = false;
   719 	edict_t *pEdict = GetEntity(params[1], &pEntity);
   720 
   721 	if (!pEdict || !pEntity)
   722 	{
   723 		return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
   724 	}
   725 
   726 	switch (params[2])
   727 	{
   728 	case Prop_Data:
   729 		{
   730 			datamap_t *pMap;
   731 			typedescription_t *td;
   732 			if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   733 			{
   734 				return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
   735 			}
   736 			pContext->LocalToString(params[3], &prop);
   737 			if ((td=g_HL2.FindInDataMap(pMap, prop)) == NULL)
   738 			{
   739 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   740 			}
   741 			if (td->fieldType != FIELD_CHARACTER)
   742 			{
   743 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
   744 			}
   745 			offset = td->fieldOffset[TD_OFFSET_NORMAL];
   746 			maxlen = td->fieldSize;
   747 			break;
   748 		}
   749 	case Prop_Send:
   750 		{
   751 			char *prop;
   752 			IServerNetworkable *pNet = pEdict->GetNetworkable();
   753 			if (!pNet)
   754 			{
   755 				return pContext->ThrowNativeError("The edict is not networkable");
   756 			}
   757 			pContext->LocalToString(params[3], &prop);
   758 			SendProp *pSend = g_HL2.FindInSendTable(pNet->GetServerClass()->GetName(), prop);
   759 			if (!pSend)
   760 			{
   761 				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   762 			}
   763 			if (pSend->GetType() != DPT_String)
   764 			{
   765 				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
   766 			}
   767 			offset = pSend->GetOffset();
   768 			maxlen = DT_MAX_STRING_BUFFERSIZE;
   769 			is_sendprop = true;
   770 			break;
   771 		}
   772 	default:
   773 		{
   774 			return pContext->ThrowNativeError("Invalid Property type %d", params[2]);
   775 		}
   776 	}
   777 
   778 	char *src;
   779 	char *dest = (char *)((uint8_t *)pEntity + offset);
   780 
   781 	pContext->LocalToString(params[4], &src);
   782 	size_t len = strncopy(dest, src, maxlen);
   783 
   784 	if (is_sendprop)
   785 	{
   786 		g_HL2.SetEdictStateChanged(pEdict, offset);
   787 	}
   788 
   789 	return len;
   790 }
   791 
   792 REGISTER_NATIVES(entityNatives)
   793 {
   794 	{"ChangeEdictState",		ChangeEdictState},
   795 	{"CreateEdict",				CreateEdict},
   796 	{"FindSendPropOffs",		FindSendPropOffs},
   797 	{"GetEdictClassname",		GetEdictClassname},
   798 	{"GetEdictFlags",			GetEdictFlags},
   799 	{"GetEntData",				GetEntData},
   800 	{"GetEntDataEnt",			GetEntDataEnt},
   801 	{"GetEntDataFloat",			GetEntDataFloat},
   802 	{"GetEntDataVector",		GetEntDataVector},
   803 	{"GetEntityCount",			GetEntityCount},
   804 	{"GetEntityNetClass",		GetEntityNetClass},
   805 	{"GetMaxEntities",			GetMaxEntities},
   806 	{"IsEntNetworkable",		IsEntNetworkable},
   807 	{"IsValidEdict",			IsValidEdict},
   808 	{"IsValidEntity",			IsValidEntity},
   809 	{"RemoveEdict",				RemoveEdict},
   810 	{"SetEdictFlags",			SetEdictFlags},
   811 	{"SetEntData",				SetEntData},
   812 	{"SetEntDataEnt",			SetEntDataEnt},
   813 	{"SetEntDataFloat",			SetEntDataFloat},
   814 	{"SetEntDataVector",		SetEntDataVector},
   815 	{"FindDataMapOffs",			FindDataMapOffs},
   816 	{"GetEntDataString",		GetEntDataString},
   817 	{"SetEntDataString",		SetEntDataString},
   818 	{"GetEntPropString",		GetEntPropString},
   819 	{"SetEntPropString",		SetEntPropString},
   820 	{NULL,						NULL}
   821 };