core/PlayerManager.cpp
author David Anderson <dvander@alliedmods.net>
Sat Dec 22 08:57:08 2007 +0000 (2007-12-22)
changeset 1813 372939c88bc7
parent 1688 513a3b2dd86d
child 1831 af5c93c936d7
permissions -rw-r--r--
fixed amb1305 by making OnClientPostAdminCheck() always occur after OnClientPutInServer(). this is now GUARANTEED. it was undefined before, however the usual case was opposite. if plugins were relying on this, they were written poorly and shouldn't have worked in the first place, especially with the threaded SQL plugin.
     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 "PlayerManager.h"
    33 #include "ForwardSys.h"
    34 #include "ShareSys.h"
    35 #include "AdminCache.h"
    36 #include "ConCmdManager.h"
    37 #include "MenuStyle_Valve.h"
    38 #include "MenuStyle_Radio.h"
    39 #include "sm_stringutil.h"
    40 #include "CoreConfig.h"
    41 #include "TimerSys.h"
    42 #include "Translator.h"
    43 #include "Logger.h"
    44 #include "ChatTriggers.h"
    45 #include "HalfLife2.h"
    46 #include <inetchannel.h>
    47 #include <iclient.h>
    48 #include "GameConfigs.h"
    49 
    50 PlayerManager g_Players;
    51 bool g_OnMapStarted = false;
    52 IForward *PreAdminCheck = NULL;
    53 IForward *PostAdminCheck = NULL;
    54 const unsigned int *g_NumPlayersToAuth = NULL;
    55 int lifestate_offset = -1;
    56 List<ICommandTargetProcessor *> target_processors;
    57 
    58 SH_DECL_HOOK5(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, edict_t *, const char *, const char *, char *, int);
    59 SH_DECL_HOOK2_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, edict_t *, const char *);
    60 SH_DECL_HOOK1_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, edict_t *);
    61 #if defined ORANGEBOX_BUILD
    62 SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *, const CCommand &);
    63 #else
    64 SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *);
    65 #endif
    66 SH_DECL_HOOK1_void(IServerGameClients, ClientSettingsChanged, SH_NOATTRIB, 0, edict_t *);
    67 SH_DECL_HOOK3_void(IServerGameDLL, ServerActivate, SH_NOATTRIB, 0, edict_t *, int, int);
    68 
    69 class KickPlayerTimer : public ITimedEvent
    70 {
    71 public:
    72 	ResultType OnTimer(ITimer *pTimer, void *pData)
    73 	{
    74 		int userid = (int)pData;
    75 		int client = g_Players.GetClientOfUserId(userid);
    76 		if (client)
    77 		{
    78 			CPlayer *player = g_Players.GetPlayerByIndex(client);
    79 			player->Kick("Your name is reserved by SourceMod; set your password to use it.");
    80 		}
    81 		return Pl_Stop;
    82 	}
    83 	void OnTimerEnd(ITimer *pTimer, void *pData)
    84 	{
    85 	}
    86 } s_KickPlayerTimer;
    87 
    88 PlayerManager::PlayerManager()
    89 {
    90 	m_AuthQueue = NULL;
    91 	m_FirstPass = false;
    92 
    93 	m_UserIdLookUp = new int[USHRT_MAX+1];
    94 	memset(m_UserIdLookUp, 0, sizeof(int) * (USHRT_MAX+1));
    95 }
    96 
    97 PlayerManager::~PlayerManager()
    98 {
    99 	g_NumPlayersToAuth = NULL;
   100 
   101 	delete [] m_AuthQueue;
   102 	delete [] m_UserIdLookUp;
   103 }
   104 
   105 void PlayerManager::OnSourceModAllInitialized()
   106 {
   107 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &PlayerManager::OnClientConnect, false);
   108 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &PlayerManager::OnClientConnect_Post, true);
   109 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, serverClients, this, &PlayerManager::OnClientPutInServer, true);
   110 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &PlayerManager::OnClientDisconnect, false);
   111 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &PlayerManager::OnClientDisconnect_Post, true);
   112 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientCommand, serverClients, this, &PlayerManager::OnClientCommand, false);
   113 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, serverClients, this, &PlayerManager::OnClientSettingsChanged, true);
   114 	SH_ADD_HOOK_MEMFUNC(IServerGameDLL, ServerActivate, gamedll, this, &PlayerManager::OnServerActivate, true);
   115 
   116 	g_ShareSys.AddInterface(NULL, this);
   117 
   118 	ParamType p1[] = {Param_Cell, Param_String, Param_Cell};
   119 	ParamType p2[] = {Param_Cell};
   120 
   121 	m_clconnect = g_Forwards.CreateForward("OnClientConnect", ET_Event, 3, p1);
   122 	m_clputinserver = g_Forwards.CreateForward("OnClientPutInServer", ET_Ignore, 1, p2);
   123 	m_cldisconnect = g_Forwards.CreateForward("OnClientDisconnect", ET_Ignore, 1, p2);
   124 	m_cldisconnect_post = g_Forwards.CreateForward("OnClientDisconnect_Post", ET_Ignore, 1, p2);
   125 	m_clcommand = g_Forwards.CreateForward("OnClientCommand", ET_Hook, 2, NULL, Param_Cell, Param_Cell);
   126 	m_clinfochanged = g_Forwards.CreateForward("OnClientSettingsChanged", ET_Ignore, 1, p2);
   127 	m_clauth = g_Forwards.CreateForward("OnClientAuthorized", ET_Ignore, 2, NULL, Param_Cell, Param_String);
   128 	m_onActivate = g_Forwards.CreateForward("OnServerLoad", ET_Ignore, 0, NULL);
   129 	m_onActivate2 = g_Forwards.CreateForward("OnMapStart", ET_Ignore, 0, NULL);
   130 
   131 	PreAdminCheck = g_Forwards.CreateForward("OnClientPreAdminCheck", ET_Event, 1, p1);
   132 	PostAdminCheck = g_Forwards.CreateForward("OnClientPostAdminCheck", ET_Ignore, 1, p1);
   133 }
   134 
   135 void PlayerManager::OnSourceModShutdown()
   136 {
   137 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &PlayerManager::OnClientConnect, false);
   138 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, serverClients, this, &PlayerManager::OnClientPutInServer, true);
   139 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &PlayerManager::OnClientDisconnect, false);
   140 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &PlayerManager::OnClientDisconnect_Post, true);
   141 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientCommand, serverClients, this, &PlayerManager::OnClientCommand, false);
   142 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, serverClients, this, &PlayerManager::OnClientSettingsChanged, true);
   143 	SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, ServerActivate, gamedll, this, &PlayerManager::OnServerActivate, true);
   144 
   145 	/* Release forwards */
   146 	g_Forwards.ReleaseForward(m_clconnect);
   147 	g_Forwards.ReleaseForward(m_clputinserver);
   148 	g_Forwards.ReleaseForward(m_cldisconnect);
   149 	g_Forwards.ReleaseForward(m_cldisconnect_post);
   150 	g_Forwards.ReleaseForward(m_clcommand);
   151 	g_Forwards.ReleaseForward(m_clinfochanged);
   152 	g_Forwards.ReleaseForward(m_clauth);
   153 	g_Forwards.ReleaseForward(m_onActivate);
   154 	g_Forwards.ReleaseForward(m_onActivate2);
   155 
   156 	g_Forwards.ReleaseForward(PreAdminCheck);
   157 	g_Forwards.ReleaseForward(PostAdminCheck);
   158 
   159 	delete [] m_Players;
   160 }
   161 
   162 ConfigResult PlayerManager::OnSourceModConfigChanged(const char *key, 
   163 												 const char *value, 
   164 												 ConfigSource source, 
   165 												 char *error, 
   166 												 size_t maxlength)
   167 {
   168 	if (strcmp(key, "PassInfoVar") == 0)
   169 	{
   170 		if (strcmp(value, "_password") != 0)
   171 		{
   172 			m_PassInfoVar.assign(value);
   173 		}
   174 		return ConfigResult_Accept;
   175 	} else if (strcmp(key, "AllowClLanguageVar") == 0) {
   176 		if (strcasecmp(value, "on") == 0)
   177 		{
   178 			m_QueryLang = true;
   179 		} else if (strcasecmp(value, "off") == 0) {
   180 			m_QueryLang = false;
   181 		} else {
   182 			UTIL_Format(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
   183 			return ConfigResult_Reject;
   184 		}
   185 		return ConfigResult_Accept;
   186 	}
   187 	return ConfigResult_Ignore;
   188 }
   189 
   190 void PlayerManager::OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
   191 {
   192 	if (!m_FirstPass)
   193 	{
   194 		/* Initialize all players */
   195 		m_maxClients = clientMax;
   196 		m_PlayerCount = 0;
   197 		m_Players = new CPlayer[m_maxClients + 1];
   198 		m_AuthQueue = new unsigned int[m_maxClients + 1];
   199 		m_FirstPass = true;
   200 
   201 		memset(m_AuthQueue, 0, sizeof(unsigned int) * (m_maxClients + 1));
   202 
   203 		g_NumPlayersToAuth = &m_AuthQueue[0];
   204 	}
   205 	m_onActivate->Execute(NULL);
   206 	m_onActivate2->Execute(NULL);
   207 
   208 	List<IClientListener *>::iterator iter;
   209 	for (iter = m_hooks.begin(); iter != m_hooks.end(); iter++)
   210 	{
   211 		if ((*iter)->GetClientListenerVersion() >= 5)
   212 		{
   213 			(*iter)->OnServerActivated(clientMax);
   214 		}
   215 	}
   216 
   217 	g_OnMapStarted = true;
   218 
   219 	SM_ExecuteAllConfigs();
   220 }
   221 
   222 bool PlayerManager::IsServerActivated()
   223 {
   224 	return m_FirstPass;
   225 }
   226 
   227 bool PlayerManager::CheckSetAdmin(int index, CPlayer *pPlayer, AdminId id)
   228 {
   229 	const char *password = g_Admins.GetAdminPassword(id);
   230 	if (password != NULL)
   231 	{
   232 		if (m_PassInfoVar.size() < 1)
   233 		{
   234 			return false;
   235 		}
   236 
   237 		/* Whoa... the user needs a password! */
   238 		const char *given = engine->GetClientConVarValue(index, m_PassInfoVar.c_str());
   239 		if (!given || strcmp(given, password) != 0)
   240 		{
   241 			return false;
   242 		}
   243 	}
   244 
   245 	pPlayer->SetAdminId(id, false);
   246 
   247 	return true;
   248 }
   249 
   250 bool PlayerManager::CheckSetAdminName(int index, CPlayer *pPlayer, AdminId id)
   251 {
   252 	const char *password = g_Admins.GetAdminPassword(id);
   253 	if (password == NULL)
   254 	{
   255 		return false;
   256 	}
   257 
   258 	if (m_PassInfoVar.size() < 1)
   259 	{
   260 		return false;
   261 	}
   262 
   263 	/* Whoa... the user needs a password! */
   264 	const char *given = engine->GetClientConVarValue(index, m_PassInfoVar.c_str());
   265 	if (!given || strcmp(given, password) != 0)
   266 	{
   267 		return false;
   268 	}
   269 
   270 	pPlayer->SetAdminId(id, false);
   271 
   272 	return true;
   273 }
   274 
   275 void PlayerManager::RunAuthChecks()
   276 {
   277 	CPlayer *pPlayer;
   278 	const char *authstr;
   279 	unsigned int removed = 0;
   280 	for (unsigned int i=1; i<=m_AuthQueue[0]; i++)
   281 	{
   282 		pPlayer = GetPlayerByIndex(m_AuthQueue[i]);
   283 		authstr = engine->GetPlayerNetworkIDString(pPlayer->m_pEdict);
   284 		if (authstr && authstr[0] != '\0'
   285 			&& (strcmp(authstr, "STEAM_ID_PENDING") != 0))
   286 		{
   287 			/* Set authorization */
   288 			pPlayer->Authorize(authstr);
   289 
   290 			/* Mark as removed from queue */
   291 			unsigned int client = m_AuthQueue[i];
   292 			m_AuthQueue[i] = 0;
   293 			removed++;
   294 
   295 			/* Send to extensions */
   296 			List<IClientListener *>::iterator iter;
   297 			IClientListener *pListener;
   298 			for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   299 			{
   300 				pListener = (*iter);
   301 				pListener->OnClientAuthorized(client, authstr);
   302 				if (!pPlayer->IsConnected())
   303 				{
   304 					break;
   305 				}
   306 			}
   307 
   308 			/* Send to plugins if player is still connected */
   309 			if (pPlayer->IsConnected() && m_clauth->GetFunctionCount())
   310 			{
   311 				/* :TODO: handle the case of a player disconnecting in the middle */
   312 				m_clauth->PushCell(client);
   313 				m_clauth->PushString(authstr);
   314 				m_clauth->Execute(NULL);
   315 			}
   316 
   317 			if (pPlayer->IsConnected())
   318 			{
   319 				pPlayer->Authorize_Post();
   320 			}
   321 		}
   322 	}
   323 
   324 	/* Clean up the queue */
   325 	if (removed)
   326 	{
   327 		/* We don't have to compact the list if it's empty */
   328 		if (removed != m_AuthQueue[0])
   329 		{
   330 			unsigned int diff = 0;
   331 			for (unsigned int i=1; i<=m_AuthQueue[0]; i++)
   332 			{
   333 				/* If this member is removed... */
   334 				if (m_AuthQueue[i] == 0)
   335 				{
   336 					/* Increase the differential */
   337 					diff++;
   338 				} else {
   339 					/* diff cannot increase faster than i+1 */
   340 					assert(i > diff);
   341 					assert(i - diff >= 1);
   342 					/* move this index down */
   343 					m_AuthQueue[i - diff] = m_AuthQueue[i];
   344 				}
   345 			}
   346 			m_AuthQueue[0] -= removed;
   347 		} else {
   348 			m_AuthQueue[0] = 0;
   349 		}
   350 	}
   351 }
   352 
   353 bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen)
   354 {
   355 	int client = engine->IndexOfEdict(pEntity);
   356 
   357 	List<IClientListener *>::iterator iter;
   358 	IClientListener *pListener = NULL;
   359 	for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   360 	{
   361 		pListener = (*iter);
   362 		if (!pListener->InterceptClientConnect(client, reject, maxrejectlen))
   363 		{
   364 			return false;
   365 		}
   366 	}
   367 
   368 	cell_t res = 1;
   369 
   370 	m_Players[client].Initialize(pszName, pszAddress, pEntity);
   371 	m_clconnect->PushCell(client);
   372 	m_clconnect->PushStringEx(reject, maxrejectlen, SM_PARAM_STRING_UTF8, SM_PARAM_COPYBACK);
   373 	m_clconnect->PushCell(maxrejectlen);
   374 	m_clconnect->Execute(&res, NULL);
   375 
   376 	if (res)
   377 	{
   378 		if (!m_Players[client].IsAuthorized())
   379 		{
   380 			m_AuthQueue[++m_AuthQueue[0]] = client;
   381 		}
   382 	} else {
   383 		RETURN_META_VALUE(MRES_SUPERCEDE, false);
   384 	}
   385 
   386 	m_UserIdLookUp[engine->GetPlayerUserId(pEntity)] = client;
   387 
   388 	return true;
   389 }
   390 
   391 bool PlayerManager::OnClientConnect_Post(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen)
   392 {
   393 	int client = engine->IndexOfEdict(pEntity);
   394 	bool orig_value = META_RESULT_ORIG_RET(bool);
   395 	CPlayer *pPlayer = GetPlayerByIndex(client);
   396 
   397 	if (orig_value)
   398 	{
   399 		List<IClientListener *>::iterator iter;
   400 		IClientListener *pListener = NULL;
   401 		for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   402 		{
   403 			pListener = (*iter);
   404 			pListener->OnClientConnected(client);
   405 			if (!pPlayer->IsConnected())
   406 			{
   407 				break;
   408 			}
   409 		}
   410 	}
   411 
   412 	return true;
   413 }
   414 
   415 void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername)
   416 {
   417 	cell_t res;
   418 	int client = engine->IndexOfEdict(pEntity);
   419 
   420 	CPlayer *pPlayer = GetPlayerByIndex(client);
   421 	/* If they're not connected, they're a bot */
   422 	if (!pPlayer->IsConnected())
   423 	{
   424 		/* Run manual connection routines */
   425 		char error[255];
   426 		const char *authid = engine->GetPlayerNetworkIDString(pEntity);
   427 		pPlayer->Authorize(authid);
   428 		if (!OnClientConnect(pEntity, playername, "127.0.0.1", error, sizeof(error)))
   429 		{
   430 			/* :TODO: kick the bot if it's rejected */
   431 			return;
   432 		}
   433 		List<IClientListener *>::iterator iter;
   434 		IClientListener *pListener = NULL;
   435 		for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   436 		{
   437 			pListener = (*iter);
   438 			pListener->OnClientConnected(client);
   439 			/* See if bot was kicked */
   440 			if (!pPlayer->IsConnected())
   441 			{
   442 				return;
   443 			}
   444 		}
   445 		/* Now do authorization */
   446 		for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   447 		{
   448 			pListener = (*iter);
   449 			pListener->OnClientAuthorized(client, authid);
   450 		}
   451 		/* Finally, tell plugins */
   452 		if (m_clauth->GetFunctionCount())
   453 		{
   454 			m_clauth->PushCell(client);
   455 			m_clauth->PushString(authid);
   456 			m_clauth->Execute(NULL);
   457 		}
   458 		pPlayer->Authorize_Post();
   459 	}
   460 
   461 	if (playerinfo)
   462 	{
   463 		pPlayer->m_Info = playerinfo->GetPlayerInfo(pEntity);
   464 	}
   465 
   466 	/* Get the client's language */
   467 	if (m_QueryLang)
   468 	{
   469 		const char *name;
   470 		if (!pPlayer->IsFakeClient() && (name=engine->GetClientConVarValue(client, "cl_language")))
   471 		{
   472 			unsigned int langid;
   473 			pPlayer->m_LangId = (g_Translator.GetLanguageByName(name, &langid)) ? langid : g_Translator.GetServerLanguage();
   474 		} else {
   475 			pPlayer->m_LangId = g_Translator.GetServerLanguage();
   476 		}
   477 	}
   478 
   479 	m_Players[client].Connect();
   480 	m_PlayerCount++;
   481 
   482 	List<IClientListener *>::iterator iter;
   483 	IClientListener *pListener = NULL;
   484 	for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   485 	{
   486 		pListener = (*iter);
   487 		pListener->OnClientPutInServer(client);
   488 	}
   489 
   490 	m_clputinserver->PushCell(client);
   491 	m_clputinserver->Execute(&res, NULL);
   492 
   493 	if (m_Players[client].IsAuthorized())
   494 	{
   495 		m_Players[client].DoPostConnectAuthorization();
   496 	}
   497 }
   498 
   499 void PlayerManager::OnSourceModLevelEnd()
   500 {
   501 	/* Disconnect all bots still in game */
   502 	for (int i=1; i<=m_maxClients; i++)
   503 	{
   504 		if (m_Players[i].IsConnected())
   505 		{
   506 			OnClientDisconnect(m_Players[i].GetEdict());
   507 		}
   508 	}
   509 	m_PlayerCount = 0;
   510 }
   511 
   512 void PlayerManager::OnClientDisconnect(edict_t *pEntity)
   513 {
   514 	cell_t res;
   515 	int client = engine->IndexOfEdict(pEntity);
   516 
   517 	if (m_Players[client].IsConnected())
   518 	{
   519 		m_cldisconnect->PushCell(client);
   520 		m_cldisconnect->Execute(&res, NULL);
   521 	} else {
   522 		/* We don't care, prevent a double call */
   523 		return;
   524 	}
   525 
   526 	if (m_Players[client].WasCountedAsInGame())
   527 	{
   528 		m_PlayerCount--;
   529 	}
   530 
   531 	List<IClientListener *>::iterator iter;
   532 	IClientListener *pListener = NULL;
   533 	for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   534 	{
   535 		pListener = (*iter);
   536 		pListener->OnClientDisconnecting(client);
   537 	}
   538 
   539 	/**
   540 	 * Remove client from auth queue if necessary
   541 	 */
   542 	if (!m_Players[client].IsAuthorized())
   543 	{
   544 		for (unsigned int i=1; i<=m_AuthQueue[0]; i++)
   545 		{
   546 			if (m_AuthQueue[i] == (unsigned)client)
   547 			{
   548 				/* Move everything ahead of us back by one */
   549 				for (unsigned int j=i+1; j<=m_AuthQueue[0]; j++)
   550 				{
   551 					m_AuthQueue[j-1] = m_AuthQueue[j];
   552 				}
   553 				/* Remove us and break */
   554 				m_AuthQueue[0]--;
   555 				break;
   556 			}
   557 		}
   558 	}
   559 
   560 	m_Players[client].Disconnect();
   561 	m_UserIdLookUp[engine->GetPlayerUserId(pEntity)] = 0;
   562 }
   563 
   564 void PlayerManager::OnClientDisconnect_Post(edict_t *pEntity)
   565 {
   566 	cell_t res;
   567 	int client = engine->IndexOfEdict(pEntity);
   568 
   569 	m_cldisconnect_post->PushCell(client);
   570 	m_cldisconnect_post->Execute(&res, NULL);
   571 
   572 	List<IClientListener *>::iterator iter;
   573 	IClientListener *pListener = NULL;
   574 	for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   575 	{
   576 		pListener = (*iter);
   577 		pListener->OnClientDisconnected(client);
   578 	}
   579 }
   580 
   581 #if defined ORANGEBOX_BUILD
   582 void PlayerManager::OnClientCommand(edict_t *pEntity, const CCommand &args)
   583 {
   584 #else
   585 void PlayerManager::OnClientCommand(edict_t *pEntity)
   586 {
   587 	CCommand args;
   588 #endif
   589 	int client = engine->IndexOfEdict(pEntity);
   590 	cell_t res = Pl_Continue;
   591 
   592 	CPlayer *pPlayer = GetPlayerByIndex(client);
   593 	if (!pPlayer || !pPlayer->IsConnected())
   594 	{
   595 		return;
   596 	}
   597 
   598 	g_HL2.PushCommandStack(&args);
   599 
   600 	int argcount = args.ArgC() - 1;
   601 	const char *cmd = g_HL2.CurrentCommandName();
   602 
   603 	bool result = g_ValveMenuStyle.OnClientCommand(client, cmd, args);
   604 	if (result)
   605 	{
   606 		res = Pl_Handled;
   607 	} else {
   608 		result = g_RadioMenuStyle.OnClientCommand(client, cmd, args);
   609 		if (result)
   610 		{
   611 			res = Pl_Handled;
   612 		}
   613 	}
   614 
   615 	cell_t res2 = Pl_Continue;
   616 	m_clcommand->PushCell(client);
   617 	m_clcommand->PushCell(argcount);
   618 	m_clcommand->Execute(&res2, NULL);
   619 
   620 	if (res2 > res)
   621 	{
   622 		res = res2;
   623 	}
   624 
   625 	if (res >= Pl_Stop)
   626 	{
   627 		g_HL2.PopCommandStack();
   628 		RETURN_META(MRES_SUPERCEDE);
   629 	}
   630 
   631 	res = g_ConCmds.DispatchClientCommand(client, cmd, argcount, (ResultType)res);
   632 
   633 	g_HL2.PopCommandStack();
   634 
   635 	if (res >= Pl_Handled)
   636 	{
   637 		RETURN_META(MRES_SUPERCEDE);
   638 	}
   639 }
   640 
   641 void PlayerManager::OnClientSettingsChanged(edict_t *pEntity)
   642 {
   643 	cell_t res;
   644 	int client = engine->IndexOfEdict(pEntity);
   645 
   646 	if (!m_Players[client].IsConnected())
   647 	{
   648 		return;
   649 	}
   650 
   651 	m_clinfochanged->PushCell(engine->IndexOfEdict(pEntity));
   652 	m_clinfochanged->Execute(&res, NULL);
   653 
   654 	IPlayerInfo *info = m_Players[client].GetPlayerInfo();
   655 	const char *new_name = info ? info->GetName() : engine->GetClientConVarValue(client, "name");
   656 	const char *old_name = m_Players[client].m_Name.c_str();
   657 
   658 	if (strcmp(old_name, new_name) != 0)
   659 	{
   660 		AdminId id = g_Admins.FindAdminByIdentity("name", new_name);
   661 		if (id != INVALID_ADMIN_ID && m_Players[client].GetAdminId() != id)
   662 		{
   663 			if (!CheckSetAdminName(client, &m_Players[client], id))
   664 			{
   665 				m_Players[client].Kick("Your name is reserved by SourceMod; set your password to use it.");
   666 				RETURN_META(MRES_IGNORED);
   667 			}
   668 		} else if ((id = g_Admins.FindAdminByIdentity("name", old_name)) != INVALID_ADMIN_ID) {
   669 			if (id == m_Players[client].GetAdminId())
   670 			{
   671 				/* This player is changing their name; force them to drop admin privileges! */
   672 				m_Players[client].SetAdminId(INVALID_ADMIN_ID, false);
   673 			}
   674 		}
   675 		m_Players[client].SetName(new_name);
   676 	}
   677 	
   678 	if (m_PassInfoVar.size() > 0)
   679 	{
   680 		/* Try for a password change */
   681 		const char *old_pass = m_Players[client].m_LastPassword.c_str();
   682 		const char *new_pass = engine->GetClientConVarValue(client, m_PassInfoVar.c_str());
   683 		if (strcmp(old_pass, new_pass) != 0)
   684 		{
   685 			m_Players[client].m_LastPassword.assign(new_pass);
   686 			if (m_Players[client].IsInGame() && m_Players[client].IsAuthorized())
   687 			{
   688 				/* If there is already an admin id assigned, this will just bail out. */
   689 				m_Players[client].DoBasicAdminChecks();
   690 			}
   691 		}
   692 	}
   693 }
   694 
   695 int PlayerManager::GetMaxClients()
   696 {
   697 	return m_maxClients;
   698 }
   699 
   700 CPlayer *PlayerManager::GetPlayerByIndex(int client) const
   701 {
   702 	if (client > m_maxClients || client < 1)
   703 	{
   704 		return NULL;
   705 	}
   706 	return &m_Players[client];
   707 }
   708 
   709 int PlayerManager::GetNumPlayers()
   710 {
   711 	return m_PlayerCount;
   712 }
   713 
   714 int PlayerManager::GetClientOfUserId(int userid)
   715 {
   716 	if (userid < 0 || userid > USHRT_MAX)
   717 	{
   718 		return 0;
   719 	}
   720 	
   721 	int client = m_UserIdLookUp[userid];
   722 
   723 	/* Verify the userid.  The cache can get messed up with older
   724 	 * Valve engines.  :TODO: If this gets fixed, do an old engine 
   725 	 * check before invoking this backwards compat code.
   726 	 */
   727 	if (client)
   728 	{
   729 		CPlayer *player = GetPlayerByIndex(client);
   730 		if (player && player->IsConnected())
   731 		{
   732 			int realUserId = engine->GetPlayerUserId(player->GetEdict());
   733 			if (realUserId == userid)
   734 			{
   735 				return client;
   736 			}
   737 		}
   738 	}
   739 
   740 	/* If we can't verify the userid, we have to do a manual loop */
   741 	CPlayer *player;
   742 	for (int i = 1; i <= m_maxClients; i++)
   743 	{
   744 		player = GetPlayerByIndex(i);
   745 		if (!player || !player->IsConnected())
   746 		{
   747 			continue;
   748 		}
   749 		if (engine->GetPlayerUserId(player->GetEdict()) == userid)
   750 		{
   751 			m_UserIdLookUp[userid] = i;
   752 			return i;
   753 		}
   754 	}
   755 
   756 	return 0;
   757 }
   758 
   759 void PlayerManager::AddClientListener(IClientListener *listener)
   760 {
   761 	m_hooks.push_back(listener);
   762 }
   763 
   764 void PlayerManager::RemoveClientListener(IClientListener *listener)
   765 {
   766 	m_hooks.remove(listener);
   767 }
   768 
   769 IGamePlayer *PlayerManager::GetGamePlayer(edict_t *pEdict)
   770 {
   771 	int index = engine->IndexOfEdict(pEdict);
   772 	return GetGamePlayer(index);
   773 }
   774 
   775 IGamePlayer *PlayerManager::GetGamePlayer(int client)
   776 {
   777 	return GetPlayerByIndex(client);
   778 }
   779 
   780 void PlayerManager::ClearAdminId(AdminId id)
   781 {
   782 	for (int i=1; i<=m_maxClients; i++)
   783 	{
   784 		if (m_Players[i].m_Admin == id)
   785 		{
   786 			m_Players[i].DumpAdmin(true);
   787 		}
   788 	}
   789 }
   790 
   791 void PlayerManager::ClearAllAdmins()
   792 {
   793 	for (int i=1; i<=m_maxClients; i++)
   794 	{
   795 		m_Players[i].DumpAdmin(true);
   796 	}
   797 }
   798 
   799 const char *PlayerManager::GetPassInfoVar()
   800 {
   801 	return m_PassInfoVar.c_str();
   802 }
   803 
   804 void PlayerManager::RecheckAnyAdmins()
   805 {
   806 	for (int i=1; i<=m_maxClients; i++)
   807 	{
   808 		if (m_Players[i].IsInGame() && m_Players[i].IsAuthorized())
   809 		{
   810 			m_Players[i].DoBasicAdminChecks();
   811 		}
   812 	}
   813 }
   814 
   815 unsigned int PlayerManager::GetReplyTo()
   816 {
   817 	return g_ChatTriggers.GetReplyTo();
   818 }
   819 
   820 unsigned int PlayerManager::SetReplyTo(unsigned int reply)
   821 {
   822 	return g_ChatTriggers.SetReplyTo(reply);
   823 }
   824 
   825 int PlayerManager::FilterCommandTarget(IGamePlayer *pAdmin, IGamePlayer *pTarget, int flags)
   826 {
   827 	return InternalFilterCommandTarget((CPlayer *)pAdmin, (CPlayer *)pTarget, flags);
   828 }
   829 
   830 void PlayerManager::RegisterCommandTargetProcessor(ICommandTargetProcessor *pHandler)
   831 {
   832 	target_processors.push_back(pHandler);
   833 }
   834 
   835 void PlayerManager::UnregisterCommandTargetProcessor(ICommandTargetProcessor *pHandler)
   836 {
   837 	target_processors.remove(pHandler);
   838 }
   839 
   840 int PlayerManager::InternalFilterCommandTarget(CPlayer *pAdmin, CPlayer *pTarget, int flags)
   841 {
   842 	if ((flags & COMMAND_FILTER_CONNECTED) == COMMAND_FILTER_CONNECTED
   843 		&& !pTarget->IsConnected())
   844 	{
   845 		return COMMAND_TARGET_NONE;
   846 	}
   847 	else if ((flags & COMMAND_FILTER_CONNECTED) != COMMAND_FILTER_CONNECTED && 
   848 		!pTarget->IsInGame())
   849 	{
   850 		return COMMAND_TARGET_NOT_IN_GAME;
   851 	}
   852 
   853 	if ((flags & COMMAND_FILTER_NO_BOTS) == COMMAND_FILTER_NO_BOTS
   854 		&& pTarget->IsFakeClient())
   855 	{
   856 		return COMMAND_TARGET_NOT_HUMAN;
   857 	}
   858 
   859 	if (pAdmin != NULL)
   860 	{
   861 		if ((flags & COMMAND_FILTER_NO_IMMUNITY) != COMMAND_FILTER_NO_IMMUNITY
   862 			&& !g_Admins.CanAdminTarget(pAdmin->GetAdminId(), pTarget->GetAdminId()))
   863 		{
   864 			return COMMAND_TARGET_IMMUNE;
   865 		}
   866 	}
   867 
   868 	if ((flags & COMMAND_FILTER_ALIVE) == COMMAND_FILTER_ALIVE 
   869 		&& pTarget->GetLifeState() != PLAYER_LIFE_ALIVE)
   870 	{
   871 		return COMMAND_TARGET_NOT_ALIVE;
   872 	}
   873 
   874 	if ((flags & COMMAND_FILTER_DEAD) == COMMAND_FILTER_DEAD
   875 		&& pTarget->GetLifeState() != PLAYER_LIFE_DEAD)
   876 	{
   877 		return COMMAND_TARGET_NOT_DEAD;
   878 	}
   879 
   880 	return COMMAND_TARGET_VALID;
   881 }
   882 
   883 void PlayerManager::ProcessCommandTarget(cmd_target_info_t *info)
   884 {
   885 	CPlayer *pTarget, *pAdmin;
   886 	int max_clients, total = 0;
   887 
   888 	max_clients = GetMaxClients();
   889 
   890 	if (info->max_targets < 1)
   891 	{
   892 		info->reason = COMMAND_TARGET_NONE;
   893 		info->num_targets = 0;
   894 	}
   895 
   896 	if (info->admin == 0)
   897 	{
   898 		pAdmin = NULL;
   899 	}
   900 	else
   901 	{
   902 		pAdmin = GetPlayerByIndex(info->admin);
   903 	}
   904 
   905 	if (info->pattern[0] == '#')
   906 	{
   907 		int userid = atoi(&info->pattern[1]);
   908 		int client = GetClientOfUserId(userid);
   909 
   910 		/* See if a valid userid matched */
   911 		if (client > 0)
   912 		{
   913 			IGamePlayer *pTarget = GetPlayerByIndex(client);
   914 			if (pTarget != NULL)
   915 			{
   916 				if ((info->reason = FilterCommandTarget(pAdmin, pTarget, info->flags)) == COMMAND_TARGET_VALID)
   917 				{
   918 					info->targets[0] = client;
   919 					info->num_targets = 1;
   920 					strncopy(info->target_name, pTarget->GetName(), info->target_name_maxlength);
   921 					info->target_name_style = COMMAND_TARGETNAME_RAW;
   922 				}
   923 				else
   924 				{
   925 					info->num_targets = 0;
   926 				}
   927 				return;
   928 			}
   929 		}
   930 
   931 		/* See if an exact name matches */
   932 		for (int i = 1; i <= max_clients; i++)
   933 		{
   934 			if ((pTarget = GetPlayerByIndex(i)) == NULL)
   935 			{
   936 				continue;
   937 			}
   938 			if  (!pTarget->IsConnected())
   939 			{
   940 				continue;
   941 			}
   942 			if (strcmp(pTarget->GetName(), &info->pattern[1]) == 0)
   943 			{
   944 				if ((info->reason = FilterCommandTarget(pAdmin, pTarget, info->flags))
   945 					== COMMAND_TARGET_VALID)
   946 				{
   947 					info->targets[0] = i;
   948 					info->num_targets = 1;
   949 					strncopy(info->target_name, pTarget->GetName(), info->target_name_maxlength);
   950 					info->target_name_style = COMMAND_TARGETNAME_RAW;
   951 				}
   952 				else
   953 				{
   954 					info->num_targets = 0;
   955 				}
   956 				return;
   957 			}
   958 		}
   959 	}
   960 
   961 	if ((info->flags & COMMAND_FILTER_NO_MULTI) != COMMAND_FILTER_NO_MULTI)
   962 	{
   963 		bool is_multi = false;
   964 		bool bots_only = false;
   965 
   966 		if (strcmp(info->pattern, "@all") == 0)
   967 		{
   968 			is_multi = true;
   969 			strncopy(info->target_name, "all players", info->target_name_maxlength);
   970 			info->target_name_style = COMMAND_TARGETNAME_ML;
   971 		}
   972 		else if (strcmp(info->pattern, "@dead") == 0)
   973 		{
   974 			is_multi = true;
   975 			if ((info->flags & COMMAND_FILTER_ALIVE) == COMMAND_FILTER_ALIVE)
   976 			{
   977 				info->num_targets = 0;
   978 				info->reason = COMMAND_TARGET_NOT_ALIVE;
   979 				return;
   980 			}
   981 			info->flags |= COMMAND_FILTER_DEAD;
   982 			strncopy(info->target_name, "all dead players", info->target_name_maxlength);
   983 			info->target_name_style = COMMAND_TARGETNAME_ML;
   984 		}
   985 		else if (strcmp(info->pattern, "@alive") == 0)
   986 		{
   987 			is_multi = true;
   988 			if ((info->flags & COMMAND_FILTER_DEAD) == COMMAND_FILTER_DEAD)
   989 			{
   990 				info->num_targets = 0;
   991 				info->reason = COMMAND_TARGET_NOT_DEAD;
   992 				return;
   993 			}
   994 			strncopy(info->target_name, "all alive players", info->target_name_maxlength);
   995 			info->target_name_style = COMMAND_TARGETNAME_ML;
   996 			info->flags |= COMMAND_FILTER_ALIVE;
   997 		}
   998 		else if (strcmp(info->pattern, "@bots") == 0)
   999 		{
  1000 			is_multi = true;
  1001 			if ((info->flags & COMMAND_FILTER_NO_BOTS) == COMMAND_FILTER_NO_BOTS)
  1002 			{
  1003 				info->num_targets = 0;
  1004 				info->reason = COMMAND_FILTER_NO_BOTS;
  1005 				return;
  1006 			}
  1007 			strncopy(info->target_name, "all bots", info->target_name_maxlength);
  1008 			info->target_name_style = COMMAND_TARGETNAME_ML;
  1009 			bots_only = true;
  1010 		}
  1011 		else if (strcmp(info->pattern, "@humans") == 0)
  1012 		{
  1013 			is_multi = true;
  1014 			strncopy(info->target_name, "all humans", info->target_name_maxlength);
  1015 			info->target_name_style = COMMAND_TARGETNAME_ML;
  1016 			info->flags |= COMMAND_FILTER_NO_BOTS;
  1017 		}
  1018 
  1019 		if (is_multi)
  1020 		{
  1021 			for (int i = 1; i <= max_clients && total < info->max_targets; i++)
  1022 			{
  1023 				if ((pTarget = GetPlayerByIndex(i)) == NULL)
  1024 				{
  1025 					continue;
  1026 				}
  1027 				if (FilterCommandTarget(pAdmin, pTarget, info->flags) > 0)
  1028 				{
  1029 					if (!bots_only || pTarget->IsFakeClient())
  1030 					{
  1031 						info->targets[total++] = i;
  1032 					}
  1033 				}
  1034 			}
  1035 
  1036 			info->num_targets = total;
  1037 			info->reason = (info->num_targets) ? COMMAND_TARGET_VALID : COMMAND_TARGET_EMPTY_FILTER;
  1038 			return;
  1039 		}
  1040 	}
  1041 
  1042 	List<ICommandTargetProcessor *>::iterator iter;
  1043 	for (iter = target_processors.begin(); iter != target_processors.end(); iter++)
  1044 	{
  1045 		ICommandTargetProcessor *pProcessor = (*iter);
  1046 		if (pProcessor->ProcessCommandTarget(info))
  1047 		{
  1048 			return;
  1049 		}
  1050 	}
  1051 
  1052 	/* Check partial names */
  1053 	int found_client = 0;
  1054 	CPlayer *pFoundClient = NULL;
  1055 	for (int i = 1; i <= max_clients; i++)
  1056 	{
  1057 		if ((pTarget = GetPlayerByIndex(i)) == NULL)
  1058 		{
  1059 			continue;
  1060 		}
  1061 
  1062 		if (stristr(pTarget->GetName(), info->pattern) != NULL)
  1063 		{
  1064 			if (found_client)
  1065 			{
  1066 				info->num_targets = 0;
  1067 				info->reason = COMMAND_TARGET_AMBIGUOUS;
  1068 				return;
  1069 			}
  1070 			else
  1071 			{
  1072 				found_client = i;
  1073 				pFoundClient = pTarget;
  1074 			}
  1075 		}
  1076 	}
  1077 
  1078 	if (found_client)
  1079 	{
  1080 		if ((info->reason = FilterCommandTarget(pAdmin, pFoundClient, info->flags)) 
  1081 			== COMMAND_TARGET_VALID)
  1082 		{
  1083 			info->targets[0] = found_client;
  1084 			info->num_targets = 1;
  1085 			strncopy(info->target_name, pFoundClient->GetName(), info->target_name_maxlength);
  1086 			info->target_name_style = COMMAND_TARGETNAME_RAW;
  1087 		}
  1088 		else
  1089 		{
  1090 			info->num_targets = 0;
  1091 		}
  1092 	}
  1093 	else
  1094 	{
  1095 		info->num_targets = 0;
  1096 		info->reason = COMMAND_TARGET_NONE;
  1097 	}
  1098 }
  1099 
  1100 /*******************
  1101  *** PLAYER CODE ***
  1102  *******************/
  1103 
  1104 CPlayer::CPlayer()
  1105 {
  1106 	m_IsConnected = false;
  1107 	m_IsInGame = false;
  1108 	m_IsAuthorized = false;
  1109 	m_pEdict = NULL;
  1110 	m_Admin = INVALID_ADMIN_ID;
  1111 	m_TempAdmin = false;
  1112 	m_Info = NULL;
  1113 	m_bAdminCheckSignalled = false;
  1114 	m_bIsInKickQueue = false;
  1115 	m_LastPassword.clear();
  1116 	m_LangId = LANGUAGE_ENGLISH;
  1117 }
  1118 
  1119 void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity)
  1120 {
  1121 	m_IsConnected = true;
  1122 	m_Name.assign(name);
  1123 	m_Ip.assign(ip);
  1124 	m_pEdict = pEntity;
  1125 	m_iIndex = engine->IndexOfEdict(pEntity);
  1126 	m_LangId = g_Translator.GetServerLanguage();
  1127 
  1128 	char ip2[24], *ptr;
  1129 	strncopy(ip2, ip, sizeof(ip2));
  1130 	if ((ptr = strchr(ip2, ':')) != NULL)
  1131 	{
  1132 		*ptr = '\0';
  1133 	}
  1134 	m_IpNoPort.assign(ip2);
  1135 }
  1136 
  1137 void CPlayer::Connect()
  1138 {
  1139 	if (m_IsInGame)
  1140 	{
  1141 		return;
  1142 	}
  1143 
  1144 	m_IsInGame = true;
  1145 
  1146 	const char *var = g_Players.GetPassInfoVar();
  1147 	int client = engine->IndexOfEdict(m_pEdict);
  1148 	if (var[0] != '\0')
  1149 	{
  1150 		const char *pass = engine->GetClientConVarValue(client, var);
  1151 		m_LastPassword.assign(pass ? pass : "");
  1152 	}
  1153 	else
  1154 	{
  1155 		m_LastPassword.assign("");
  1156 	}
  1157 }
  1158 
  1159 void CPlayer::Authorize(const char *steamid)
  1160 {
  1161 	if (m_IsAuthorized)
  1162 	{
  1163 		return;
  1164 	}
  1165 
  1166 	m_IsAuthorized = true;
  1167 	m_AuthID.assign(steamid);
  1168 }
  1169 
  1170 void CPlayer::Disconnect()
  1171 {
  1172 	DumpAdmin(false);
  1173 	m_IsConnected = false;
  1174 	m_IsInGame = false;
  1175 	m_IsAuthorized = false;
  1176 	m_Name.clear();
  1177 	m_Ip.clear();
  1178 	m_AuthID.clear();
  1179 	m_pEdict = NULL;
  1180 	m_Info = NULL;
  1181 	m_bAdminCheckSignalled = false;
  1182 	m_UserId = -1;
  1183 	m_bIsInKickQueue = false;
  1184 }
  1185 
  1186 void CPlayer::SetName(const char *name)
  1187 {
  1188 	m_Name.assign(name);
  1189 }
  1190 
  1191 const char *CPlayer::GetName()
  1192 {
  1193 	if (m_Info && m_pEdict->GetUnknown())
  1194 	{
  1195 		return m_Info->GetName();
  1196 	}
  1197 	
  1198 	return m_Name.c_str();
  1199 }
  1200 
  1201 const char *CPlayer::GetIPAddress()
  1202 {
  1203 	return m_Ip.c_str();
  1204 }
  1205 
  1206 const char *CPlayer::GetAuthString()
  1207 {
  1208 	return m_AuthID.c_str();
  1209 }
  1210 
  1211 edict_t *CPlayer::GetEdict()
  1212 {
  1213 	return m_pEdict;
  1214 }
  1215 
  1216 bool CPlayer::IsInGame()
  1217 {
  1218 	return m_IsInGame && (m_pEdict->GetUnknown() != NULL);
  1219 }
  1220 
  1221 bool CPlayer::WasCountedAsInGame()
  1222 {
  1223 	return m_IsInGame;
  1224 }
  1225 
  1226 bool CPlayer::IsConnected()
  1227 {
  1228 	return m_IsConnected;
  1229 }
  1230 
  1231 bool CPlayer::IsAuthorized()
  1232 {
  1233 	return m_IsAuthorized;
  1234 }
  1235 
  1236 IPlayerInfo *CPlayer::GetPlayerInfo()
  1237 {
  1238 	if (m_pEdict->GetUnknown())
  1239 	{
  1240 		return m_Info;
  1241 	}
  1242 
  1243 	return NULL;
  1244 }
  1245 
  1246 bool CPlayer::IsFakeClient()
  1247 {
  1248 	return (strcmp(m_AuthID.c_str(), "BOT") == 0);
  1249 }
  1250 
  1251 void CPlayer::SetAdminId(AdminId id, bool temporary)
  1252 {
  1253 	if (!m_IsConnected)
  1254 	{
  1255 		return;
  1256 	}
  1257 
  1258 	DumpAdmin(false);
  1259 
  1260 	m_Admin = id;
  1261 	m_TempAdmin = temporary;
  1262 }
  1263 
  1264 AdminId CPlayer::GetAdminId()
  1265 {
  1266 	return m_Admin;
  1267 }
  1268 
  1269 void CPlayer::DumpAdmin(bool deleting)
  1270 {
  1271 	if (m_Admin != INVALID_ADMIN_ID)
  1272 	{
  1273 		if (m_TempAdmin && !deleting)
  1274 		{
  1275 			g_Admins.InvalidateAdmin(m_Admin);
  1276 		}
  1277 		m_Admin = INVALID_ADMIN_ID;
  1278 		m_TempAdmin = false;
  1279 	}
  1280 }
  1281 
  1282 void CPlayer::Kick(const char *str)
  1283 {
  1284 	MarkAsBeingKicked();
  1285 	INetChannel *pNetChan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(m_iIndex));
  1286 	IClient *pClient = static_cast<IClient *>(pNetChan->GetMsgHandler());
  1287 	pClient->Disconnect("%s", str);
  1288 }
  1289 
  1290 void CPlayer::Authorize_Post()
  1291 {
  1292 	if (m_IsInGame)
  1293 	{
  1294 		DoPostConnectAuthorization();
  1295 	}
  1296 }
  1297 
  1298 void CPlayer::DoPostConnectAuthorization()
  1299 {
  1300 	bool delay = false;
  1301 
  1302 	List<IClientListener *>::iterator iter;
  1303 	for (iter = g_Players.m_hooks.begin();
  1304 		 iter != g_Players.m_hooks.end();
  1305 		 iter++)
  1306 	{
  1307 		IClientListener *pListener = (*iter);
  1308 #if defined MIN_API_FOR_ADMINCALLS
  1309 		if (pListener->GetClientListenerVersion() < MIN_API_FOR_ADMINCALLS)
  1310 		{
  1311 			continue;
  1312 		}
  1313 #endif
  1314 		if (!pListener->OnClientPreAdminCheck(m_iIndex))
  1315 		{
  1316 			delay = true;
  1317 		}
  1318 	}
  1319 	
  1320 	cell_t result = 0;
  1321 	PreAdminCheck->PushCell(m_iIndex);
  1322 	PreAdminCheck->Execute(&result);
  1323 
  1324 	/* Defer, for better or worse */
  1325 	if (delay || (ResultType)result >= Pl_Handled)
  1326 	{
  1327 		return;
  1328 	}
  1329 
  1330 	/* Sanity check */
  1331 	if (!IsConnected())
  1332 	{
  1333 		return;
  1334 	}
  1335 
  1336 	/* Otherwise, go ahead and do admin checks */
  1337 	DoBasicAdminChecks();
  1338 
  1339 	/* Send the notification out */
  1340 	NotifyPostAdminChecks();
  1341 }
  1342 
  1343 bool CPlayer::RunAdminCacheChecks()
  1344 {
  1345 	AdminId old_id = GetAdminId();
  1346 
  1347 	DoBasicAdminChecks();
  1348 
  1349 	return (GetAdminId() != old_id);
  1350 }
  1351 
  1352 void CPlayer::NotifyPostAdminChecks()
  1353 {
  1354 	if (m_bAdminCheckSignalled)
  1355 	{
  1356 		return;
  1357 	}
  1358 
  1359 	/* Block beforehand so they can't double-call */
  1360 	m_bAdminCheckSignalled = true;
  1361 
  1362 	List<IClientListener *>::iterator iter;
  1363 	for (iter = g_Players.m_hooks.begin();
  1364 		iter != g_Players.m_hooks.end();
  1365 		iter++)
  1366 	{
  1367 		IClientListener *pListener = (*iter);
  1368 #if defined MIN_API_FOR_ADMINCALLS
  1369 		if (pListener->GetClientListenerVersion() < MIN_API_FOR_ADMINCALLS)
  1370 		{
  1371 			continue;
  1372 		}
  1373 #endif
  1374 		pListener->OnClientPostAdminCheck(m_iIndex);
  1375 	}
  1376 
  1377 	PostAdminCheck->PushCell(m_iIndex);
  1378 	PostAdminCheck->Execute(NULL);
  1379 }
  1380 
  1381 void CPlayer::DoBasicAdminChecks()
  1382 {
  1383 	if (GetAdminId() != INVALID_ADMIN_ID)
  1384 	{
  1385 		return;
  1386 	}
  1387 
  1388 	/* First check the name */
  1389 	AdminId id;
  1390 	int client = engine->IndexOfEdict(m_pEdict);
  1391 
  1392 	if ((id = g_Admins.FindAdminByIdentity("name", GetName())) != INVALID_ADMIN_ID)
  1393 	{
  1394 		if (!g_Players.CheckSetAdminName(client, this, id))
  1395 		{
  1396 			int userid = engine->GetPlayerUserId(m_pEdict);
  1397 			g_Timers.CreateTimer(&s_KickPlayerTimer, 0.1f, (void *)userid, 0);
  1398 		}
  1399 		return;
  1400 	}
  1401 	
  1402 	/* Check IP */
  1403 	if ((id = g_Admins.FindAdminByIdentity("ip", m_IpNoPort.c_str())) != INVALID_ADMIN_ID)
  1404 	{
  1405 		if (g_Players.CheckSetAdmin(client, this, id))
  1406 		{
  1407 			return;
  1408 		}
  1409 	}
  1410 
  1411 	/* Check IP address */
  1412 	if ((id = g_Admins.FindAdminByIdentity("steam", m_AuthID.c_str())) != INVALID_ADMIN_ID)
  1413 	{
  1414 		if (g_Players.CheckSetAdmin(client, this, id))
  1415 		{
  1416 			return;
  1417 		}
  1418 	}
  1419 }
  1420 
  1421 unsigned int CPlayer::GetLanguageId()
  1422 {
  1423 	return m_LangId;
  1424 }
  1425 
  1426 int CPlayer::GetUserId()
  1427 {
  1428 	if (m_UserId == -1)
  1429 	{
  1430 		m_UserId = engine->GetPlayerUserId(GetEdict());
  1431 	}
  1432 
  1433 	return m_UserId;
  1434 }
  1435 
  1436 bool CPlayer::IsInKickQueue()
  1437 {
  1438 	return m_bIsInKickQueue;
  1439 }
  1440 
  1441 void CPlayer::MarkAsBeingKicked()
  1442 {
  1443 	m_bIsInKickQueue = true;
  1444 }
  1445 
  1446 int CPlayer::GetLifeState()
  1447 {
  1448 	if (lifestate_offset == -1)
  1449 	{
  1450 		if (!g_pGameConf->GetOffset("m_lifeState", &lifestate_offset))
  1451 		{
  1452 			lifestate_offset = -2;
  1453 		}
  1454 	}
  1455 
  1456 	if (lifestate_offset < 0)
  1457 	{
  1458 		IPlayerInfo *info = GetPlayerInfo();
  1459 		if (info == NULL)
  1460 		{
  1461 			return PLAYER_LIFE_UNKNOWN;
  1462 		}
  1463 		return info->IsDead() ? PLAYER_LIFE_DEAD : PLAYER_LIFE_ALIVE;
  1464 	}
  1465 
  1466 	if (m_pEdict == NULL)
  1467 	{
  1468 		return PLAYER_LIFE_UNKNOWN;
  1469 	}
  1470 
  1471 	CBaseEntity *pEntity;
  1472 	IServerUnknown *pUnknown = m_pEdict->GetUnknown();
  1473 	if (pUnknown == NULL || (pEntity = pUnknown->GetBaseEntity()) == NULL)
  1474 	{
  1475 		return PLAYER_LIFE_UNKNOWN;
  1476 	}
  1477 
  1478 	if (*((uint8_t *)pEntity + lifestate_offset) == LIFE_ALIVE)
  1479 	{
  1480 		return PLAYER_LIFE_ALIVE;
  1481 	}
  1482 	else
  1483 	{
  1484 		return PLAYER_LIFE_DEAD;
  1485 	}
  1486 }