core/HalfLife2.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Tue Jan 22 21:43:12 2013 -0500 (2013-01-22)
changeset 3743 a98686a77974
parent 3695 74cd59113b43
child 3763 336283ba3a38
permissions -rw-r--r--
Added initial support for protobuf usermessages on CS:GO (bug 5579, r=asherkin).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "HalfLife2.h"
    33 #include "sourcemod.h"
    34 #include "sourcemm_api.h"
    35 #include "UserMessages.h"
    36 #include "PlayerManager.h"
    37 #include "sm_stringutil.h"
    38 #include <IGameConfigs.h>
    39 #include <compat_wrappers.h>
    40 #include <Logger.h>
    41 #include "LibrarySys.h"
    42 #include "logic_bridge.h"
    43 #include <tier0/mem.h>
    44 
    45 #if SOURCE_ENGINE == SE_CSGO
    46 #include <cstrike15_usermessages.pb.h>
    47 #endif
    48 
    49 
    50 typedef ICommandLine *(*FakeGetCommandLine)();
    51 
    52 #if defined _WIN32
    53 #define TIER0_NAME			"tier0.dll"
    54 #define VSTDLIB_NAME		"vstdlib.dll"
    55 #elif defined __APPLE__
    56 #define TIER0_NAME			"libtier0.dylib"
    57 #define VSTDLIB_NAME		"libvstdlib.dylib"
    58 #elif defined __linux__
    59 #if SOURCE_ENGINE == SE_ORANGEBOXVALVE
    60 #define TIER0_NAME			"libtier0_srv.so"
    61 #define VSTDLIB_NAME		"libvstdlib_srv.so"
    62 #elif SOURCE_ENGINE >= SE_CSS
    63 #define TIER0_NAME			"libtier0.so"
    64 #define VSTDLIB_NAME		"libvstdlib.so"
    65 #else
    66 #define TIER0_NAME			"tier0_i486.so"
    67 #define VSTDLIB_NAME		"vstdlib_i486.so"
    68 #endif
    69 #endif
    70 
    71 CHalfLife2 g_HL2;
    72 ConVar *sv_lan = NULL;
    73 
    74 static void *g_EntList = NULL;
    75 static int entInfoOffset = -1;
    76 
    77 namespace SourceHook
    78 {
    79 	template<>
    80 	int HashFunction<datamap_t *>(datamap_t * const &k)
    81 	{
    82 		return reinterpret_cast<int>(k);
    83 	}
    84 
    85 	template<>
    86 	int Compare<datamap_t *>(datamap_t * const &k1, datamap_t * const &k2)
    87 	{
    88 		return (k1-k2);
    89 	}
    90 }
    91 
    92 CHalfLife2::CHalfLife2()
    93 {
    94 	m_pClasses = sm_trie_create();
    95 }
    96 
    97 CHalfLife2::~CHalfLife2()
    98 {
    99 	sm_trie_destroy(m_pClasses);
   100 
   101 	List<DataTableInfo *>::iterator iter;
   102 	DataTableInfo *pInfo;
   103 	for (iter=m_Tables.begin(); iter!=m_Tables.end(); iter++)
   104 	{
   105 		pInfo = (*iter);
   106 		delete pInfo;
   107 	}
   108 
   109 	m_Tables.clear();
   110 
   111 	THash<datamap_t *, DataMapTrie>::iterator h_iter;
   112 	for (h_iter=m_Maps.begin(); h_iter!=m_Maps.end(); h_iter++)
   113 	{
   114 		if (h_iter->val.trie)
   115 		{
   116 			sm_trie_destroy(h_iter->val.trie);
   117 			h_iter->val.trie = NULL;
   118 		}
   119 	}
   120 
   121 	m_Maps.clear();
   122 }
   123 
   124 #if SOURCE_ENGINE != SE_DARKMESSIAH
   125 CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
   126 #endif
   127 
   128 #if !defined METAMOD_PLAPI_VERSION || PLAPI_VERSION < 11
   129 bool is_original_engine = false;
   130 #endif
   131 
   132 void CHalfLife2::OnSourceModStartup(bool late)
   133 {
   134 #if SOURCE_ENGINE != SE_DARKMESSIAH
   135 
   136 	/* The Ship currently is the only known game to use an older version of the engine */
   137 #if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11
   138 	if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
   139 #else
   140 	if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
   141 #endif
   142 	{
   143 		is_original_engine = true;
   144 	}
   145 	else if (g_pSharedChangeInfo == NULL)
   146 	{
   147 		g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo();
   148 	}
   149 #endif
   150 }
   151 
   152 void CHalfLife2::OnSourceModAllInitialized()
   153 {
   154 	m_MsgTextMsg = g_UserMsgs.GetMessageIndex("TextMsg");
   155 	m_HinTextMsg = g_UserMsgs.GetMessageIndex("HintText");
   156 	m_SayTextMsg = g_UserMsgs.GetMessageIndex("SayText");
   157 	m_VGUIMenu = g_UserMsgs.GetMessageIndex("VGUIMenu");
   158 	g_ShareSys.AddInterface(NULL, this);
   159 }
   160 
   161 void CHalfLife2::OnSourceModAllInitialized_Post()
   162 {
   163 	InitLogicalEntData();
   164 	InitCommandLine();
   165 }
   166 
   167 void CHalfLife2::InitLogicalEntData()
   168 {
   169 	char *addr = NULL;
   170 
   171 	/*
   172 	 * gEntList and/or g_pEntityList
   173 	 *
   174 	 * First try to lookup pointer directly for platforms with symbols.
   175 	 * If symbols aren't present (Windows or stripped Linux/Mac), 
   176 	 * attempt find via LevelShutdown + offset
   177 	 */
   178 	if (g_pGameConf->GetMemSig("gEntList", (void **)&addr))
   179 	{
   180 #if !defined PLATFORM_WINDOWS
   181 		if (!addr)
   182 		{
   183 			// Key exists so notify if lookup fails, but try other method.
   184 			g_Logger.LogError("Failed lookup of gEntList directly - Reverting to lookup via LevelShutdown");
   185 		}
   186 		else
   187 		{
   188 #endif
   189 			g_EntList = reinterpret_cast<void *>(addr);
   190 #if !defined PLATFORM_WINDOWS
   191 		}
   192 #endif
   193 	}
   194 
   195 
   196 	if (!g_EntList)
   197 	{
   198 		if (!g_pGameConf->GetMemSig("LevelShutdown", (void **)&addr))
   199 		{
   200 			g_Logger.LogError("Logical Entities not supported by this mod (LevelShutdown) - Reverting to networkable entities only");
   201 			return;
   202 		}
   203 
   204 		if (!addr)
   205 		{
   206 			g_Logger.LogError("Failed lookup of LevelShutdown - Reverting to networkable entities only");
   207 			return;
   208 		}
   209 
   210 		int offset;
   211 		if (!g_pGameConf->GetOffset("gEntList", &offset))
   212 		{
   213 			g_Logger.LogError("Logical Entities not supported by this mod (gEntList) - Reverting to networkable entities only");
   214 			return;
   215 		}
   216 
   217 		g_EntList = *reinterpret_cast<void **>(addr + offset);
   218 	}
   219 	
   220 	if (!g_EntList)
   221 	{
   222 		g_Logger.LogError("Failed lookup of gEntList - Reverting to networkable entities only");
   223 		return;
   224 	}
   225 
   226 	if (!g_pGameConf->GetOffset("EntInfo", &entInfoOffset))
   227 	{
   228 		g_Logger.LogError("Logical Entities not supported by this mod (EntInfo) - Reverting to networkable entities only");
   229 		return;
   230 	}
   231 }
   232 
   233 void CHalfLife2::InitCommandLine()
   234 {
   235 	char path[PLATFORM_MAX_PATH];
   236 	char error[256];
   237 	
   238 	g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "../bin/" TIER0_NAME);
   239 
   240 	if (!g_LibSys.IsPathFile(path))
   241 	{
   242 		g_Logger.LogError("Could not find path for: " TIER0_NAME);
   243 		return;
   244 	}
   245 
   246 	ILibrary *lib = g_LibSys.OpenLibrary(path, error, sizeof(error));
   247 	m_pGetCommandLine = lib->GetSymbolAddress("CommandLine_Tier0");
   248 
   249 	/* '_Tier0' dropped on Alien Swarm version */
   250 	if (m_pGetCommandLine == NULL)
   251 	{
   252 		m_pGetCommandLine = lib->GetSymbolAddress("CommandLine");
   253 	}
   254 
   255 	if (m_pGetCommandLine == NULL)
   256 	{
   257 		/* We probably have a Ship engine. */
   258 		lib->CloseLibrary();
   259 		g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "../bin/" VSTDLIB_NAME);
   260 		if (!g_LibSys.IsPathFile(path))
   261 		{
   262 			g_Logger.LogError("Could not find path for: " VSTDLIB_NAME);
   263 			return;
   264 		}
   265 
   266 		if ((lib = g_LibSys.OpenLibrary(path, error, sizeof(error))) == NULL)
   267 		{
   268 			g_Logger.LogError("Could not load %s: %s", path, error);
   269 			return;
   270 		}
   271 
   272 		m_pGetCommandLine = lib->GetSymbolAddress("CommandLine");
   273 
   274 		if (m_pGetCommandLine == NULL)
   275 		{
   276 			g_Logger.LogError("Could not locate any command line functionality");
   277 		}
   278 
   279 		lib->CloseLibrary();
   280 	}
   281 }
   282 
   283 ICommandLine *CHalfLife2::GetValveCommandLine()
   284 {
   285 	if (!m_pGetCommandLine)
   286 		return NULL;
   287 
   288 	return ((FakeGetCommandLine)((FakeGetCommandLine *)m_pGetCommandLine))();
   289 }
   290 
   291 #if !defined METAMOD_PLAPI_VERSION || PLAPI_VERSION < 11
   292 bool CHalfLife2::IsOriginalEngine()
   293 {
   294 	return is_original_engine;
   295 }
   296 #endif
   297 
   298 #if SOURCE_ENGINE != SE_DARKMESSIAH
   299 IChangeInfoAccessor *CBaseEdict::GetChangeAccessor()
   300 {
   301 	return engine->GetChangeAccessor( (const edict_t *)this );
   302 }
   303 #endif
   304 
   305 bool UTIL_FindInSendTable(SendTable *pTable, 
   306 						  const char *name,
   307 						  sm_sendprop_info_t *info,
   308 						  unsigned int offset)
   309 {
   310 	const char *pname;
   311 	int props = pTable->GetNumProps();
   312 	SendProp *prop;
   313 
   314 	for (int i=0; i<props; i++)
   315 	{
   316 		prop = pTable->GetProp(i);
   317 		pname = prop->GetName();
   318 		if (pname && strcmp(name, pname) == 0)
   319 		{
   320 			info->prop = prop;
   321 			info->actual_offset = offset + info->prop->GetOffset();
   322 			return true;
   323 		}
   324 		if (prop->GetDataTable())
   325 		{
   326 			if (UTIL_FindInSendTable(prop->GetDataTable(), 
   327 				name,
   328 				info,
   329 				offset + prop->GetOffset())
   330 				)
   331 			{
   332 				return true;
   333 			}
   334 		}
   335 	}
   336 
   337 	return false;
   338 }
   339 
   340 typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name)
   341 {
   342 	while (pMap)
   343 	{
   344 		for (int i=0; i<pMap->dataNumFields; i++)
   345 		{
   346 			if (pMap->dataDesc[i].fieldName == NULL)
   347 			{
   348 				continue;
   349 			}
   350 			if (strcmp(name, pMap->dataDesc[i].fieldName) == 0)
   351 			{
   352 				return &(pMap->dataDesc[i]);
   353 			}
   354 			if (pMap->dataDesc[i].td)
   355 			{
   356 				typedescription_t *_td;
   357 				if ((_td=UTIL_FindInDataMap(pMap->dataDesc[i].td, name)) != NULL)
   358 				{
   359 					return _td;
   360 				}
   361 			}
   362 		}
   363 		pMap = pMap->baseMap;
   364 	}
   365 
   366 	return NULL; 
   367 }
   368 
   369 ServerClass *CHalfLife2::FindServerClass(const char *classname)
   370 {
   371 	DataTableInfo *pInfo = _FindServerClass(classname);
   372 
   373 	if (!pInfo)
   374 	{
   375 		return NULL;
   376 	}
   377 
   378 	return pInfo->sc;
   379 }
   380 
   381 DataTableInfo *CHalfLife2::_FindServerClass(const char *classname)
   382 {
   383 	DataTableInfo *pInfo = NULL;
   384 
   385 	if (!sm_trie_retrieve(m_pClasses, classname, (void **)&pInfo))
   386 	{
   387 		ServerClass *sc = gamedll->GetAllServerClasses();
   388 		while (sc)
   389 		{
   390 			if (strcmp(classname, sc->GetName()) == 0)
   391 			{
   392 				pInfo = new DataTableInfo;
   393 				pInfo->sc = sc;
   394 				sm_trie_insert(m_pClasses, classname, pInfo);
   395 				m_Tables.push_back(pInfo);
   396 				break;
   397 			}
   398 			sc = sc->m_pNext;
   399 		}
   400 		if (!pInfo)
   401 		{
   402 			return NULL;
   403 		}
   404 	}
   405 
   406 	return pInfo;
   407 }
   408 
   409 bool CHalfLife2::FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info)
   410 {
   411 	DataTableInfo *pInfo;
   412 	sm_sendprop_info_t *prop;
   413 
   414 	if ((pInfo = _FindServerClass(classname)) == NULL)
   415 	{
   416 		return false;
   417 	}
   418 
   419 	if ((prop = pInfo->lookup.retrieve(offset)) == NULL)
   420 	{
   421 		sm_sendprop_info_t temp_info;
   422 
   423 		if (!UTIL_FindInSendTable(pInfo->sc->m_pTable, offset, &temp_info, 0))
   424 		{
   425 			return false;
   426 		}
   427 
   428 		pInfo->lookup.insert(offset, temp_info);
   429 		*info = temp_info;
   430 	}
   431 	else
   432 	{
   433 		*info = *prop;
   434 	}
   435 	
   436 	return true;
   437 }
   438 
   439 SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset)
   440 {
   441 	sm_sendprop_info_t info;
   442 
   443 	if (!FindSendPropInfo(classname, offset, &info))
   444 	{
   445 		return NULL;
   446 	}
   447 
   448 	return info.prop;
   449 }
   450 
   451 typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset)
   452 {
   453 	typedescription_t *td = NULL;
   454 	DataMapTrie &val = m_Maps[pMap];
   455 
   456 	if (!val.trie)
   457 	{
   458 		val.trie = sm_trie_create();
   459 	}
   460 	if (!sm_trie_retrieve(val.trie, offset, (void **)&td))
   461 	{
   462 		if ((td = UTIL_FindInDataMap(pMap, offset)) != NULL)
   463 		{
   464 			sm_trie_insert(val.trie, offset, td);
   465 		}
   466 	}
   467 
   468 	return td;
   469 }
   470 
   471 void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset)
   472 {
   473 #if SOURCE_ENGINE != SE_DARKMESSIAH
   474 	if (g_pSharedChangeInfo != NULL)
   475 	{
   476 		if (offset)
   477 		{
   478 			pEdict->StateChanged(offset);
   479 		}
   480 		else
   481 		{
   482 			pEdict->StateChanged();
   483 		}
   484 	}
   485 	else
   486 #endif
   487 	{
   488 		pEdict->m_fStateFlags |= FL_EDICT_CHANGED;
   489 	}
   490 }
   491 
   492 bool CHalfLife2::TextMsg(int client, int dest, const char *msg)
   493 {
   494 #if SOURCE_ENGINE != SE_CSGO
   495 	bf_write *pBitBuf = NULL;
   496 #endif
   497 	cell_t players[] = {client};
   498 
   499 	if (dest == HUD_PRINTTALK)
   500 	{
   501 		const char *chat_saytext = g_pGameConf->GetKeyValue("ChatSayText");
   502 
   503 		/* Use SayText user message instead */
   504 		if (chat_saytext != NULL && strcmp(chat_saytext, "yes") == 0)
   505 		{
   506 			char buffer[192];
   507 			UTIL_Format(buffer, sizeof(buffer), "%s\1\n", msg);
   508 
   509 #if SOURCE_ENGINE == SE_CSGO
   510 			CCSUsrMsg_SayText *pMsg;
   511 			if ((pMsg = (CCSUsrMsg_SayText *)g_UserMsgs.StartProtobufMessage(m_SayTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   512 			{
   513 				return false;
   514 			}
   515 
   516 			pMsg->set_ent_idx(0);
   517 			pMsg->set_text(buffer);
   518 			pMsg->set_chat(false);
   519 #else
   520 			if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_SayTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   521 			{
   522 				return false;
   523 			}
   524 
   525 			pBitBuf->WriteByte(0);
   526 			pBitBuf->WriteString(buffer);
   527 			pBitBuf->WriteByte(1);
   528 #endif
   529 
   530 			g_UserMsgs.EndMessage();
   531 
   532 			return true;
   533 		}
   534 	}
   535 
   536 #if SOURCE_ENGINE == SE_CSGO
   537 	CCSUsrMsg_TextMsg *pMsg;
   538 	if ((pMsg = (CCSUsrMsg_TextMsg *)g_UserMsgs.StartProtobufMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   539 	{
   540 		return false;
   541 	}
   542 
   543 	// Client tries to read all 5 'params' and will crash if less
   544 	pMsg->set_msg_dst(dest);
   545 	pMsg->add_params(msg);
   546 	pMsg->add_params("");
   547 	pMsg->add_params("");
   548 	pMsg->add_params("");
   549 	pMsg->add_params("");
   550 #else
   551 	if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   552 	{
   553 		return false;
   554 	}
   555 
   556 	pBitBuf->WriteByte(dest);
   557 	pBitBuf->WriteString(msg);
   558 #endif
   559 
   560 	g_UserMsgs.EndMessage();
   561 
   562 	return true;
   563 }
   564 
   565 bool CHalfLife2::HintTextMsg(int client, const char *msg)
   566 {
   567 	cell_t players[] = {client};
   568 
   569 #if SOURCE_ENGINE == SE_CSGO
   570 	CCSUsrMsg_HintText *pMsg;
   571 	if ((pMsg = (CCSUsrMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   572 	{
   573 		return false;
   574 	}
   575 
   576 	pMsg->set_text(msg);
   577 #else
   578 	bf_write *pBitBuf = NULL;
   579 
   580 	if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   581 	{
   582 		return false;
   583 	}
   584 
   585 	const char *pre_byte = g_pGameConf->GetKeyValue("HintTextPreByte");
   586 	if (pre_byte != NULL && strcmp(pre_byte, "yes") == 0)
   587 	{
   588 		pBitBuf->WriteByte(1);
   589 	}
   590 	pBitBuf->WriteString(msg);
   591 #endif
   592 	g_UserMsgs.EndMessage();
   593 
   594 	return true;
   595 }
   596 
   597 bool CHalfLife2::HintTextMsg(cell_t *players, int count, const char *msg)
   598 {
   599 #if SOURCE_ENGINE == SE_CSGO
   600 	CCSUsrMsg_HintText *pMsg;
   601 	if ((pMsg = (CCSUsrMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, count, USERMSG_RELIABLE)) == NULL)
   602 	{
   603 		return false;
   604 	}
   605 
   606 	pMsg->set_text(msg);
   607 #else
   608 	bf_write *pBitBuf = NULL;
   609 
   610 	if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_HinTextMsg, players, count, USERMSG_RELIABLE)) == NULL)
   611 	{
   612 		return false;
   613 	}
   614 
   615 	const char *pre_byte = g_pGameConf->GetKeyValue("HintTextPreByte");
   616 	if (pre_byte != NULL && strcmp(pre_byte, "yes") == 0)
   617 	{
   618 		pBitBuf->WriteByte(1);
   619 	}
   620 	pBitBuf->WriteString(msg);
   621 #endif
   622 
   623 	g_UserMsgs.EndMessage();
   624 
   625 	return true;
   626 }
   627 
   628 bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show)
   629 {
   630 	KeyValues *SubKey = NULL;
   631 	int count = 0;
   632 	cell_t players[] = {client};
   633 
   634 #if SOURCE_ENGINE == SE_CSGO
   635 	CCSUsrMsg_VGUIMenu *pMsg;
   636 	if ((pMsg = (CCSUsrMsg_VGUIMenu *)g_UserMsgs.StartProtobufMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
   637 	{
   638 		return false;
   639 	}
   640 #else
   641 	bf_write *pBitBuf = NULL;
   642 	if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
   643 	{
   644 		return false;
   645 	}
   646 #endif
   647 
   648 	if (data)
   649 	{
   650 		SubKey = data->GetFirstSubKey();
   651 		while (SubKey)
   652 		{
   653 			count++;
   654 			SubKey = SubKey->GetNextKey();
   655 		}
   656 		SubKey = data->GetFirstSubKey();
   657 	}
   658 
   659 #if SOURCE_ENGINE == SE_CSGO
   660 	pMsg->set_name(name);
   661 	pMsg->set_show(show);
   662 
   663 	while (SubKey)
   664 	{
   665 		CCSUsrMsg_VGUIMenu_Subkey *key = pMsg->add_subkeys();
   666 		key->set_name(SubKey->GetName());
   667 		key->set_str(SubKey->GetString());
   668 		SubKey = SubKey->GetNextKey();
   669 	}
   670 #else
   671 	pBitBuf->WriteString(name);
   672 	pBitBuf->WriteByte((show) ? 1 : 0);
   673 	pBitBuf->WriteByte(count);
   674 	while (SubKey)
   675 	{
   676 		pBitBuf->WriteString(SubKey->GetName());
   677 		pBitBuf->WriteString(SubKey->GetString());
   678 		SubKey = SubKey->GetNextKey();
   679 	}
   680 #endif
   681 
   682 	g_UserMsgs.EndMessage();
   683 
   684 	return true;
   685 }
   686 
   687 void CHalfLife2::AddToFakeCliCmdQueue(int client, int userid, const char *cmd)
   688 {
   689 	DelayedFakeCliCmd *pFake;
   690 
   691 	if (m_FreeCmds.empty())
   692 	{
   693 		pFake = new DelayedFakeCliCmd;
   694 	} else {
   695 		pFake = m_FreeCmds.front();
   696 		m_FreeCmds.pop();
   697 	}
   698 
   699 	pFake->client = client;
   700 	pFake->userid = userid;
   701 	pFake->cmd.assign(cmd);
   702 
   703 	m_CmdQueue.push(pFake);
   704 }
   705 
   706 void CHalfLife2::ProcessFakeCliCmdQueue()
   707 {
   708 	while (!m_CmdQueue.empty())
   709 	{
   710 		DelayedFakeCliCmd *pFake = m_CmdQueue.first();
   711 
   712 		if (g_Players.GetClientOfUserId(pFake->userid) == pFake->client)
   713 		{
   714 			CPlayer *pPlayer = g_Players.GetPlayerByIndex(pFake->client);
   715 			serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), pFake->cmd.c_str());
   716 		}
   717 
   718 		m_CmdQueue.pop();
   719 	}
   720 }
   721 
   722 bool CHalfLife2::IsLANServer()
   723 {
   724 	sv_lan = icvar->FindVar("sv_lan");
   725 
   726 	if (!sv_lan)
   727 	{
   728 		return false;
   729 	}
   730 
   731 	return (sv_lan->GetInt() != 0);
   732 }
   733 
   734 bool CHalfLife2::KVLoadFromFile(KeyValues *kv, IBaseFileSystem *filesystem, const char *resourceName, const char *pathID)
   735 {
   736 #if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11
   737 	if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
   738 #else
   739 	if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
   740 #endif
   741 	{
   742 		Assert(filesystem);
   743 #ifdef _MSC_VER
   744 		Assert(_heapchk() == _HEAPOK);
   745 #endif
   746 
   747 		FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
   748 		if (!f)
   749 			return false;
   750 
   751 		// load file into a null-terminated buffer
   752 		int fileSize = filesystem->Size(f);
   753 		char *buffer = (char *)MemAllocScratch(fileSize + 1);
   754 
   755 		Assert(buffer);
   756 
   757 		filesystem->Read(buffer, fileSize, f); // read into local buffer
   758 
   759 		buffer[fileSize] = 0; // null terminate file as EOF
   760 
   761 		filesystem->Close( f );	// close file after reading
   762 
   763 		bool retOK = kv->LoadFromBuffer( resourceName, buffer, filesystem );
   764 
   765 		MemFreeScratch();
   766 
   767 		return retOK;
   768 	}
   769 	else
   770 	{
   771 		return kv->LoadFromFile(filesystem, resourceName, pathID);
   772 	}
   773 }
   774 
   775 void CHalfLife2::PushCommandStack(const CCommand *cmd)
   776 {
   777 	CachedCommandInfo info;
   778 
   779 	info.args = cmd;
   780 #if SOURCE_ENGINE <= SE_DARKMESSIAH
   781 	strncopy(info.cmd, cmd->Arg(0), sizeof(info.cmd));
   782 #endif
   783 
   784 	m_CommandStack.push(info);
   785 }
   786 
   787 const CCommand *CHalfLife2::PeekCommandStack()
   788 {
   789 	if (m_CommandStack.empty())
   790 	{
   791 		return NULL;
   792 	}
   793 
   794 	return m_CommandStack.front().args;
   795 }
   796 
   797 void CHalfLife2::PopCommandStack()
   798 {
   799 	m_CommandStack.pop();
   800 }
   801 
   802 const char *CHalfLife2::CurrentCommandName()
   803 {
   804 #if SOURCE_ENGINE >= SE_ORANGEBOX
   805 	return m_CommandStack.front().args->Arg(0);
   806 #else
   807 	return m_CommandStack.front().cmd;
   808 #endif
   809 }
   810 
   811 void CHalfLife2::AddDelayedKick(int client, int userid, const char *msg)
   812 {
   813 	DelayedKickInfo kick;
   814 
   815 	kick.client = client;
   816 	kick.userid = userid;
   817 	UTIL_Format(kick.buffer, sizeof(kick.buffer), "%s", msg);
   818 
   819 	m_DelayedKicks.push(kick);
   820 }
   821 
   822 void CHalfLife2::ProcessDelayedKicks()
   823 {
   824 	while (!m_DelayedKicks.empty())
   825 	{
   826 		DelayedKickInfo info = m_DelayedKicks.first();
   827 		m_DelayedKicks.pop();
   828 
   829 		CPlayer *player = g_Players.GetPlayerByIndex(info.client);
   830 		if (player == NULL || player->GetUserId() != info.userid)
   831 		{
   832 			continue;
   833 		}
   834 
   835 		player->Kick(info.buffer);
   836 	}
   837 }
   838 
   839 edict_t *CHalfLife2::EdictOfIndex(int index)
   840 {
   841 	return ::PEntityOfEntIndex(index);
   842 }
   843 
   844 int CHalfLife2::IndexOfEdict(edict_t *pEnt)
   845 {
   846 	return ::IndexOfEdict(pEnt);
   847 }
   848 
   849 edict_t *CHalfLife2::GetHandleEntity(CBaseHandle &hndl)
   850 {
   851 	if (!hndl.IsValid())
   852 	{
   853 		return NULL;
   854 	}
   855 
   856 	int index = hndl.GetEntryIndex();
   857 
   858 	edict_t *pStoredEdict;
   859 	CBaseEntity *pStoredEntity;
   860 
   861 	if (!IndexToAThings(index, &pStoredEntity, &pStoredEdict))
   862 	{
   863 		return NULL;
   864 	}
   865 
   866 	if (pStoredEdict == NULL || pStoredEntity == NULL)
   867 	{
   868 		return NULL;
   869 	}
   870 
   871 	IServerEntity *pSE = pStoredEdict->GetIServerEntity();
   872 
   873 	if (pSE == NULL)
   874 	{
   875 		return NULL;
   876 	}
   877 
   878 	if (pSE->GetRefEHandle() != hndl)
   879 	{
   880 		return NULL;
   881 	}
   882 
   883 	return pStoredEdict;
   884 }
   885 
   886 void CHalfLife2::SetHandleEntity(CBaseHandle &hndl, edict_t *pEnt)
   887 {
   888 	IServerEntity *pEntOther = pEnt->GetIServerEntity();
   889 
   890 	if (pEntOther == NULL)
   891 	{
   892 		return;
   893 	}
   894 
   895 	hndl.Set(pEntOther);
   896 }
   897 
   898 const char *CHalfLife2::GetCurrentMap()
   899 {
   900 	return STRING(gpGlobals->mapname);
   901 }
   902 
   903 void CHalfLife2::ServerCommand(const char *buffer)
   904 {
   905 	engine->ServerCommand(buffer);
   906 }
   907 
   908 cell_t CHalfLife2::EntityToReference(CBaseEntity *pEntity)
   909 {
   910 	IServerUnknown *pUnknown = (IServerUnknown *)pEntity;
   911 	CBaseHandle hndl = pUnknown->GetRefEHandle();
   912 	return (hndl.ToInt() | (1<<31));
   913 }
   914 
   915 CBaseEntity *CHalfLife2::ReferenceToEntity(cell_t entRef)
   916 {
   917 	if ((unsigned)entRef == INVALID_EHANDLE_INDEX)
   918 	{
   919 		return NULL;
   920 	}
   921 
   922 	CEntInfo *pInfo = NULL;
   923 
   924 	if (entRef & (1<<31))
   925 	{
   926 		/* Proper ent reference */
   927 		int hndlValue = entRef & ~(1<<31);
   928 		CBaseHandle hndl(hndlValue);
   929 
   930 		pInfo = LookupEntity(hndl.GetEntryIndex());
   931 		if (!pInfo || pInfo->m_SerialNumber != hndl.GetSerialNumber())
   932 		{
   933 			return NULL;
   934 		}
   935 	}
   936 	else
   937 	{
   938 		/* Old style index only */
   939 		pInfo = LookupEntity(entRef);
   940 	}
   941 
   942 	if (!pInfo)
   943 	{
   944 		return NULL;
   945 	}
   946 
   947 	IServerUnknown *pUnk = static_cast<IServerUnknown *>(pInfo->m_pEntity);
   948 	if (pUnk)
   949 	{
   950 		return pUnk->GetBaseEntity();
   951 	}
   952 
   953 	return NULL;
   954 }
   955 
   956 /**
   957  * Retrieves the CEntInfo pointer from g_EntList for a given entity index
   958  */
   959 CEntInfo *CHalfLife2::LookupEntity(int entIndex)
   960 {
   961 	// Make sure that our index is within the bounds of the global ent array
   962 	if (entIndex < 0 || entIndex >= NUM_ENT_ENTRIES)
   963 	{
   964 		return NULL;
   965 	}
   966 
   967 	if (!g_EntList || entInfoOffset == -1)
   968 	{
   969 		/* Attempt to use engine interface instead */
   970 		static CEntInfo tempInfo;
   971 		tempInfo.m_pNext = NULL;
   972 		tempInfo.m_pPrev = NULL;
   973 
   974 		edict_t *pEdict = PEntityOfEntIndex(entIndex);
   975 
   976 		if (!pEdict)
   977 		{
   978 			return NULL;
   979 		}
   980 
   981 		IServerUnknown *pUnk = pEdict->GetUnknown();
   982 
   983 		if (!pUnk)
   984 		{
   985 			return NULL;
   986 		}
   987 
   988 		tempInfo.m_pEntity = pUnk;
   989 		tempInfo.m_SerialNumber = pUnk->GetRefEHandle().GetSerialNumber();
   990 
   991 		return &tempInfo;
   992 	}
   993 
   994 	CEntInfo *pArray = (CEntInfo *)(((unsigned char *)g_EntList) + entInfoOffset);
   995 	return &pArray[entIndex];
   996 }
   997 
   998 /**
   999 	SERIAL_MASK = 0x7fff (15 bits)
  1000 
  1001 	#define	MAX_EDICT_BITS				11
  1002 	#define NUM_ENT_ENTRY_BITS		(MAX_EDICT_BITS + 1)
  1003 	m_Index = iEntry | (iSerialNumber << NUM_ENT_ENTRY_BITS); 
  1004 	
  1005 	Top 5 bits of a handle are unused.
  1006 	Bit 31 - Our 'reference' flag indicator
  1007 */
  1008 
  1009 cell_t CHalfLife2::IndexToReference(int entIndex)
  1010 {
  1011 	CBaseEntity *pEnt = ReferenceToEntity(entIndex);
  1012 	if (!pEnt)
  1013 	{
  1014 		return INVALID_EHANDLE_INDEX;
  1015 	}
  1016 
  1017 	return EntityToReference(pEnt);
  1018 }
  1019 
  1020 int CHalfLife2::ReferenceToIndex(cell_t entRef)
  1021 {
  1022 	if ((unsigned)entRef == INVALID_EHANDLE_INDEX)
  1023 	{
  1024 		return INVALID_EHANDLE_INDEX;
  1025 	}
  1026 
  1027 	if (entRef & (1<<31))
  1028 	{
  1029 		/* Proper ent reference */
  1030 		int hndlValue = entRef & ~(1<<31);
  1031 		CBaseHandle hndl(hndlValue);
  1032 
  1033 		CEntInfo *pInfo = LookupEntity(hndl.GetEntryIndex());
  1034 
  1035 		if (pInfo->m_SerialNumber != hndl.GetSerialNumber())
  1036 		{
  1037 			return INVALID_EHANDLE_INDEX;
  1038 		}
  1039 
  1040 		return hndl.GetEntryIndex();
  1041 	}
  1042 
  1043 	return entRef;
  1044 }
  1045 
  1046 cell_t CHalfLife2::EntityToBCompatRef(CBaseEntity *pEntity)
  1047 {
  1048 	if (pEntity == NULL)
  1049 	{
  1050 		return INVALID_EHANDLE_INDEX;
  1051 	}
  1052 
  1053 	IServerUnknown *pUnknown = (IServerUnknown *)pEntity;
  1054 	CBaseHandle hndl = pUnknown->GetRefEHandle();
  1055 
  1056 	if (hndl == INVALID_EHANDLE_INDEX)
  1057 	{
  1058 		return INVALID_EHANDLE_INDEX;
  1059 	}
  1060 	
  1061 	if (hndl.GetEntryIndex() >= MAX_EDICTS)
  1062 	{
  1063 		return (hndl.ToInt() | (1<<31));
  1064 	}
  1065 	else
  1066 	{
  1067 		return hndl.GetEntryIndex();
  1068 	}
  1069 }
  1070 
  1071 cell_t CHalfLife2::ReferenceToBCompatRef(cell_t entRef)
  1072 {
  1073 	if ((unsigned)entRef == INVALID_EHANDLE_INDEX)
  1074 	{
  1075 		return INVALID_EHANDLE_INDEX;
  1076 	}
  1077 
  1078 	int hndlValue = entRef & ~(1<<31);
  1079 	CBaseHandle hndl(hndlValue);
  1080 
  1081 	if (hndl.GetEntryIndex() < MAX_EDICTS)
  1082 	{
  1083 		return hndl.GetEntryIndex();
  1084 	}
  1085 
  1086 	return entRef;
  1087 }
  1088 
  1089 void *CHalfLife2::GetGlobalEntityList()
  1090 {
  1091 	return g_EntList;
  1092 }
  1093 
  1094 int CHalfLife2::GetSendPropOffset(SendProp *prop)
  1095 {
  1096 	return prop->GetOffset();
  1097 }
  1098 
  1099 const char *CHalfLife2::GetEntityClassname(edict_t * pEdict)
  1100 {
  1101 	if (pEdict == NULL || pEdict->IsFree())
  1102 	{
  1103 		return NULL;
  1104 	}
  1105 	
  1106 	IServerUnknown *pUnk = pEdict->GetUnknown();
  1107 	if (pUnk == NULL)
  1108 	{
  1109 		return NULL;
  1110 	}
  1111 	
  1112 	CBaseEntity * pEntity = pUnk->GetBaseEntity();
  1113 	
  1114 	if (pEntity == NULL)
  1115 	{
  1116 		return NULL;
  1117 	}
  1118 	
  1119 	return GetEntityClassname(pEntity);
  1120 }
  1121 
  1122 const char *CHalfLife2::GetEntityClassname(CBaseEntity *pEntity)
  1123 {
  1124 	static int offset = -1;
  1125 	if (offset == -1)
  1126 	{
  1127 		datamap_t *pMap = GetDataMap(pEntity);
  1128 		typedescription_t *pDesc = FindInDataMap(pMap, "m_iClassname");
  1129 		offset = GetTypeDescOffs(pDesc);
  1130 	}
  1131 
  1132 	return *(const char **)(((unsigned char *)pEntity) + offset);
  1133 }