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