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