core/HalfLife2.cpp
author Peace-Maker <januk@freenet.de>
Sat May 10 05:42:33 2014 -0700 (2014-05-10)
changeset 4354 7011587e0021
parent 4322 74cb2e0ab2ae
permissions -rw-r--r--
Remove LogMessageEx definition from logging.inc (bug 5897, r=kyles).
     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 \
    62 	|| SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_LEFT4DEAD2 || SOURCE_ENGINE == SE_NUCLEARDAWN
    63 #define TIER0_NAME			"libtier0_srv.so"
    64 #define VSTDLIB_NAME		"libvstdlib_srv.so"
    65 #elif SOURCE_ENGINE >= SE_LEFT4DEAD
    66 #define TIER0_NAME			"libtier0.so"
    67 #define VSTDLIB_NAME		"libvstdlib.so"
    68 #else
    69 #define TIER0_NAME			"tier0_i486.so"
    70 #define VSTDLIB_NAME		"vstdlib_i486.so"
    71 #endif
    72 #endif
    73 
    74 CHalfLife2 g_HL2;
    75 ConVar *sv_lan = NULL;
    76 
    77 static void *g_EntList = NULL;
    78 static void **g_pEntInfoList = NULL;
    79 static int entInfoOffset = -1;
    80 
    81 static CEntInfo *EntInfoArray()
    82 {
    83 	if (g_EntList != NULL)
    84 	{
    85 		return (CEntInfo *)((intp)g_EntList + entInfoOffset);
    86 	}
    87 	else if (g_pEntInfoList)
    88 	{
    89 		return *(CEntInfo **)g_pEntInfoList;
    90 	}
    91 	
    92 	return NULL;
    93 }
    94 
    95 CHalfLife2::CHalfLife2()
    96 {
    97 	m_Maps.init();
    98 
    99 	m_pGetCommandLine = NULL;
   100 }
   101 
   102 CHalfLife2::~CHalfLife2()
   103 {
   104 	for (NameHashSet<DataTableInfo *>::iterator iter = m_Classes.iter(); !iter.empty(); iter.next())
   105 		delete *iter;
   106 
   107 	for (DataTableMap::iterator iter = m_Maps.iter(); !iter.empty(); iter.next())
   108 		delete iter->value;
   109 }
   110 
   111 #if SOURCE_ENGINE != SE_DARKMESSIAH
   112 CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
   113 #endif
   114 
   115 #if !defined METAMOD_PLAPI_VERSION || PLAPI_VERSION < 11
   116 bool is_original_engine = false;
   117 #endif
   118 
   119 void CHalfLife2::OnSourceModStartup(bool late)
   120 {
   121 #if SOURCE_ENGINE != SE_DARKMESSIAH
   122 
   123 	/* The Ship currently is the only known game to use an older version of the engine */
   124 #if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11
   125 	if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
   126 #else
   127 	if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
   128 #endif
   129 	{
   130 		is_original_engine = true;
   131 	}
   132 	else if (g_pSharedChangeInfo == NULL)
   133 	{
   134 		g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo();
   135 	}
   136 #endif
   137 }
   138 
   139 void CHalfLife2::OnSourceModAllInitialized()
   140 {
   141 	m_MsgTextMsg = g_UserMsgs.GetMessageIndex("TextMsg");
   142 	m_HinTextMsg = g_UserMsgs.GetMessageIndex("HintText");
   143 	m_SayTextMsg = g_UserMsgs.GetMessageIndex("SayText");
   144 	m_VGUIMenu = g_UserMsgs.GetMessageIndex("VGUIMenu");
   145 	sharesys->AddInterface(NULL, this);
   146 }
   147 
   148 void CHalfLife2::OnSourceModAllInitialized_Post()
   149 {
   150 	InitLogicalEntData();
   151 	InitCommandLine();
   152 }
   153 
   154 void CHalfLife2::InitLogicalEntData()
   155 {
   156 	char *addr = NULL;
   157 
   158 	/*
   159 	 * gEntList and/or g_pEntityList
   160 	 *
   161 	 * First try to lookup pointer directly for platforms with symbols.
   162 	 * If symbols aren't present (Windows or stripped Linux/Mac), 
   163 	 * attempt find via LevelShutdown + offset
   164 	 */
   165 	if (g_pGameConf->GetMemSig("gEntList", (void **)&addr))
   166 	{
   167 #if !defined PLATFORM_WINDOWS
   168 		if (!addr)
   169 		{
   170 			// Key exists so notify if lookup fails, but try other method.
   171 			g_Logger.LogError("Failed lookup of gEntList directly - Reverting to lookup via LevelShutdown");
   172 		}
   173 		else
   174 		{
   175 #endif
   176 			g_EntList = reinterpret_cast<void *>(addr);
   177 #if !defined PLATFORM_WINDOWS
   178 		}
   179 #endif
   180 	}
   181 
   182 	if (!g_EntList)
   183 	{
   184 		if (g_pGameConf->GetMemSig("LevelShutdown", (void **) &addr) && addr)
   185 		{
   186 			int offset;
   187 			if (!g_pGameConf->GetOffset("gEntList", &offset))
   188 			{
   189 				g_Logger.LogError("Logical Entities not supported by this mod (gEntList) - Reverting to networkable entities only");
   190 				return;
   191 			}
   192 
   193 			g_EntList = *reinterpret_cast<void **>(addr + offset);
   194 
   195 		}
   196 	}
   197 
   198 	// If we have g_EntList from either of the above methods, make sure we can get the offset from it to EntInfo as well
   199 	if (g_EntList && !g_pGameConf->GetOffset("EntInfo", &entInfoOffset))
   200 	{
   201 		g_Logger.LogError("Logical Entities not supported by this mod (EntInfo) - Reverting to networkable entities only");
   202 		g_EntList = NULL;
   203 		return;
   204 	}
   205 
   206 	// If we don't have g_EntList or have it but don't know where EntInfo is on it, use fallback.
   207 	if (!g_EntList || entInfoOffset == -1)
   208 	{
   209 		g_pGameConf->GetAddress("EntInfosPtr", (void **)&g_pEntInfoList);
   210 	}
   211 	
   212 	if (!g_EntList && !g_pEntInfoList)
   213 	{
   214 		g_Logger.LogError("Failed lookup of gEntList - Reverting to networkable entities only");
   215 		return;
   216 	}
   217 }
   218 
   219 void CHalfLife2::InitCommandLine()
   220 {
   221 	char error[256];
   222 
   223 	if (!is_original_engine)
   224 	{
   225 		ke::AutoPtr<ILibrary> lib(g_LibSys.OpenLibrary(TIER0_NAME, error, sizeof(error)));
   226 		if (lib == NULL)
   227 		{
   228 			g_Logger.LogError("Could not load %s: %s", TIER0_NAME, error);
   229 			return;
   230 		}
   231 		
   232 		m_pGetCommandLine = lib->GetSymbolAddress("CommandLine_Tier0");
   233 
   234 		/* '_Tier0' dropped on Alien Swarm version */
   235 		if (m_pGetCommandLine == NULL)
   236 		{
   237 			m_pGetCommandLine = lib->GetSymbolAddress("CommandLine");
   238 		}
   239 	}
   240 	else
   241 	{
   242 		ke::AutoPtr<ILibrary> lib(g_LibSys.OpenLibrary(VSTDLIB_NAME, error, sizeof(error)));
   243 		if (lib == NULL)
   244 		{
   245 			g_Logger.LogError("Could not load %s: %s", VSTDLIB_NAME, error);
   246 			return;
   247 		}
   248 
   249 		m_pGetCommandLine = lib->GetSymbolAddress("CommandLine");
   250 	}
   251 	
   252 	if (m_pGetCommandLine == NULL)
   253 	{
   254 		g_Logger.LogError("Could not locate any command line functionality");
   255 	}
   256 }
   257 
   258 ICommandLine *CHalfLife2::GetValveCommandLine()
   259 {
   260 	if (!m_pGetCommandLine)
   261 		return NULL;
   262 
   263 	return ((FakeGetCommandLine)((FakeGetCommandLine *)m_pGetCommandLine))();
   264 }
   265 
   266 #if !defined METAMOD_PLAPI_VERSION || PLAPI_VERSION < 11
   267 bool CHalfLife2::IsOriginalEngine()
   268 {
   269 	return is_original_engine;
   270 }
   271 #endif
   272 
   273 #if SOURCE_ENGINE != SE_DARKMESSIAH
   274 IChangeInfoAccessor *CBaseEdict::GetChangeAccessor()
   275 {
   276 #if SOURCE_ENGINE == SE_DOTA
   277 	return engine->GetChangeAccessor( IndexOfEdict((const edict_t *)this) );
   278 #else
   279 	return engine->GetChangeAccessor( (const edict_t *)this );
   280 #endif
   281 }
   282 #endif
   283 
   284 bool UTIL_FindInSendTable(SendTable *pTable, 
   285 						  const char *name,
   286 						  sm_sendprop_info_t *info,
   287 						  unsigned int offset)
   288 {
   289 	const char *pname;
   290 	int props = pTable->GetNumProps();
   291 	SendProp *prop;
   292 
   293 	for (int i=0; i<props; i++)
   294 	{
   295 		prop = pTable->GetProp(i);
   296 		pname = prop->GetName();
   297 		if (pname && strcmp(name, pname) == 0)
   298 		{
   299 			info->prop = prop;
   300 			info->actual_offset = offset + info->prop->GetOffset();
   301 			return true;
   302 		}
   303 		if (prop->GetDataTable())
   304 		{
   305 			if (UTIL_FindInSendTable(prop->GetDataTable(), 
   306 				name,
   307 				info,
   308 				offset + prop->GetOffset())
   309 				)
   310 			{
   311 				return true;
   312 			}
   313 		}
   314 	}
   315 
   316 	return false;
   317 }
   318 
   319 bool UTIL_FindDataMapInfo(datamap_t *pMap, const char *name, sm_datatable_info_t *pDataTable)
   320 {
   321 	while (pMap)
   322 	{
   323 		for (int i = 0; i < pMap->dataNumFields; ++i)
   324 		{
   325 			if (pMap->dataDesc[i].fieldName == NULL)
   326 			{
   327 				continue;
   328 			}
   329 			if (strcmp(name, pMap->dataDesc[i].fieldName) == 0)
   330 			{
   331 				pDataTable->prop = &(pMap->dataDesc[i]);
   332 				pDataTable->actual_offset = GetTypeDescOffs(pDataTable->prop);
   333 				return true;
   334 			}
   335 			if (pMap->dataDesc[i].td == NULL || !UTIL_FindDataMapInfo(pMap->dataDesc[i].td, name, pDataTable))
   336 			{
   337 				continue;
   338 			}
   339 			
   340 			pDataTable->actual_offset += GetTypeDescOffs(&(pMap->dataDesc[i]));
   341 			return true;
   342 		}
   343 		
   344 		pMap = pMap->baseMap;
   345 	}
   346 
   347 	return false; 
   348 }
   349 
   350 ServerClass *CHalfLife2::FindServerClass(const char *classname)
   351 {
   352 	DataTableInfo *pInfo = _FindServerClass(classname);
   353 
   354 	if (!pInfo)
   355 		return NULL;
   356 
   357 	return pInfo->sc;
   358 }
   359 
   360 DataTableInfo *CHalfLife2::_FindServerClass(const char *classname)
   361 {
   362 	DataTableInfo *pInfo = NULL;
   363 	if (!m_Classes.retrieve(classname, &pInfo))
   364 	{
   365 		ServerClass *sc = gamedll->GetAllServerClasses();
   366 		while (sc)
   367 		{
   368 			if (strcmp(classname, sc->GetName()) == 0)
   369 			{
   370 				pInfo = new DataTableInfo(sc);
   371 				m_Classes.insert(classname, pInfo);
   372 				break;
   373 			}
   374 			sc = sc->m_pNext;
   375 		}
   376 		if (!pInfo)
   377 			return NULL;
   378 	}
   379 
   380 	return pInfo;
   381 }
   382 
   383 bool CHalfLife2::FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info)
   384 {
   385 	DataTableInfo *pInfo;
   386 
   387 	if ((pInfo = _FindServerClass(classname)) == NULL)
   388 	{
   389 		return false;
   390 	}
   391 
   392 	if (!pInfo->lookup.retrieve(offset, info))
   393 	{
   394 		sm_sendprop_info_t temp_info;
   395 
   396 		if (!UTIL_FindInSendTable(pInfo->sc->m_pTable, offset, &temp_info, 0))
   397 		{
   398 			return false;
   399 		}
   400 
   401 		pInfo->lookup.insert(offset, temp_info);
   402 		*info = temp_info;
   403 	}
   404 	
   405 	return true;
   406 }
   407 
   408 SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset)
   409 {
   410 	sm_sendprop_info_t info;
   411 
   412 	if (!FindSendPropInfo(classname, offset, &info))
   413 	{
   414 		return NULL;
   415 	}
   416 
   417 	return info.prop;
   418 }
   419 
   420 typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset)
   421 {
   422 	sm_datatable_info_t dt_info;
   423 	
   424 	if (!(this->FindDataMapInfo(pMap, offset, &dt_info)))
   425 	{
   426 		return NULL;
   427 	}
   428 	
   429 	return dt_info.prop;
   430 }
   431 
   432 bool CHalfLife2::FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatable_info_t *pDataTable)
   433 {
   434 	DataTableMap::Insert i = m_Maps.findForAdd(pMap);
   435 	if (!i.found())
   436 		m_Maps.add(i, pMap, new DataMapCache());
   437 
   438 	DataMapCache *cache = i->value;
   439 
   440 	if (!cache->retrieve(offset, pDataTable))
   441 	{
   442 		if (!UTIL_FindDataMapInfo(pMap, offset, pDataTable))
   443 			return false;
   444 		cache->insert(offset, *pDataTable);
   445 	}
   446 
   447 	return true;
   448 }
   449 
   450 void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset)
   451 {
   452 #if SOURCE_ENGINE != SE_DARKMESSIAH
   453 	if (g_pSharedChangeInfo != NULL)
   454 	{
   455 		if (offset)
   456 		{
   457 			pEdict->StateChanged(offset);
   458 		}
   459 		else
   460 		{
   461 			pEdict->StateChanged();
   462 		}
   463 	}
   464 	else
   465 #endif
   466 	{
   467 		pEdict->m_fStateFlags |= FL_EDICT_CHANGED;
   468 	}
   469 }
   470 
   471 bool CHalfLife2::TextMsg(int client, int dest, const char *msg)
   472 {
   473 #ifndef USE_PROTOBUF_USERMESSAGES
   474 	bf_write *pBitBuf = NULL;
   475 #endif
   476 	cell_t players[] = {client};
   477 
   478 	if (dest == HUD_PRINTTALK)
   479 	{
   480 		const char *chat_saytext = g_pGameConf->GetKeyValue("ChatSayText");
   481 
   482 		/* Use SayText user message instead */
   483 		if (chat_saytext != NULL && strcmp(chat_saytext, "yes") == 0)
   484 		{
   485 			char buffer[192];
   486 			UTIL_Format(buffer, sizeof(buffer), "%s\1\n", msg);
   487 
   488 #if SOURCE_ENGINE == SE_DOTA
   489 			CUserMsg_SayText *pMsg;
   490 			if ((pMsg = (CUserMsg_SayText *)g_UserMsgs.StartProtobufMessage(m_SayTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   491 			{
   492 				return false;
   493 			}
   494 
   495 			pMsg->set_client(0);
   496 			pMsg->set_text(buffer);
   497 			pMsg->set_chat(false);
   498 #elif SOURCE_ENGINE == SE_CSGO
   499 			CCSUsrMsg_SayText *pMsg;
   500 			if ((pMsg = (CCSUsrMsg_SayText *)g_UserMsgs.StartProtobufMessage(m_SayTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   501 			{
   502 				return false;
   503 			}
   504 
   505 			pMsg->set_ent_idx(0);
   506 			pMsg->set_text(buffer);
   507 			pMsg->set_chat(false);
   508 #else
   509 			if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_SayTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   510 			{
   511 				return false;
   512 			}
   513 
   514 			pBitBuf->WriteByte(0);
   515 			pBitBuf->WriteString(buffer);
   516 			pBitBuf->WriteByte(1);
   517 #endif
   518 
   519 			g_UserMsgs.EndMessage();
   520 
   521 			return true;
   522 		}
   523 	}
   524 
   525 #if SOURCE_ENGINE == SE_DOTA
   526 	CUserMsg_TextMsg *pMsg;
   527 	if ((pMsg = (CUserMsg_TextMsg *)g_UserMsgs.StartProtobufMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   528 	{
   529 		return false;
   530 	}
   531 
   532 	pMsg->set_dest(dest);
   533 	pMsg->add_param(msg);
   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 		m_FreeCmds.push(pFake);
   755 	}
   756 }
   757 
   758 bool CHalfLife2::IsLANServer()
   759 {
   760 	sv_lan = icvar->FindVar("sv_lan");
   761 
   762 	if (!sv_lan)
   763 	{
   764 		return false;
   765 	}
   766 
   767 	return (sv_lan->GetInt() != 0);
   768 }
   769 
   770 bool CHalfLife2::KVLoadFromFile(KeyValues *kv, IBaseFileSystem *filesystem, const char *resourceName, const char *pathID)
   771 {
   772 #if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11
   773 	if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
   774 #else
   775 	if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
   776 #endif
   777 	{
   778 		Assert(filesystem);
   779 #ifdef _MSC_VER
   780 		Assert(_heapchk() == _HEAPOK);
   781 #endif
   782 
   783 		FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
   784 		if (!f)
   785 			return false;
   786 
   787 		// load file into a null-terminated buffer
   788 		int fileSize = filesystem->Size(f);
   789 		char *buffer = (char *)MemAllocScratch(fileSize + 1);
   790 
   791 		Assert(buffer);
   792 
   793 		filesystem->Read(buffer, fileSize, f); // read into local buffer
   794 
   795 		buffer[fileSize] = 0; // null terminate file as EOF
   796 
   797 		filesystem->Close( f );	// close file after reading
   798 
   799 		bool retOK = kv->LoadFromBuffer( resourceName, buffer, filesystem );
   800 
   801 		MemFreeScratch();
   802 
   803 		return retOK;
   804 	}
   805 	else
   806 	{
   807 		return kv->LoadFromFile(filesystem, resourceName, pathID);
   808 	}
   809 }
   810 
   811 void CHalfLife2::PushCommandStack(const CCommand *cmd)
   812 {
   813 	CachedCommandInfo info;
   814 
   815 	info.args = cmd;
   816 #if SOURCE_ENGINE <= SE_DARKMESSIAH
   817 	strncopy(info.cmd, cmd->Arg(0), sizeof(info.cmd));
   818 #endif
   819 
   820 	m_CommandStack.push(info);
   821 }
   822 
   823 const CCommand *CHalfLife2::PeekCommandStack()
   824 {
   825 	if (m_CommandStack.empty())
   826 	{
   827 		return NULL;
   828 	}
   829 
   830 	return m_CommandStack.front().args;
   831 }
   832 
   833 void CHalfLife2::PopCommandStack()
   834 {
   835 	m_CommandStack.pop();
   836 }
   837 
   838 const char *CHalfLife2::CurrentCommandName()
   839 {
   840 #if SOURCE_ENGINE >= SE_ORANGEBOX
   841 	return m_CommandStack.front().args->Arg(0);
   842 #else
   843 	return m_CommandStack.front().cmd;
   844 #endif
   845 }
   846 
   847 void CHalfLife2::AddDelayedKick(int client, int userid, const char *msg)
   848 {
   849 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   850 	if (!pPlayer || !pPlayer->IsConnected() || pPlayer->IsInKickQueue())
   851 	{
   852 		return;
   853 	}
   854 	
   855 	pPlayer->MarkAsBeingKicked();
   856 
   857 	DelayedKickInfo kick;
   858 
   859 	kick.client = client;
   860 	kick.userid = userid;
   861 	UTIL_Format(kick.buffer, sizeof(kick.buffer), "%s", msg);
   862 
   863 	m_DelayedKicks.push(kick);
   864 }
   865 
   866 void CHalfLife2::ProcessDelayedKicks()
   867 {
   868 	while (!m_DelayedKicks.empty())
   869 	{
   870 		DelayedKickInfo info = m_DelayedKicks.first();
   871 		m_DelayedKicks.pop();
   872 
   873 		CPlayer *player = g_Players.GetPlayerByIndex(info.client);
   874 		if (player == NULL || player->GetUserId() != info.userid)
   875 		{
   876 			continue;
   877 		}
   878 
   879 		player->Kick(info.buffer);
   880 	}
   881 }
   882 
   883 edict_t *CHalfLife2::EdictOfIndex(int index)
   884 {
   885 	return ::PEntityOfEntIndex(index);
   886 }
   887 
   888 int CHalfLife2::IndexOfEdict(edict_t *pEnt)
   889 {
   890 	return ::IndexOfEdict(pEnt);
   891 }
   892 
   893 edict_t *CHalfLife2::GetHandleEntity(CBaseHandle &hndl)
   894 {
   895 	if (!hndl.IsValid())
   896 	{
   897 		return NULL;
   898 	}
   899 
   900 	int index = hndl.GetEntryIndex();
   901 
   902 	edict_t *pStoredEdict;
   903 	CBaseEntity *pStoredEntity;
   904 
   905 	if (!IndexToAThings(index, &pStoredEntity, &pStoredEdict))
   906 	{
   907 		return NULL;
   908 	}
   909 
   910 	if (pStoredEdict == NULL || pStoredEntity == NULL)
   911 	{
   912 		return NULL;
   913 	}
   914 
   915 	IServerEntity *pSE = pStoredEdict->GetIServerEntity();
   916 
   917 	if (pSE == NULL)
   918 	{
   919 		return NULL;
   920 	}
   921 
   922 	if (pSE->GetRefEHandle() != hndl)
   923 	{
   924 		return NULL;
   925 	}
   926 
   927 	return pStoredEdict;
   928 }
   929 
   930 void CHalfLife2::SetHandleEntity(CBaseHandle &hndl, edict_t *pEnt)
   931 {
   932 	IServerEntity *pEntOther = pEnt->GetIServerEntity();
   933 
   934 	if (pEntOther == NULL)
   935 	{
   936 		return;
   937 	}
   938 
   939 	hndl.Set(pEntOther);
   940 }
   941 
   942 const char *CHalfLife2::GetCurrentMap()
   943 {
   944 	return STRING(gpGlobals->mapname);
   945 }
   946 
   947 void CHalfLife2::ServerCommand(const char *buffer)
   948 {
   949 	engine->ServerCommand(buffer);
   950 }
   951 
   952 cell_t CHalfLife2::EntityToReference(CBaseEntity *pEntity)
   953 {
   954 	IServerUnknown *pUnknown = (IServerUnknown *)pEntity;
   955 	CBaseHandle hndl = pUnknown->GetRefEHandle();
   956 	return (hndl.ToInt() | (1<<31));
   957 }
   958 
   959 CBaseEntity *CHalfLife2::ReferenceToEntity(cell_t entRef)
   960 {
   961 	if ((unsigned)entRef == INVALID_EHANDLE_INDEX)
   962 	{
   963 		return NULL;
   964 	}
   965 
   966 	CEntInfo *pInfo = NULL;
   967 
   968 	if (entRef & (1<<31))
   969 	{
   970 		/* Proper ent reference */
   971 		int hndlValue = entRef & ~(1<<31);
   972 		CBaseHandle hndl(hndlValue);
   973 
   974 		pInfo = LookupEntity(hndl.GetEntryIndex());
   975 		if (!pInfo || pInfo->m_SerialNumber != hndl.GetSerialNumber())
   976 		{
   977 			return NULL;
   978 		}
   979 	}
   980 	else
   981 	{
   982 		/* Old style index only */
   983 		pInfo = LookupEntity(entRef);
   984 	}
   985 
   986 	if (!pInfo)
   987 	{
   988 		return NULL;
   989 	}
   990 
   991 	IServerUnknown *pUnk = static_cast<IServerUnknown *>(pInfo->m_pEntity);
   992 	if (pUnk)
   993 	{
   994 		return pUnk->GetBaseEntity();
   995 	}
   996 
   997 	return NULL;
   998 }
   999 
  1000 /**
  1001  * Retrieves the CEntInfo pointer from g_EntList for a given entity index
  1002  */
  1003 CEntInfo *CHalfLife2::LookupEntity(int entIndex)
  1004 {
  1005 	// Make sure that our index is within the bounds of the global ent array
  1006 	if (entIndex < 0 || entIndex >= NUM_ENT_ENTRIES)
  1007 	{
  1008 		return NULL;
  1009 	}
  1010 
  1011 	CEntInfo *entInfos = EntInfoArray();
  1012 
  1013 	if (!entInfos)
  1014 	{
  1015 		/* Attempt to use engine interface instead */
  1016 		static CEntInfo tempInfo;
  1017 		tempInfo.m_pNext = NULL;
  1018 		tempInfo.m_pPrev = NULL;
  1019 
  1020 		edict_t *pEdict = PEntityOfEntIndex(entIndex);
  1021 
  1022 		if (!pEdict)
  1023 		{
  1024 			return NULL;
  1025 		}
  1026 
  1027 		IServerUnknown *pUnk = pEdict->GetUnknown();
  1028 
  1029 		if (!pUnk)
  1030 		{
  1031 			return NULL;
  1032 		}
  1033 
  1034 		tempInfo.m_pEntity = pUnk;
  1035 		tempInfo.m_SerialNumber = pUnk->GetRefEHandle().GetSerialNumber();
  1036 
  1037 		return &tempInfo;
  1038 	}
  1039 
  1040 	return &entInfos[entIndex];
  1041 }
  1042 
  1043 /**
  1044 	SERIAL_MASK = 0x7fff (15 bits)
  1045 
  1046 	#define	MAX_EDICT_BITS				11
  1047 	#define NUM_ENT_ENTRY_BITS		(MAX_EDICT_BITS + 1)
  1048 	m_Index = iEntry | (iSerialNumber << NUM_ENT_ENTRY_BITS); 
  1049 	
  1050 	Top 5 bits of a handle are unused.
  1051 	Bit 31 - Our 'reference' flag indicator
  1052 */
  1053 
  1054 cell_t CHalfLife2::IndexToReference(int entIndex)
  1055 {
  1056 	CBaseEntity *pEnt = ReferenceToEntity(entIndex);
  1057 	if (!pEnt)
  1058 	{
  1059 		return INVALID_EHANDLE_INDEX;
  1060 	}
  1061 
  1062 	return EntityToReference(pEnt);
  1063 }
  1064 
  1065 int CHalfLife2::ReferenceToIndex(cell_t entRef)
  1066 {
  1067 	if ((unsigned)entRef == INVALID_EHANDLE_INDEX)
  1068 	{
  1069 		return INVALID_EHANDLE_INDEX;
  1070 	}
  1071 
  1072 	if (entRef & (1<<31))
  1073 	{
  1074 		/* Proper ent reference */
  1075 		int hndlValue = entRef & ~(1<<31);
  1076 		CBaseHandle hndl(hndlValue);
  1077 
  1078 		CEntInfo *pInfo = LookupEntity(hndl.GetEntryIndex());
  1079 
  1080 		if (pInfo->m_SerialNumber != hndl.GetSerialNumber())
  1081 		{
  1082 			return INVALID_EHANDLE_INDEX;
  1083 		}
  1084 
  1085 		return hndl.GetEntryIndex();
  1086 	}
  1087 
  1088 	return entRef;
  1089 }
  1090 
  1091 cell_t CHalfLife2::EntityToBCompatRef(CBaseEntity *pEntity)
  1092 {
  1093 	if (pEntity == NULL)
  1094 	{
  1095 		return INVALID_EHANDLE_INDEX;
  1096 	}
  1097 
  1098 	IServerUnknown *pUnknown = (IServerUnknown *)pEntity;
  1099 	CBaseHandle hndl = pUnknown->GetRefEHandle();
  1100 
  1101 	if (hndl == INVALID_EHANDLE_INDEX)
  1102 	{
  1103 		return INVALID_EHANDLE_INDEX;
  1104 	}
  1105 	
  1106 	if (hndl.GetEntryIndex() >= MAX_EDICTS)
  1107 	{
  1108 		return (hndl.ToInt() | (1<<31));
  1109 	}
  1110 	else
  1111 	{
  1112 		return hndl.GetEntryIndex();
  1113 	}
  1114 }
  1115 
  1116 cell_t CHalfLife2::ReferenceToBCompatRef(cell_t entRef)
  1117 {
  1118 	if ((unsigned)entRef == INVALID_EHANDLE_INDEX)
  1119 	{
  1120 		return INVALID_EHANDLE_INDEX;
  1121 	}
  1122 
  1123 	int hndlValue = entRef & ~(1<<31);
  1124 	CBaseHandle hndl(hndlValue);
  1125 
  1126 	if (hndl.GetEntryIndex() < MAX_EDICTS)
  1127 	{
  1128 		return hndl.GetEntryIndex();
  1129 	}
  1130 
  1131 	return entRef;
  1132 }
  1133 
  1134 void *CHalfLife2::GetGlobalEntityList()
  1135 {
  1136 	return g_EntList;
  1137 }
  1138 
  1139 int CHalfLife2::GetSendPropOffset(SendProp *prop)
  1140 {
  1141 	return prop->GetOffset();
  1142 }
  1143 
  1144 const char *CHalfLife2::GetEntityClassname(edict_t * pEdict)
  1145 {
  1146 	if (pEdict == NULL || pEdict->IsFree())
  1147 	{
  1148 		return NULL;
  1149 	}
  1150 	
  1151 	IServerUnknown *pUnk = pEdict->GetUnknown();
  1152 	if (pUnk == NULL)
  1153 	{
  1154 		return NULL;
  1155 	}
  1156 	
  1157 	CBaseEntity * pEntity = pUnk->GetBaseEntity();
  1158 	
  1159 	if (pEntity == NULL)
  1160 	{
  1161 		return NULL;
  1162 	}
  1163 	
  1164 	return GetEntityClassname(pEntity);
  1165 }
  1166 
  1167 const char *CHalfLife2::GetEntityClassname(CBaseEntity *pEntity)
  1168 {
  1169 	static int offset = -1;
  1170 	if (offset == -1)
  1171 	{
  1172 		CBaseEntity *pGetterEnt = ReferenceToEntity(0);
  1173 		datamap_t *pMap = GetDataMap(pGetterEnt);
  1174 		
  1175 		sm_datatable_info_t info;
  1176 		if (!FindDataMapInfo(pMap, "m_iClassname", &info))
  1177 		{
  1178 			return NULL;
  1179 		}
  1180 		
  1181 		offset = info.actual_offset;
  1182 	}
  1183 
  1184 	return *(const char **)(((unsigned char *)pEntity) + offset);
  1185 }
  1186 
  1187 #if SOURCE_ENGINE >= SE_LEFT4DEAD
  1188 static bool ResolveFuzzyMapName(const char *fuzzyName, char *outFullname, int size)
  1189 {
  1190 	static ConCommand *pHelperCmd = g_pCVar->FindCommand("changelevel");
  1191 	if (!pHelperCmd || !pHelperCmd->CanAutoComplete())
  1192 		return false;
  1193 
  1194 	static size_t helperCmdLen = strlen(pHelperCmd->GetName());
  1195 
  1196 	CUtlVector<CUtlString> results;
  1197 	pHelperCmd->AutoCompleteSuggest(fuzzyName, results);
  1198 	if (results.Count() == 0)
  1199 		return false;
  1200 
  1201 	// Results come back as you'd see in autocomplete. (ie. "changelevel fullmapnamehere"),
  1202 	// so skip ahead to start of map path/name
  1203 
  1204 	// Like the engine, we're only going to deal with the first match.
  1205 
  1206 	strncopy(outFullname, &results[0][helperCmdLen + 1], size);
  1207 
  1208 	return true;
  1209 }
  1210 #endif
  1211 
  1212 bool CHalfLife2::IsMapValid(const char *map)
  1213 {
  1214 	if (!map || !map[0])
  1215 		return false;
  1216 
  1217 	bool ret = engine->IsMapValid(map);
  1218 #if SOURCE_ENGINE >= SE_LEFT4DEAD
  1219 	if (!ret)
  1220 	{
  1221 		static char szFuzzyName[PLATFORM_MAX_PATH];
  1222 		if (ResolveFuzzyMapName(map, szFuzzyName, sizeof(szFuzzyName)))
  1223 		{
  1224 			ret = engine->IsMapValid(szFuzzyName);
  1225 		}
  1226 	}
  1227 #endif
  1228 	return ret;
  1229 }