core/HalfLife2.cpp
author David Anderson <dvander@alliedmods.net>
Sun Feb 01 02:03:03 2009 -0500 (2009-02-01)
changeset 2545 9cb1a1fefc2c
parent 2400 76b7b4575e21
child 2583 d3b2571c378b
permissions -rw-r--r--
Added the ability for extensions to invoke arbitrary natives (bug 3605, r=ds,theY4Kman).
     1 /**
     2  * vim: set ts=4 :
     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 "GameConfigs.h"
    39 #include <compat_wrappers.h>
    40 
    41 CHalfLife2 g_HL2;
    42 ConVar *sv_lan = NULL;
    43 
    44 namespace SourceHook
    45 {
    46 	template<>
    47 	int HashFunction<datamap_t *>(datamap_t * const &k)
    48 	{
    49 		return reinterpret_cast<int>(k);
    50 	}
    51 
    52 	template<>
    53 	int Compare<datamap_t *>(datamap_t * const &k1, datamap_t * const &k2)
    54 	{
    55 		return (k1-k2);
    56 	}
    57 }
    58 
    59 CHalfLife2::CHalfLife2()
    60 {
    61 	m_pClasses = sm_trie_create();
    62 }
    63 
    64 CHalfLife2::~CHalfLife2()
    65 {
    66 	sm_trie_destroy(m_pClasses);
    67 
    68 	List<DataTableInfo *>::iterator iter;
    69 	DataTableInfo *pInfo;
    70 	for (iter=m_Tables.begin(); iter!=m_Tables.end(); iter++)
    71 	{
    72 		pInfo = (*iter);
    73 		delete pInfo;
    74 	}
    75 
    76 	m_Tables.clear();
    77 
    78 	THash<datamap_t *, DataMapTrie>::iterator h_iter;
    79 	for (h_iter=m_Maps.begin(); h_iter!=m_Maps.end(); h_iter++)
    80 	{
    81 		if (h_iter->val.trie)
    82 		{
    83 			sm_trie_destroy(h_iter->val.trie);
    84 			h_iter->val.trie = NULL;
    85 		}
    86 	}
    87 
    88 	m_Maps.clear();
    89 }
    90 
    91 CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
    92 bool is_original_engine = false;
    93 
    94 void CHalfLife2::OnSourceModStartup(bool late)
    95 {
    96 	/* The Ship currently is the only known game to use an older version of the engine */
    97 #if defined METAMOD_PLAPI_VERSION
    98 	if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
    99 #else
   100 	if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
   101 #endif
   102 	{
   103 		is_original_engine = true;
   104 	}
   105 	else if (g_pSharedChangeInfo == NULL)
   106 	{
   107 		g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo();
   108 	}
   109 }
   110 
   111 void CHalfLife2::OnSourceModAllInitialized()
   112 {
   113 	m_MsgTextMsg = g_UserMsgs.GetMessageIndex("TextMsg");
   114 	m_HinTextMsg = g_UserMsgs.GetMessageIndex("HintText");
   115 	m_VGUIMenu = g_UserMsgs.GetMessageIndex("VGUIMenu");
   116 	g_ShareSys.AddInterface(NULL, this);
   117 }
   118 
   119 #if !defined METAMOD_PLAPI_VERSION
   120 bool CHalfLife2::IsOriginalEngine()
   121 {
   122 	return is_original_engine;
   123 }
   124 #endif
   125 
   126 IChangeInfoAccessor *CBaseEdict::GetChangeAccessor()
   127 {
   128 	return engine->GetChangeAccessor( (const edict_t *)this );
   129 }
   130 
   131 bool UTIL_FindInSendTable(SendTable *pTable, 
   132 						  const char *name,
   133 						  sm_sendprop_info_t *info,
   134 						  unsigned int offset)
   135 {
   136 	const char *pname;
   137 	int props = pTable->GetNumProps();
   138 	SendProp *prop;
   139 
   140 	for (int i=0; i<props; i++)
   141 	{
   142 		prop = pTable->GetProp(i);
   143 		pname = prop->GetName();
   144 		if (pname && strcmp(name, pname) == 0)
   145 		{
   146 			info->prop = prop;
   147 			info->actual_offset = offset + info->prop->GetOffset();
   148 			return true;
   149 		}
   150 		if (prop->GetDataTable())
   151 		{
   152 			if (UTIL_FindInSendTable(prop->GetDataTable(), 
   153 				name,
   154 				info,
   155 				offset + prop->GetOffset())
   156 				)
   157 			{
   158 				return true;
   159 			}
   160 		}
   161 	}
   162 
   163 	return false;
   164 }
   165 
   166 typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name)
   167 {
   168 	while (pMap)
   169 	{
   170 		for (int i=0; i<pMap->dataNumFields; i++)
   171 		{
   172 			if (pMap->dataDesc[i].fieldName == NULL)
   173 			{
   174 				continue;
   175 			}
   176 			if (strcmp(name, pMap->dataDesc[i].fieldName) == 0)
   177 			{
   178 				return &(pMap->dataDesc[i]);
   179 			}
   180 			if (pMap->dataDesc[i].td)
   181 			{
   182 				typedescription_t *_td;
   183 				if ((_td=UTIL_FindInDataMap(pMap->dataDesc[i].td, name)) != NULL)
   184 				{
   185 					return _td;
   186 				}
   187 			}
   188 		}
   189 		pMap = pMap->baseMap;
   190 	}
   191 
   192 	return NULL; 
   193 }
   194 
   195 ServerClass *CHalfLife2::FindServerClass(const char *classname)
   196 {
   197 	DataTableInfo *pInfo = _FindServerClass(classname);
   198 
   199 	if (!pInfo)
   200 	{
   201 		return NULL;
   202 	}
   203 
   204 	return pInfo->sc;
   205 }
   206 
   207 DataTableInfo *CHalfLife2::_FindServerClass(const char *classname)
   208 {
   209 	DataTableInfo *pInfo = NULL;
   210 
   211 	if (!sm_trie_retrieve(m_pClasses, classname, (void **)&pInfo))
   212 	{
   213 		ServerClass *sc = gamedll->GetAllServerClasses();
   214 		while (sc)
   215 		{
   216 			if (strcmp(classname, sc->GetName()) == 0)
   217 			{
   218 				pInfo = new DataTableInfo;
   219 				pInfo->sc = sc;
   220 				sm_trie_insert(m_pClasses, classname, pInfo);
   221 				m_Tables.push_back(pInfo);
   222 				break;
   223 			}
   224 			sc = sc->m_pNext;
   225 		}
   226 		if (!pInfo)
   227 		{
   228 			return NULL;
   229 		}
   230 	}
   231 
   232 	return pInfo;
   233 }
   234 
   235 bool CHalfLife2::FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info)
   236 {
   237 	DataTableInfo *pInfo;
   238 	sm_sendprop_info_t *prop;
   239 
   240 	if ((pInfo = _FindServerClass(classname)) == NULL)
   241 	{
   242 		return false;
   243 	}
   244 
   245 	if ((prop = pInfo->lookup.retrieve(offset)) == NULL)
   246 	{
   247 		sm_sendprop_info_t temp_info;
   248 
   249 		if (!UTIL_FindInSendTable(pInfo->sc->m_pTable, offset, &temp_info, 0))
   250 		{
   251 			return false;
   252 		}
   253 
   254 		pInfo->lookup.insert(offset, temp_info);
   255 		*info = temp_info;
   256 	}
   257 	else
   258 	{
   259 		*info = *prop;
   260 	}
   261 	
   262 	return true;
   263 }
   264 
   265 SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset)
   266 {
   267 	sm_sendprop_info_t info;
   268 
   269 	if (!FindSendPropInfo(classname, offset, &info))
   270 	{
   271 		return NULL;
   272 	}
   273 
   274 	return info.prop;
   275 }
   276 
   277 typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset)
   278 {
   279 	typedescription_t *td = NULL;
   280 	DataMapTrie &val = m_Maps[pMap];
   281 
   282 	if (!val.trie)
   283 	{
   284 		val.trie = sm_trie_create();
   285 	}
   286 	if (!sm_trie_retrieve(val.trie, offset, (void **)&td))
   287 	{
   288 		if ((td = UTIL_FindInDataMap(pMap, offset)) != NULL)
   289 		{
   290 			sm_trie_insert(val.trie, offset, td);
   291 		}
   292 	}
   293 
   294 	return td;
   295 }
   296 
   297 void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset)
   298 {
   299 	if (g_pSharedChangeInfo != NULL)
   300 	{
   301 		if (offset)
   302 		{
   303 			pEdict->StateChanged(offset);
   304 		}
   305 		else
   306 		{
   307 			pEdict->StateChanged();
   308 		}
   309 	}
   310 	else
   311 	{
   312 		pEdict->m_fStateFlags |= FL_EDICT_CHANGED;
   313 	}
   314 }
   315 
   316 bool CHalfLife2::TextMsg(int client, int dest, const char *msg)
   317 {
   318 	bf_write *pBitBuf = NULL;
   319 	cell_t players[] = {client};
   320 
   321 	if ((pBitBuf = g_UserMsgs.StartMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   322 	{
   323 		return false;
   324 	}
   325 
   326 	pBitBuf->WriteByte(dest);
   327 	pBitBuf->WriteString(msg);
   328 	g_UserMsgs.EndMessage();
   329 
   330 	return true;
   331 }
   332 
   333 bool CHalfLife2::HintTextMsg(int client, const char *msg)
   334 {
   335 	bf_write *pBitBuf = NULL;
   336 	cell_t players[] = {client};
   337 
   338 	if ((pBitBuf = g_UserMsgs.StartMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   339 	{
   340 		return false;
   341 	}
   342 
   343 	const char *pre_byte = g_pGameConf->GetKeyValue("HintTextPreByte");
   344 	if (pre_byte != NULL && strcmp(pre_byte, "yes") == 0)
   345 	{
   346 		pBitBuf->WriteByte(1);
   347 	}
   348 	pBitBuf->WriteString(msg);
   349 	g_UserMsgs.EndMessage();
   350 
   351 	return true;
   352 }
   353 
   354 bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show)
   355 {
   356 	bf_write *pBitBuf = NULL;
   357 	KeyValues *SubKey = NULL;
   358 	int count = 0;
   359 	cell_t players[] = {client};
   360 
   361 	if ((pBitBuf = g_UserMsgs.StartMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
   362 	{
   363 		return false;
   364 	}
   365 
   366 	if (data)
   367 	{
   368 		SubKey = data->GetFirstSubKey();
   369 		while (SubKey)
   370 		{
   371 			count++;
   372 			SubKey = SubKey->GetNextKey();
   373 		}
   374 		SubKey = data->GetFirstSubKey();
   375 	}
   376 
   377 	pBitBuf->WriteString(name);
   378 	pBitBuf->WriteByte((show) ? 1 : 0);
   379 	pBitBuf->WriteByte(count);
   380 	while (SubKey)
   381 	{
   382 		pBitBuf->WriteString(SubKey->GetName());
   383 		pBitBuf->WriteString(SubKey->GetString());
   384 		SubKey = SubKey->GetNextKey();
   385 	}
   386 	g_UserMsgs.EndMessage();
   387 
   388 	return true;
   389 }
   390 
   391 void CHalfLife2::AddToFakeCliCmdQueue(int client, int userid, const char *cmd)
   392 {
   393 	DelayedFakeCliCmd *pFake;
   394 
   395 	if (m_FreeCmds.empty())
   396 	{
   397 		pFake = new DelayedFakeCliCmd;
   398 	} else {
   399 		pFake = m_FreeCmds.front();
   400 		m_FreeCmds.pop();
   401 	}
   402 
   403 	pFake->client = client;
   404 	pFake->userid = userid;
   405 	pFake->cmd.assign(cmd);
   406 
   407 	m_CmdQueue.push(pFake);
   408 }
   409 
   410 void CHalfLife2::ProcessFakeCliCmdQueue()
   411 {
   412 	while (!m_CmdQueue.empty())
   413 	{
   414 		DelayedFakeCliCmd *pFake = m_CmdQueue.first();
   415 
   416 		if (g_Players.GetClientOfUserId(pFake->userid) == pFake->client)
   417 		{
   418 			CPlayer *pPlayer = g_Players.GetPlayerByIndex(pFake->client);
   419 			serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), pFake->cmd.c_str());
   420 		}
   421 
   422 		m_CmdQueue.pop();
   423 	}
   424 }
   425 
   426 bool CHalfLife2::IsLANServer()
   427 {
   428 	sv_lan = icvar->FindVar("sv_lan");
   429 
   430 	if (!sv_lan)
   431 	{
   432 		return false;
   433 	}
   434 
   435 	return (sv_lan->GetInt() != 0);
   436 }
   437 
   438 bool CHalfLife2::KVLoadFromFile(KeyValues *kv, IBaseFileSystem *filesystem, const char *resourceName, const char *pathID)
   439 {
   440 #if defined METAMOD_PLAPI_VERSION
   441 	if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
   442 #else
   443 	if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
   444 #endif
   445 	{
   446 		Assert(filesystem);
   447 #ifdef _MSC_VER
   448 		Assert(_heapchk() == _HEAPOK);
   449 #endif
   450 
   451 		FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
   452 		if (!f)
   453 			return false;
   454 
   455 		// load file into a null-terminated buffer
   456 		int fileSize = filesystem->Size(f);
   457 		char *buffer = (char *)MemAllocScratch(fileSize + 1);
   458 
   459 		Assert(buffer);
   460 
   461 		filesystem->Read(buffer, fileSize, f); // read into local buffer
   462 
   463 		buffer[fileSize] = 0; // null terminate file as EOF
   464 
   465 		filesystem->Close( f );	// close file after reading
   466 
   467 		bool retOK = kv->LoadFromBuffer( resourceName, buffer, filesystem );
   468 
   469 		MemFreeScratch();
   470 
   471 		return retOK;
   472 	}
   473 	else
   474 	{
   475 		return kv->LoadFromFile(filesystem, resourceName, pathID);
   476 	}
   477 }
   478 
   479 void CHalfLife2::PushCommandStack(const CCommand *cmd)
   480 {
   481 	CachedCommandInfo info;
   482 
   483 	info.args = cmd;
   484 #if SOURCE_ENGINE == SE_EPISODEONE
   485 	strncopy(info.cmd, cmd->Arg(0), sizeof(info.cmd));
   486 #endif
   487 
   488 	m_CommandStack.push(info);
   489 }
   490 
   491 const CCommand *CHalfLife2::PeekCommandStack()
   492 {
   493 	if (m_CommandStack.empty())
   494 	{
   495 		return NULL;
   496 	}
   497 
   498 	return m_CommandStack.front().args;
   499 }
   500 
   501 void CHalfLife2::PopCommandStack()
   502 {
   503 	m_CommandStack.pop();
   504 }
   505 
   506 const char *CHalfLife2::CurrentCommandName()
   507 {
   508 #if SOURCE_ENGINE >= SE_ORANGEBOX
   509 	return m_CommandStack.front().args->Arg(0);
   510 #else
   511 	return m_CommandStack.front().cmd;
   512 #endif
   513 }
   514 
   515 void CHalfLife2::AddDelayedKick(int client, int userid, const char *msg)
   516 {
   517 	DelayedKickInfo kick;
   518 
   519 	kick.client = client;
   520 	kick.userid = userid;
   521 	UTIL_Format(kick.buffer, sizeof(kick.buffer), "%s", msg);
   522 
   523 	m_DelayedKicks.push(kick);
   524 }
   525 
   526 void CHalfLife2::ProcessDelayedKicks()
   527 {
   528 	while (!m_DelayedKicks.empty())
   529 	{
   530 		DelayedKickInfo info = m_DelayedKicks.first();
   531 		m_DelayedKicks.pop();
   532 
   533 		CPlayer *player = g_Players.GetPlayerByIndex(info.client);
   534 		if (player == NULL || player->GetUserId() != info.userid)
   535 		{
   536 			continue;
   537 		}
   538 
   539 		player->Kick(info.buffer);
   540 	}
   541 }