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