core/smn_entities.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1231 17badaf66ce4
child 1471 dac1c6d68abd
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

(This day shall henceforth be known as the Eighty Column Massacre of '07)
     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 };