core/HalfLife2.cpp
author Michael McKoy <ferret99gt@gmail.com>
Sat Nov 24 03:28:33 2007 +0000 (2007-11-24)
changeset 1719 c9d5e55f0899
parent 1690 7cc4f79a0416
child 1847 5336a438763f
permissions -rw-r--r--
The current map played shouldn't appear in MapChooser votes or RTV votes now. It can still be nominated in RTV though.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 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 		sm_trie_destroy(pInfo->lookup);
    74 		delete pInfo;
    75 	}
    76 
    77 	m_Tables.clear();
    78 
    79 	THash<datamap_t *, DataMapTrie>::iterator h_iter;
    80 	for (h_iter=m_Maps.begin(); h_iter!=m_Maps.end(); h_iter++)
    81 	{
    82 		if (h_iter->val.trie)
    83 		{
    84 			sm_trie_destroy(h_iter->val.trie);
    85 			h_iter->val.trie = NULL;
    86 		}
    87 	}
    88 
    89 	m_Maps.clear();
    90 }
    91 
    92 CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
    93 bool is_original_engine = false;
    94 
    95 void CHalfLife2::OnSourceModStartup(bool late)
    96 {
    97 	/* The Ship currently is the only known game to use an older version of the engine */
    98 #if defined METAMOD_PLAPI_VERSION
    99 	if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
   100 #else
   101 	if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
   102 #endif
   103 	{
   104 		is_original_engine = true;
   105 	}
   106 	else if (g_pSharedChangeInfo == NULL)
   107 	{
   108 		g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo();
   109 	}
   110 }
   111 
   112 void CHalfLife2::OnSourceModAllInitialized()
   113 {
   114 	m_MsgTextMsg = g_UserMsgs.GetMessageIndex("TextMsg");
   115 	m_HinTextMsg = g_UserMsgs.GetMessageIndex("HintText");
   116 	m_VGUIMenu = g_UserMsgs.GetMessageIndex("VGUIMenu");
   117 	g_ShareSys.AddInterface(NULL, this);
   118 }
   119 
   120 #if !defined METAMOD_PLAPI_VERSION
   121 bool CHalfLife2::IsOriginalEngine()
   122 {
   123 	return is_original_engine;
   124 }
   125 #endif
   126 
   127 IChangeInfoAccessor *CBaseEdict::GetChangeAccessor()
   128 {
   129 	return engine->GetChangeAccessor( (const edict_t *)this );
   130 }
   131 
   132 SendProp *UTIL_FindInSendTable(SendTable *pTable, const char *name)
   133 {
   134 	const char *pname;
   135 	int props = pTable->GetNumProps();
   136 	SendProp *prop;
   137 
   138 	for (int i=0; i<props; i++)
   139 	{
   140 		prop = pTable->GetProp(i);
   141 		pname = prop->GetName();
   142 		if (pname && strcmp(name, pname) == 0)
   143 		{
   144 			return prop;
   145 		}
   146 		if (prop->GetDataTable())
   147 		{
   148 			if ((prop=UTIL_FindInSendTable(prop->GetDataTable(), name)) != NULL)
   149 			{
   150 				return prop;
   151 			}
   152 		}
   153 	}
   154 
   155 	return NULL;
   156 }
   157 
   158 typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name)
   159 {
   160 	while (pMap)
   161 	{
   162 		for (int i=0; i<pMap->dataNumFields; i++)
   163 		{
   164 			if (pMap->dataDesc[i].fieldName == NULL)
   165 			{
   166 				continue;
   167 			}
   168 			if (strcmp(name, pMap->dataDesc[i].fieldName) == 0)
   169 			{
   170 				return &(pMap->dataDesc[i]);
   171 			}
   172 			if (pMap->dataDesc[i].td)
   173 			{
   174 				typedescription_t *_td;
   175 				if ((_td=UTIL_FindInDataMap(pMap->dataDesc[i].td, name)) != NULL)
   176 				{
   177 					return _td;
   178 				}
   179 			}
   180 		}
   181 		pMap = pMap->baseMap;
   182 	}
   183 
   184 	return NULL; 
   185 }
   186 
   187 ServerClass *CHalfLife2::FindServerClass(const char *classname)
   188 {
   189 	DataTableInfo *pInfo = _FindServerClass(classname);
   190 	if (!pInfo)
   191 	{
   192 		return NULL;
   193 	}
   194 
   195 	return pInfo->sc;
   196 }
   197 
   198 DataTableInfo *CHalfLife2::_FindServerClass(const char *classname)
   199 {
   200 	DataTableInfo *pInfo = NULL;
   201 
   202 	if (!sm_trie_retrieve(m_pClasses, classname, (void **)&pInfo))
   203 	{
   204 		ServerClass *sc = gamedll->GetAllServerClasses();
   205 		while (sc)
   206 		{
   207 			if (strcmp(classname, sc->GetName()) == 0)
   208 			{
   209 				pInfo = new DataTableInfo;
   210 				pInfo->lookup = sm_trie_create();
   211 				pInfo->sc = sc;
   212 				sm_trie_insert(m_pClasses, classname, pInfo);
   213 				m_Tables.push_back(pInfo);
   214 				break;
   215 			}
   216 			sc = sc->m_pNext;
   217 		}
   218 		if (!pInfo)
   219 		{
   220 			return NULL;
   221 		}
   222 	}
   223 
   224 	return pInfo;
   225 }
   226 
   227 SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset)
   228 {
   229 	DataTableInfo *pInfo = _FindServerClass(classname);
   230 
   231 	if (!pInfo)
   232 	{
   233 		return NULL;
   234 	}
   235 
   236 	SendProp *pProp = NULL;
   237 	if (!sm_trie_retrieve(pInfo->lookup, offset, (void **)&pProp))
   238 	{
   239 		if ((pProp = UTIL_FindInSendTable(pInfo->sc->m_pTable, offset)) != NULL)
   240 		{
   241 			sm_trie_insert(pInfo->lookup, offset, pProp);
   242 		}
   243 	}
   244 
   245 	return pProp;
   246 }
   247 
   248 typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset)
   249 {
   250 	typedescription_t *td = NULL;
   251 	DataMapTrie &val = m_Maps[pMap];
   252 
   253 	if (!val.trie)
   254 	{
   255 		val.trie = sm_trie_create();
   256 	}
   257 	if (!sm_trie_retrieve(val.trie, offset, (void **)&td))
   258 	{
   259 		if ((td = UTIL_FindInDataMap(pMap, offset)) != NULL)
   260 		{
   261 			sm_trie_insert(val.trie, offset, td);
   262 		}
   263 	}
   264 
   265 	return td;
   266 }
   267 
   268 void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset)
   269 {
   270 	if (g_pSharedChangeInfo != NULL)
   271 	{
   272 		if (offset)
   273 		{
   274 			pEdict->StateChanged(offset);
   275 		} else {
   276 			pEdict->StateChanged();
   277 		}
   278 	}
   279 	else
   280 	{
   281 		pEdict->m_fStateFlags |= FL_EDICT_CHANGED;
   282 	}
   283 }
   284 
   285 bool CHalfLife2::TextMsg(int client, int dest, const char *msg)
   286 {
   287 	bf_write *pBitBuf = NULL;
   288 	cell_t players[] = {client};
   289 
   290 	if ((pBitBuf = g_UserMsgs.StartMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   291 	{
   292 		return false;
   293 	}
   294 
   295 	pBitBuf->WriteByte(dest);
   296 	pBitBuf->WriteString(msg);
   297 	g_UserMsgs.EndMessage();
   298 
   299 	return true;
   300 }
   301 
   302 bool CHalfLife2::HintTextMsg(int client, const char *msg)
   303 {
   304 	bf_write *pBitBuf = NULL;
   305 	cell_t players[] = {client};
   306 
   307 	if ((pBitBuf = g_UserMsgs.StartMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   308 	{
   309 		return false;
   310 	}
   311 
   312 	const char *pre_byte = g_pGameConf->GetKeyValue("HintTextPreByte");
   313 	if (pre_byte != NULL && strcmp(pre_byte, "yes") == 0)
   314 	{
   315 		pBitBuf->WriteByte(1);
   316 	}
   317 	pBitBuf->WriteString(msg);
   318 	g_UserMsgs.EndMessage();
   319 
   320 	return true;
   321 }
   322 
   323 bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show)
   324 {
   325 	bf_write *pBitBuf = NULL;
   326 	KeyValues *SubKey = NULL;
   327 	int count = 0;
   328 	cell_t players[] = {client};
   329 
   330 	if ((pBitBuf = g_UserMsgs.StartMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
   331 	{
   332 		return false;
   333 	}
   334 
   335 	if (data)
   336 	{
   337 		SubKey = data->GetFirstSubKey();
   338 		while (SubKey)
   339 		{
   340 			count++;
   341 			SubKey = SubKey->GetNextKey();
   342 		}
   343 		SubKey = data->GetFirstSubKey();
   344 	}
   345 
   346 	pBitBuf->WriteString(name);
   347 	pBitBuf->WriteByte((show) ? 1 : 0);
   348 	pBitBuf->WriteByte(count);
   349 	while (SubKey)
   350 	{
   351 		pBitBuf->WriteString(SubKey->GetName());
   352 		pBitBuf->WriteString(SubKey->GetString());
   353 		SubKey = SubKey->GetNextKey();
   354 	}
   355 	g_UserMsgs.EndMessage();
   356 
   357 	return true;
   358 }
   359 
   360 void CHalfLife2::AddToFakeCliCmdQueue(int client, int userid, const char *cmd)
   361 {
   362 	DelayedFakeCliCmd *pFake;
   363 
   364 	if (m_FreeCmds.empty())
   365 	{
   366 		pFake = new DelayedFakeCliCmd;
   367 	} else {
   368 		pFake = m_FreeCmds.front();
   369 		m_FreeCmds.pop();
   370 	}
   371 
   372 	pFake->client = client;
   373 	pFake->userid = userid;
   374 	pFake->cmd.assign(cmd);
   375 
   376 	m_CmdQueue.push(pFake);
   377 }
   378 
   379 void CHalfLife2::ProcessFakeCliCmdQueue()
   380 {
   381 	while (!m_CmdQueue.empty())
   382 	{
   383 		DelayedFakeCliCmd *pFake = m_CmdQueue.first();
   384 
   385 		if (g_Players.GetClientOfUserId(pFake->userid) == pFake->client)
   386 		{
   387 			CPlayer *pPlayer = g_Players.GetPlayerByIndex(pFake->client);
   388 			serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), pFake->cmd.c_str());
   389 		}
   390 
   391 		m_CmdQueue.pop();
   392 	}
   393 }
   394 
   395 bool CHalfLife2::IsLANServer()
   396 {
   397 	sv_lan = icvar->FindVar("sv_lan");
   398 
   399 	if (!sv_lan)
   400 	{
   401 		return false;
   402 	}
   403 
   404 	return (sv_lan->GetInt() != 0);
   405 }
   406 
   407 void CHalfLife2::PushCommandStack(const CCommand *cmd)
   408 {
   409 	CachedCommandInfo info;
   410 
   411 	info.args = cmd;
   412 #if !defined ORANGEBOX_BUILD
   413 	strncopy(info.cmd, cmd->Arg(0), sizeof(info.cmd));
   414 #endif
   415 
   416 	m_CommandStack.push(info);
   417 }
   418 
   419 const CCommand *CHalfLife2::PeekCommandStack()
   420 {
   421 	if (m_CommandStack.empty())
   422 	{
   423 		return NULL;
   424 	}
   425 
   426 	return m_CommandStack.front().args;
   427 }
   428 
   429 void CHalfLife2::PopCommandStack()
   430 {
   431 	m_CommandStack.pop();
   432 }
   433 
   434 const char *CHalfLife2::CurrentCommandName()
   435 {
   436 #if defined ORANGEBOX_BUILD
   437 	return m_CommandStack.front().args->Arg(0);
   438 #else
   439 	return m_CommandStack.front().cmd;
   440 #endif
   441 }