core/HalfLife2.cpp
author David Anderson <dvander@alliedmods.net>
Wed Mar 12 03:33:52 2008 +0000 (2008-03-12)
changeset 1925 61d7a244b641
parent 1848 6214ae913a8a
child 1943 3bc85e217519
permissions -rw-r--r--
reworked antiflood. it now has some logic in core to take care of loading order nastiness, and to fully prevent trigger spamming
     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 		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 	FindInSendTable("CTFPlayer", "m_nDisguiseClass");
   119 }
   120 
   121 #if !defined METAMOD_PLAPI_VERSION
   122 bool CHalfLife2::IsOriginalEngine()
   123 {
   124 	return is_original_engine;
   125 }
   126 #endif
   127 
   128 IChangeInfoAccessor *CBaseEdict::GetChangeAccessor()
   129 {
   130 	return engine->GetChangeAccessor( (const edict_t *)this );
   131 }
   132 
   133 bool UTIL_FindInSendTable(SendTable *pTable, 
   134 						  const char *name,
   135 						  sm_sendprop_info_t *info,
   136 						  unsigned int offset)
   137 {
   138 	const char *pname;
   139 	int props = pTable->GetNumProps();
   140 	SendProp *prop;
   141 
   142 	for (int i=0; i<props; i++)
   143 	{
   144 		prop = pTable->GetProp(i);
   145 		pname = prop->GetName();
   146 		if (pname && strcmp(name, pname) == 0)
   147 		{
   148 			info->prop = prop;
   149 			info->actual_offset = offset + info->prop->GetOffset();
   150 			return true;
   151 		}
   152 		if (prop->GetDataTable())
   153 		{
   154 			if (UTIL_FindInSendTable(prop->GetDataTable(), 
   155 				name,
   156 				info,
   157 				offset + prop->GetOffset())
   158 				)
   159 			{
   160 				return true;
   161 			}
   162 		}
   163 	}
   164 
   165 	return false;
   166 }
   167 
   168 typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name)
   169 {
   170 	while (pMap)
   171 	{
   172 		for (int i=0; i<pMap->dataNumFields; i++)
   173 		{
   174 			if (pMap->dataDesc[i].fieldName == NULL)
   175 			{
   176 				continue;
   177 			}
   178 			if (strcmp(name, pMap->dataDesc[i].fieldName) == 0)
   179 			{
   180 				return &(pMap->dataDesc[i]);
   181 			}
   182 			if (pMap->dataDesc[i].td)
   183 			{
   184 				typedescription_t *_td;
   185 				if ((_td=UTIL_FindInDataMap(pMap->dataDesc[i].td, name)) != NULL)
   186 				{
   187 					return _td;
   188 				}
   189 			}
   190 		}
   191 		pMap = pMap->baseMap;
   192 	}
   193 
   194 	return NULL; 
   195 }
   196 
   197 ServerClass *CHalfLife2::FindServerClass(const char *classname)
   198 {
   199 	DataTableInfo *pInfo = _FindServerClass(classname);
   200 
   201 	if (!pInfo)
   202 	{
   203 		return NULL;
   204 	}
   205 
   206 	return pInfo->sc;
   207 }
   208 
   209 DataTableInfo *CHalfLife2::_FindServerClass(const char *classname)
   210 {
   211 	DataTableInfo *pInfo = NULL;
   212 
   213 	if (!sm_trie_retrieve(m_pClasses, classname, (void **)&pInfo))
   214 	{
   215 		ServerClass *sc = gamedll->GetAllServerClasses();
   216 		while (sc)
   217 		{
   218 			if (strcmp(classname, sc->GetName()) == 0)
   219 			{
   220 				pInfo = new DataTableInfo;
   221 				pInfo->sc = sc;
   222 				sm_trie_insert(m_pClasses, classname, pInfo);
   223 				m_Tables.push_back(pInfo);
   224 				break;
   225 			}
   226 			sc = sc->m_pNext;
   227 		}
   228 		if (!pInfo)
   229 		{
   230 			return NULL;
   231 		}
   232 	}
   233 
   234 	return pInfo;
   235 }
   236 
   237 bool CHalfLife2::FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info)
   238 {
   239 	DataTableInfo *pInfo;
   240 	sm_sendprop_info_t *prop;
   241 
   242 	if ((pInfo = _FindServerClass(classname)) == NULL)
   243 	{
   244 		return false;
   245 	}
   246 
   247 	if ((prop = pInfo->lookup.retrieve(offset)) == NULL)
   248 	{
   249 		sm_sendprop_info_t temp_info;
   250 
   251 		if (!UTIL_FindInSendTable(pInfo->sc->m_pTable, offset, &temp_info, 0))
   252 		{
   253 			return false;
   254 		}
   255 
   256 		pInfo->lookup.insert(offset, temp_info);
   257 		*info = temp_info;
   258 	}
   259 	else
   260 	{
   261 		*info = *prop;
   262 	}
   263 	
   264 	return true;
   265 }
   266 
   267 SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset)
   268 {
   269 	sm_sendprop_info_t info;
   270 
   271 	if (!FindSendPropInfo(classname, offset, &info))
   272 	{
   273 		return NULL;
   274 	}
   275 
   276 	return info.prop;
   277 }
   278 
   279 typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset)
   280 {
   281 	typedescription_t *td = NULL;
   282 	DataMapTrie &val = m_Maps[pMap];
   283 
   284 	if (!val.trie)
   285 	{
   286 		val.trie = sm_trie_create();
   287 	}
   288 	if (!sm_trie_retrieve(val.trie, offset, (void **)&td))
   289 	{
   290 		if ((td = UTIL_FindInDataMap(pMap, offset)) != NULL)
   291 		{
   292 			sm_trie_insert(val.trie, offset, td);
   293 		}
   294 	}
   295 
   296 	return td;
   297 }
   298 
   299 void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset)
   300 {
   301 	if (g_pSharedChangeInfo != NULL)
   302 	{
   303 		if (offset)
   304 		{
   305 			pEdict->StateChanged(offset);
   306 		}
   307 		else
   308 		{
   309 			pEdict->StateChanged();
   310 		}
   311 	}
   312 	else
   313 	{
   314 		pEdict->m_fStateFlags |= FL_EDICT_CHANGED;
   315 	}
   316 }
   317 
   318 bool CHalfLife2::TextMsg(int client, int dest, const char *msg)
   319 {
   320 	bf_write *pBitBuf = NULL;
   321 	cell_t players[] = {client};
   322 
   323 	if ((pBitBuf = g_UserMsgs.StartMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   324 	{
   325 		return false;
   326 	}
   327 
   328 	pBitBuf->WriteByte(dest);
   329 	pBitBuf->WriteString(msg);
   330 	g_UserMsgs.EndMessage();
   331 
   332 	return true;
   333 }
   334 
   335 bool CHalfLife2::HintTextMsg(int client, const char *msg)
   336 {
   337 	bf_write *pBitBuf = NULL;
   338 	cell_t players[] = {client};
   339 
   340 	if ((pBitBuf = g_UserMsgs.StartMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
   341 	{
   342 		return false;
   343 	}
   344 
   345 	const char *pre_byte = g_pGameConf->GetKeyValue("HintTextPreByte");
   346 	if (pre_byte != NULL && strcmp(pre_byte, "yes") == 0)
   347 	{
   348 		pBitBuf->WriteByte(1);
   349 	}
   350 	pBitBuf->WriteString(msg);
   351 	g_UserMsgs.EndMessage();
   352 
   353 	return true;
   354 }
   355 
   356 bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show)
   357 {
   358 	bf_write *pBitBuf = NULL;
   359 	KeyValues *SubKey = NULL;
   360 	int count = 0;
   361 	cell_t players[] = {client};
   362 
   363 	if ((pBitBuf = g_UserMsgs.StartMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
   364 	{
   365 		return false;
   366 	}
   367 
   368 	if (data)
   369 	{
   370 		SubKey = data->GetFirstSubKey();
   371 		while (SubKey)
   372 		{
   373 			count++;
   374 			SubKey = SubKey->GetNextKey();
   375 		}
   376 		SubKey = data->GetFirstSubKey();
   377 	}
   378 
   379 	pBitBuf->WriteString(name);
   380 	pBitBuf->WriteByte((show) ? 1 : 0);
   381 	pBitBuf->WriteByte(count);
   382 	while (SubKey)
   383 	{
   384 		pBitBuf->WriteString(SubKey->GetName());
   385 		pBitBuf->WriteString(SubKey->GetString());
   386 		SubKey = SubKey->GetNextKey();
   387 	}
   388 	g_UserMsgs.EndMessage();
   389 
   390 	return true;
   391 }
   392 
   393 void CHalfLife2::AddToFakeCliCmdQueue(int client, int userid, const char *cmd)
   394 {
   395 	DelayedFakeCliCmd *pFake;
   396 
   397 	if (m_FreeCmds.empty())
   398 	{
   399 		pFake = new DelayedFakeCliCmd;
   400 	} else {
   401 		pFake = m_FreeCmds.front();
   402 		m_FreeCmds.pop();
   403 	}
   404 
   405 	pFake->client = client;
   406 	pFake->userid = userid;
   407 	pFake->cmd.assign(cmd);
   408 
   409 	m_CmdQueue.push(pFake);
   410 }
   411 
   412 void CHalfLife2::ProcessFakeCliCmdQueue()
   413 {
   414 	while (!m_CmdQueue.empty())
   415 	{
   416 		DelayedFakeCliCmd *pFake = m_CmdQueue.first();
   417 
   418 		if (g_Players.GetClientOfUserId(pFake->userid) == pFake->client)
   419 		{
   420 			CPlayer *pPlayer = g_Players.GetPlayerByIndex(pFake->client);
   421 			serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), pFake->cmd.c_str());
   422 		}
   423 
   424 		m_CmdQueue.pop();
   425 	}
   426 }
   427 
   428 bool CHalfLife2::IsLANServer()
   429 {
   430 	sv_lan = icvar->FindVar("sv_lan");
   431 
   432 	if (!sv_lan)
   433 	{
   434 		return false;
   435 	}
   436 
   437 	return (sv_lan->GetInt() != 0);
   438 }
   439 
   440 void CHalfLife2::PushCommandStack(const CCommand *cmd)
   441 {
   442 	CachedCommandInfo info;
   443 
   444 	info.args = cmd;
   445 #if !defined ORANGEBOX_BUILD
   446 	strncopy(info.cmd, cmd->Arg(0), sizeof(info.cmd));
   447 #endif
   448 
   449 	m_CommandStack.push(info);
   450 }
   451 
   452 const CCommand *CHalfLife2::PeekCommandStack()
   453 {
   454 	if (m_CommandStack.empty())
   455 	{
   456 		return NULL;
   457 	}
   458 
   459 	return m_CommandStack.front().args;
   460 }
   461 
   462 void CHalfLife2::PopCommandStack()
   463 {
   464 	m_CommandStack.pop();
   465 }
   466 
   467 const char *CHalfLife2::CurrentCommandName()
   468 {
   469 #if defined ORANGEBOX_BUILD
   470 	return m_CommandStack.front().args->Arg(0);
   471 #else
   472 	return m_CommandStack.front().cmd;
   473 #endif
   474 }