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