core/HalfLife2.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Thu Feb 27 09:53:57 2014 -0500 (2014-02-27)
changeset 4255 dbfcf51478ee
parent 4230 45fe61058383
child 4266 b6060fd93236
permissions -rw-r--r--
Remove TextMsg params padding on Dota.
     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 #elif SOURCE_ENGINE == SE_CSGO
   534 	CCSUsrMsg_TextMsg *pMsg;
   535 	if ((pMsg = (CCSUsrMsg_TextMsg *)g_UserMsgs.StartProtobufMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   536 	{
   537 		return false;
   538 	}
   539 
   540 	// Client tries to read all 5 'params' and will crash if less
   541 	pMsg->set_msg_dst(dest);
   542 	pMsg->add_params(msg);
   543 	pMsg->add_params("");
   544 	pMsg->add_params("");
   545 	pMsg->add_params("");
   546 	pMsg->add_params("");
   547 #else
   548 	if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   549 	{
   550 		return false;
   551 	}
   552 
   553 	pBitBuf->WriteByte(dest);
   554 	pBitBuf->WriteString(msg);
   555 #endif
   556 
   557 	g_UserMsgs.EndMessage();
   558 
   559 	return true;
   560 }
   561 
   562 bool CHalfLife2::HintTextMsg(int client, const char *msg)
   563 {
   564 	cell_t players[] = {client};
   565 
   566 #if SOURCE_ENGINE == SE_DOTA
   567 	CUserMsg_HintText *pMsg;
   568 	if ((pMsg = (CUserMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   569 	{
   570 		return false;
   571 	}
   572 
   573 	pMsg->set_message(msg);
   574 #elif SOURCE_ENGINE == SE_CSGO
   575 	CCSUsrMsg_HintText *pMsg;
   576 	if ((pMsg = (CCSUsrMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   577 	{
   578 		return false;
   579 	}
   580 
   581 	pMsg->set_text(msg);
   582 #else
   583 	bf_write *pBitBuf = NULL;
   584 
   585 	if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   586 	{
   587 		return false;
   588 	}
   589 
   590 	const char *pre_byte = g_pGameConf->GetKeyValue("HintTextPreByte");
   591 	if (pre_byte != NULL && strcmp(pre_byte, "yes") == 0)
   592 	{
   593 		pBitBuf->WriteByte(1);
   594 	}
   595 	pBitBuf->WriteString(msg);
   596 #endif
   597 	g_UserMsgs.EndMessage();
   598 
   599 	return true;
   600 }
   601 
   602 bool CHalfLife2::HintTextMsg(cell_t *players, int count, const char *msg)
   603 {
   604 #if SOURCE_ENGINE == SE_DOTA
   605 	CUserMsg_HintText *pMsg;
   606 	if ((pMsg = (CUserMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, count, USERMSG_RELIABLE)) == NULL)
   607 	{
   608 		return false;
   609 	}
   610 
   611 	pMsg->set_message(msg);
   612 #elif SOURCE_ENGINE == SE_CSGO
   613 	CCSUsrMsg_HintText *pMsg;
   614 	if ((pMsg = (CCSUsrMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, count, USERMSG_RELIABLE)) == NULL)
   615 	{
   616 		return false;
   617 	}
   618 
   619 	pMsg->set_text(msg);
   620 #else
   621 	bf_write *pBitBuf = NULL;
   622 
   623 	if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_HinTextMsg, players, count, USERMSG_RELIABLE)) == NULL)
   624 	{
   625 		return false;
   626 	}
   627 
   628 	const char *pre_byte = g_pGameConf->GetKeyValue("HintTextPreByte");
   629 	if (pre_byte != NULL && strcmp(pre_byte, "yes") == 0)
   630 	{
   631 		pBitBuf->WriteByte(1);
   632 	}
   633 	pBitBuf->WriteString(msg);
   634 #endif
   635 
   636 	g_UserMsgs.EndMessage();
   637 
   638 	return true;
   639 }
   640 
   641 bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show)
   642 {
   643 	KeyValues *SubKey = NULL;
   644 	int count = 0;
   645 	cell_t players[] = {client};
   646 
   647 #if SOURCE_ENGINE == SE_DOTA
   648 	CUserMsg_VGUIMenu *pMsg;
   649 	if ((pMsg = (CUserMsg_VGUIMenu *)g_UserMsgs.StartProtobufMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
   650 	{
   651 		return false;
   652 	}
   653 #elif SOURCE_ENGINE == SE_CSGO
   654 	CCSUsrMsg_VGUIMenu *pMsg;
   655 	if ((pMsg = (CCSUsrMsg_VGUIMenu *)g_UserMsgs.StartProtobufMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
   656 	{
   657 		return false;
   658 	}
   659 #else
   660 	bf_write *pBitBuf = NULL;
   661 	if ((pBitBuf = g_UserMsgs.StartBitBufMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
   662 	{
   663 		return false;
   664 	}
   665 #endif
   666 
   667 	if (data)
   668 	{
   669 		SubKey = data->GetFirstSubKey();
   670 		while (SubKey)
   671 		{
   672 			count++;
   673 			SubKey = SubKey->GetNextKey();
   674 		}
   675 		SubKey = data->GetFirstSubKey();
   676 	}
   677 
   678 #if SOURCE_ENGINE == SE_DOTA
   679 	pMsg->set_name(name);
   680 	pMsg->set_show(show);
   681 
   682 	while (SubKey)
   683 	{
   684 		CUserMsg_VGUIMenu_Keys *key = pMsg->add_keys();
   685 		key->set_name(SubKey->GetName());
   686 		key->set_value(SubKey->GetString());
   687 		SubKey = SubKey->GetNextKey();
   688 	}
   689 #elif SOURCE_ENGINE == SE_CSGO
   690 	pMsg->set_name(name);
   691 	pMsg->set_show(show);
   692 
   693 	while (SubKey)
   694 	{
   695 		CCSUsrMsg_VGUIMenu_Subkey *key = pMsg->add_subkeys();
   696 		key->set_name(SubKey->GetName());
   697 		key->set_str(SubKey->GetString());
   698 		SubKey = SubKey->GetNextKey();
   699 	}
   700 #else
   701 	pBitBuf->WriteString(name);
   702 	pBitBuf->WriteByte((show) ? 1 : 0);
   703 	pBitBuf->WriteByte(count);
   704 	while (SubKey)
   705 	{
   706 		pBitBuf->WriteString(SubKey->GetName());
   707 		pBitBuf->WriteString(SubKey->GetString());
   708 		SubKey = SubKey->GetNextKey();
   709 	}
   710 #endif
   711 
   712 	g_UserMsgs.EndMessage();
   713 
   714 	return true;
   715 }
   716 
   717 void CHalfLife2::AddToFakeCliCmdQueue(int client, int userid, const char *cmd)
   718 {
   719 	DelayedFakeCliCmd *pFake;
   720 
   721 	if (m_FreeCmds.empty())
   722 	{
   723 		pFake = new DelayedFakeCliCmd;
   724 	} else {
   725 		pFake = m_FreeCmds.front();
   726 		m_FreeCmds.pop();
   727 	}
   728 
   729 	pFake->client = client;
   730 	pFake->userid = userid;
   731 	pFake->cmd.assign(cmd);
   732 
   733 	m_CmdQueue.push(pFake);
   734 }
   735 
   736 void CHalfLife2::ProcessFakeCliCmdQueue()
   737 {
   738 	while (!m_CmdQueue.empty())
   739 	{
   740 		DelayedFakeCliCmd *pFake = m_CmdQueue.first();
   741 
   742 		if (g_Players.GetClientOfUserId(pFake->userid) == pFake->client)
   743 		{
   744 			CPlayer *pPlayer = g_Players.GetPlayerByIndex(pFake->client);
   745 #if SOURCE_ENGINE == SE_DOTA
   746 			engine->ClientCommand(pPlayer->GetIndex(), "%s", pFake->cmd.c_str());
   747 #else
   748 			serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), pFake->cmd.c_str());
   749 #endif
   750 		}
   751 
   752 		m_CmdQueue.pop();
   753 	}
   754 }
   755 
   756 bool CHalfLife2::IsLANServer()
   757 {
   758 	sv_lan = icvar->FindVar("sv_lan");
   759 
   760 	if (!sv_lan)
   761 	{
   762 		return false;
   763 	}
   764 
   765 	return (sv_lan->GetInt() != 0);
   766 }
   767 
   768 bool CHalfLife2::KVLoadFromFile(KeyValues *kv, IBaseFileSystem *filesystem, const char *resourceName, const char *pathID)
   769 {
   770 #if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11
   771 	if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
   772 #else
   773 	if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
   774 #endif
   775 	{
   776 		Assert(filesystem);
   777 #ifdef _MSC_VER
   778 		Assert(_heapchk() == _HEAPOK);
   779 #endif
   780 
   781 		FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
   782 		if (!f)
   783 			return false;
   784 
   785 		// load file into a null-terminated buffer
   786 		int fileSize = filesystem->Size(f);
   787 		char *buffer = (char *)MemAllocScratch(fileSize + 1);
   788 
   789 		Assert(buffer);
   790 
   791 		filesystem->Read(buffer, fileSize, f); // read into local buffer
   792 
   793 		buffer[fileSize] = 0; // null terminate file as EOF
   794 
   795 		filesystem->Close( f );	// close file after reading
   796 
   797 		bool retOK = kv->LoadFromBuffer( resourceName, buffer, filesystem );
   798 
   799 		MemFreeScratch();
   800 
   801 		return retOK;
   802 	}
   803 	else
   804 	{
   805 		return kv->LoadFromFile(filesystem, resourceName, pathID);
   806 	}
   807 }
   808 
   809 void CHalfLife2::PushCommandStack(const CCommand *cmd)
   810 {
   811 	CachedCommandInfo info;
   812 
   813 	info.args = cmd;
   814 #if SOURCE_ENGINE <= SE_DARKMESSIAH
   815 	strncopy(info.cmd, cmd->Arg(0), sizeof(info.cmd));
   816 #endif
   817 
   818 	m_CommandStack.push(info);
   819 }
   820 
   821 const CCommand *CHalfLife2::PeekCommandStack()
   822 {
   823 	if (m_CommandStack.empty())
   824 	{
   825 		return NULL;
   826 	}
   827 
   828 	return m_CommandStack.front().args;
   829 }
   830 
   831 void CHalfLife2::PopCommandStack()
   832 {
   833 	m_CommandStack.pop();
   834 }
   835 
   836 const char *CHalfLife2::CurrentCommandName()
   837 {
   838 #if SOURCE_ENGINE >= SE_ORANGEBOX
   839 	return m_CommandStack.front().args->Arg(0);
   840 #else
   841 	return m_CommandStack.front().cmd;
   842 #endif
   843 }
   844 
   845 void CHalfLife2::AddDelayedKick(int client, int userid, const char *msg)
   846 {
   847 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   848 	if (!pPlayer || !pPlayer->IsConnected() || pPlayer->IsInKickQueue())
   849 	{
   850 		return;
   851 	}
   852 	
   853 	pPlayer->MarkAsBeingKicked();
   854 
   855 	DelayedKickInfo kick;
   856 
   857 	kick.client = client;
   858 	kick.userid = userid;
   859 	UTIL_Format(kick.buffer, sizeof(kick.buffer), "%s", msg);
   860 
   861 	m_DelayedKicks.push(kick);
   862 }
   863 
   864 void CHalfLife2::ProcessDelayedKicks()
   865 {
   866 	while (!m_DelayedKicks.empty())
   867 	{
   868 		DelayedKickInfo info = m_DelayedKicks.first();
   869 		m_DelayedKicks.pop();
   870 
   871 		CPlayer *player = g_Players.GetPlayerByIndex(info.client);
   872 		if (player == NULL || player->GetUserId() != info.userid)
   873 		{
   874 			continue;
   875 		}
   876 
   877 		player->Kick(info.buffer);
   878 	}
   879 }
   880 
   881 edict_t *CHalfLife2::EdictOfIndex(int index)
   882 {
   883 	return ::PEntityOfEntIndex(index);
   884 }
   885 
   886 int CHalfLife2::IndexOfEdict(edict_t *pEnt)
   887 {
   888 	return ::IndexOfEdict(pEnt);
   889 }
   890 
   891 edict_t *CHalfLife2::GetHandleEntity(CBaseHandle &hndl)
   892 {
   893 	if (!hndl.IsValid())
   894 	{
   895 		return NULL;
   896 	}
   897 
   898 	int index = hndl.GetEntryIndex();
   899 
   900 	edict_t *pStoredEdict;
   901 	CBaseEntity *pStoredEntity;
   902 
   903 	if (!IndexToAThings(index, &pStoredEntity, &pStoredEdict))
   904 	{
   905 		return NULL;
   906 	}
   907 
   908 	if (pStoredEdict == NULL || pStoredEntity == NULL)
   909 	{
   910 		return NULL;
   911 	}
   912 
   913 	IServerEntity *pSE = pStoredEdict->GetIServerEntity();
   914 
   915 	if (pSE == NULL)
   916 	{
   917 		return NULL;
   918 	}
   919 
   920 	if (pSE->GetRefEHandle() != hndl)
   921 	{
   922 		return NULL;
   923 	}
   924 
   925 	return pStoredEdict;
   926 }
   927 
   928 void CHalfLife2::SetHandleEntity(CBaseHandle &hndl, edict_t *pEnt)
   929 {
   930 	IServerEntity *pEntOther = pEnt->GetIServerEntity();
   931 
   932 	if (pEntOther == NULL)
   933 	{
   934 		return;
   935 	}
   936 
   937 	hndl.Set(pEntOther);
   938 }
   939 
   940 const char *CHalfLife2::GetCurrentMap()
   941 {
   942 	return STRING(gpGlobals->mapname);
   943 }
   944 
   945 void CHalfLife2::ServerCommand(const char *buffer)
   946 {
   947 	engine->ServerCommand(buffer);
   948 }
   949 
   950 cell_t CHalfLife2::EntityToReference(CBaseEntity *pEntity)
   951 {
   952 	IServerUnknown *pUnknown = (IServerUnknown *)pEntity;
   953 	CBaseHandle hndl = pUnknown->GetRefEHandle();
   954 	return (hndl.ToInt() | (1<<31));
   955 }
   956 
   957 CBaseEntity *CHalfLife2::ReferenceToEntity(cell_t entRef)
   958 {
   959 	if ((unsigned)entRef == INVALID_EHANDLE_INDEX)
   960 	{
   961 		return NULL;
   962 	}
   963 
   964 	CEntInfo *pInfo = NULL;
   965 
   966 	if (entRef & (1<<31))
   967 	{
   968 		/* Proper ent reference */
   969 		int hndlValue = entRef & ~(1<<31);
   970 		CBaseHandle hndl(hndlValue);
   971 
   972 		pInfo = LookupEntity(hndl.GetEntryIndex());
   973 		if (!pInfo || pInfo->m_SerialNumber != hndl.GetSerialNumber())
   974 		{
   975 			return NULL;
   976 		}
   977 	}
   978 	else
   979 	{
   980 		/* Old style index only */
   981 		pInfo = LookupEntity(entRef);
   982 	}
   983 
   984 	if (!pInfo)
   985 	{
   986 		return NULL;
   987 	}
   988 
   989 	IServerUnknown *pUnk = static_cast<IServerUnknown *>(pInfo->m_pEntity);
   990 	if (pUnk)
   991 	{
   992 		return pUnk->GetBaseEntity();
   993 	}
   994 
   995 	return NULL;
   996 }
   997 
   998 /**
   999  * Retrieves the CEntInfo pointer from g_EntList for a given entity index
  1000  */
  1001 CEntInfo *CHalfLife2::LookupEntity(int entIndex)
  1002 {
  1003 	// Make sure that our index is within the bounds of the global ent array
  1004 	if (entIndex < 0 || entIndex >= NUM_ENT_ENTRIES)
  1005 	{
  1006 		return NULL;
  1007 	}
  1008 
  1009 	CEntInfo *entInfos = EntInfoArray();
  1010 
  1011 	if (!entInfos)
  1012 	{
  1013 		/* Attempt to use engine interface instead */
  1014 		static CEntInfo tempInfo;
  1015 		tempInfo.m_pNext = NULL;
  1016 		tempInfo.m_pPrev = NULL;
  1017 
  1018 		edict_t *pEdict = PEntityOfEntIndex(entIndex);
  1019 
  1020 		if (!pEdict)
  1021 		{
  1022 			return NULL;
  1023 		}
  1024 
  1025 		IServerUnknown *pUnk = pEdict->GetUnknown();
  1026 
  1027 		if (!pUnk)
  1028 		{
  1029 			return NULL;
  1030 		}
  1031 
  1032 		tempInfo.m_pEntity = pUnk;
  1033 		tempInfo.m_SerialNumber = pUnk->GetRefEHandle().GetSerialNumber();
  1034 
  1035 		return &tempInfo;
  1036 	}
  1037 
  1038 	return &entInfos[entIndex];
  1039 }
  1040 
  1041 /**
  1042 	SERIAL_MASK = 0x7fff (15 bits)
  1043 
  1044 	#define	MAX_EDICT_BITS				11
  1045 	#define NUM_ENT_ENTRY_BITS		(MAX_EDICT_BITS + 1)
  1046 	m_Index = iEntry | (iSerialNumber << NUM_ENT_ENTRY_BITS); 
  1047 	
  1048 	Top 5 bits of a handle are unused.
  1049 	Bit 31 - Our 'reference' flag indicator
  1050 */
  1051 
  1052 cell_t CHalfLife2::IndexToReference(int entIndex)
  1053 {
  1054 	CBaseEntity *pEnt = ReferenceToEntity(entIndex);
  1055 	if (!pEnt)
  1056 	{
  1057 		return INVALID_EHANDLE_INDEX;
  1058 	}
  1059 
  1060 	return EntityToReference(pEnt);
  1061 }
  1062 
  1063 int CHalfLife2::ReferenceToIndex(cell_t entRef)
  1064 {
  1065 	if ((unsigned)entRef == INVALID_EHANDLE_INDEX)
  1066 	{
  1067 		return INVALID_EHANDLE_INDEX;
  1068 	}
  1069 
  1070 	if (entRef & (1<<31))
  1071 	{
  1072 		/* Proper ent reference */
  1073 		int hndlValue = entRef & ~(1<<31);
  1074 		CBaseHandle hndl(hndlValue);
  1075 
  1076 		CEntInfo *pInfo = LookupEntity(hndl.GetEntryIndex());
  1077 
  1078 		if (pInfo->m_SerialNumber != hndl.GetSerialNumber())
  1079 		{
  1080 			return INVALID_EHANDLE_INDEX;
  1081 		}
  1082 
  1083 		return hndl.GetEntryIndex();
  1084 	}
  1085 
  1086 	return entRef;
  1087 }
  1088 
  1089 cell_t CHalfLife2::EntityToBCompatRef(CBaseEntity *pEntity)
  1090 {
  1091 	if (pEntity == NULL)
  1092 	{
  1093 		return INVALID_EHANDLE_INDEX;
  1094 	}
  1095 
  1096 	IServerUnknown *pUnknown = (IServerUnknown *)pEntity;
  1097 	CBaseHandle hndl = pUnknown->GetRefEHandle();
  1098 
  1099 	if (hndl == INVALID_EHANDLE_INDEX)
  1100 	{
  1101 		return INVALID_EHANDLE_INDEX;
  1102 	}
  1103 	
  1104 	if (hndl.GetEntryIndex() >= MAX_EDICTS)
  1105 	{
  1106 		return (hndl.ToInt() | (1<<31));
  1107 	}
  1108 	else
  1109 	{
  1110 		return hndl.GetEntryIndex();
  1111 	}
  1112 }
  1113 
  1114 cell_t CHalfLife2::ReferenceToBCompatRef(cell_t entRef)
  1115 {
  1116 	if ((unsigned)entRef == INVALID_EHANDLE_INDEX)
  1117 	{
  1118 		return INVALID_EHANDLE_INDEX;
  1119 	}
  1120 
  1121 	int hndlValue = entRef & ~(1<<31);
  1122 	CBaseHandle hndl(hndlValue);
  1123 
  1124 	if (hndl.GetEntryIndex() < MAX_EDICTS)
  1125 	{
  1126 		return hndl.GetEntryIndex();
  1127 	}
  1128 
  1129 	return entRef;
  1130 }
  1131 
  1132 void *CHalfLife2::GetGlobalEntityList()
  1133 {
  1134 	return g_EntList;
  1135 }
  1136 
  1137 int CHalfLife2::GetSendPropOffset(SendProp *prop)
  1138 {
  1139 	return prop->GetOffset();
  1140 }
  1141 
  1142 const char *CHalfLife2::GetEntityClassname(edict_t * pEdict)
  1143 {
  1144 	if (pEdict == NULL || pEdict->IsFree())
  1145 	{
  1146 		return NULL;
  1147 	}
  1148 	
  1149 	IServerUnknown *pUnk = pEdict->GetUnknown();
  1150 	if (pUnk == NULL)
  1151 	{
  1152 		return NULL;
  1153 	}
  1154 	
  1155 	CBaseEntity * pEntity = pUnk->GetBaseEntity();
  1156 	
  1157 	if (pEntity == NULL)
  1158 	{
  1159 		return NULL;
  1160 	}
  1161 	
  1162 	return GetEntityClassname(pEntity);
  1163 }
  1164 
  1165 const char *CHalfLife2::GetEntityClassname(CBaseEntity *pEntity)
  1166 {
  1167 	static int offset = -1;
  1168 	if (offset == -1)
  1169 	{
  1170 		CBaseEntity *pGetterEnt = ReferenceToEntity(0);
  1171 		datamap_t *pMap = GetDataMap(pGetterEnt);
  1172 		
  1173 		sm_datatable_info_t info;
  1174 		if (!FindDataMapInfo(pMap, "m_iClassname", &info))
  1175 		{
  1176 			return NULL;
  1177 		}
  1178 		
  1179 		offset = info.actual_offset;
  1180 	}
  1181 
  1182 	return *(const char **)(((unsigned char *)pEntity) + offset);
  1183 }
  1184 
  1185 #if SOURCE_ENGINE >= SE_LEFT4DEAD
  1186 static bool ResolveFuzzyMapName(const char *fuzzyName, char *outFullname, int size)
  1187 {
  1188 	static ConCommand *pHelperCmd = g_pCVar->FindCommand("changelevel");
  1189 	if (!pHelperCmd || !pHelperCmd->CanAutoComplete())
  1190 		return false;
  1191 
  1192 	static size_t helperCmdLen = strlen(pHelperCmd->GetName());
  1193 
  1194 	CUtlVector<CUtlString> results;
  1195 	pHelperCmd->AutoCompleteSuggest(fuzzyName, results);
  1196 	if (results.Count() == 0)
  1197 		return false;
  1198 
  1199 	// Results come back as you'd see in autocomplete. (ie. "changelevel fullmapnamehere"),
  1200 	// so skip ahead to start of map path/name
  1201 
  1202 	// Like the engine, we're only going to deal with the first match.
  1203 
  1204 	strncopy(outFullname, &results[0][helperCmdLen + 1], size);
  1205 
  1206 	return true;
  1207 }
  1208 #endif
  1209 
  1210 bool CHalfLife2::IsMapValid(const char *map)
  1211 {
  1212 	if (!map || !map[0])
  1213 		return false;
  1214 
  1215 	bool ret = engine->IsMapValid(map);
  1216 #if SOURCE_ENGINE >= SE_LEFT4DEAD
  1217 	if (!ret)
  1218 	{
  1219 		static char szFuzzyName[PLATFORM_MAX_PATH];
  1220 		if (ResolveFuzzyMapName(map, szFuzzyName, sizeof(szFuzzyName)))
  1221 		{
  1222 			ret = engine->IsMapValid(szFuzzyName);
  1223 		}
  1224 	}
  1225 #endif
  1226 	return ret;
  1227 }