core/PlayerManager.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1288 ae5f77aef635
child 1335 2047dc42d800
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

(This day shall henceforth be known as the Eighty Column Massacre of '07)
     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 <inetchannel.h>
    42 #include <iclient.h>
    43 #include "TimerSys.h"
    44 
    45 PlayerManager g_Players;
    46 bool g_OnMapStarted = false;
    47 IForward *PreAdminCheck = NULL;
    48 IForward *PostAdminCheck = NULL;
    49 
    50 SH_DECL_HOOK5(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, edict_t *, const char *, const char *, char *, int);
    51 SH_DECL_HOOK2_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, edict_t *, const char *);
    52 SH_DECL_HOOK1_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, edict_t *);
    53 SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *);
    54 SH_DECL_HOOK1_void(IServerGameClients, ClientSettingsChanged, SH_NOATTRIB, 0, edict_t *);
    55 SH_DECL_HOOK3_void(IServerGameDLL, ServerActivate, SH_NOATTRIB, 0, edict_t *, int, int);
    56 
    57 class KickPlayerTimer : public ITimedEvent
    58 {
    59 public:
    60 	ResultType OnTimer(ITimer *pTimer, void *pData)
    61 	{
    62 		int userid = (int)pData;
    63 		int client = g_Players.GetClientOfUserId(userid);
    64 		if (client)
    65 		{
    66 			CPlayer *player = g_Players.GetPlayerByIndex(client);
    67 			player->Kick("Your name is reserved by SourceMod; set your password to use it.");
    68 		}
    69 		return Pl_Stop;
    70 	}
    71 	void OnTimerEnd(ITimer *pTimer, void *pData)
    72 	{
    73 	}
    74 } s_KickPlayerTimer;
    75 
    76 PlayerManager::PlayerManager()
    77 {
    78 	m_AuthQueue = NULL;
    79 	m_FirstPass = true;
    80 
    81 	m_UserIdLookUp = new int[USHRT_MAX];
    82 	memset(m_UserIdLookUp, 0, sizeof(int) * USHRT_MAX);
    83 }
    84 
    85 PlayerManager::~PlayerManager()
    86 {
    87 	delete [] m_AuthQueue;
    88 	delete [] m_UserIdLookUp;
    89 }
    90 
    91 void PlayerManager::OnSourceModAllInitialized()
    92 {
    93 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &PlayerManager::OnClientConnect, false);
    94 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &PlayerManager::OnClientConnect_Post, true);
    95 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, serverClients, this, &PlayerManager::OnClientPutInServer, true);
    96 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &PlayerManager::OnClientDisconnect, false);
    97 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &PlayerManager::OnClientDisconnect_Post, true);
    98 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientCommand, serverClients, this, &PlayerManager::OnClientCommand, false);
    99 	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, serverClients, this, &PlayerManager::OnClientSettingsChanged, true);
   100 	SH_ADD_HOOK_MEMFUNC(IServerGameDLL, ServerActivate, gamedll, this, &PlayerManager::OnServerActivate, true);
   101 
   102 	g_ShareSys.AddInterface(NULL, this);
   103 
   104 	ParamType p1[] = {Param_Cell, Param_String, Param_Cell};
   105 	ParamType p2[] = {Param_Cell};
   106 
   107 	m_clconnect = g_Forwards.CreateForward("OnClientConnect", ET_Event, 3, p1);
   108 	m_clputinserver = g_Forwards.CreateForward("OnClientPutInServer", ET_Ignore, 1, p2);
   109 	m_cldisconnect = g_Forwards.CreateForward("OnClientDisconnect", ET_Ignore, 1, p2);
   110 	m_cldisconnect_post = g_Forwards.CreateForward("OnClientDisconnect_Post", ET_Ignore, 1, p2);
   111 	m_clcommand = g_Forwards.CreateForward("OnClientCommand", ET_Hook, 2, NULL, Param_Cell, Param_Cell);
   112 	m_clinfochanged = g_Forwards.CreateForward("OnClientSettingsChanged", ET_Ignore, 1, p2);
   113 	m_clauth = g_Forwards.CreateForward("OnClientAuthorized", ET_Ignore, 2, NULL, Param_Cell, Param_String);
   114 	m_onActivate = g_Forwards.CreateForward("OnServerLoad", ET_Ignore, 0, NULL);
   115 	m_onActivate2 = g_Forwards.CreateForward("OnMapStart", ET_Ignore, 0, NULL);
   116 
   117 	PreAdminCheck = g_Forwards.CreateForward("OnClientPreAdminCheck", ET_Event, 1, p1);
   118 	PostAdminCheck = g_Forwards.CreateForward("OnClientPostAdminCheck", ET_Ignore, 1, p1);
   119 }
   120 
   121 void PlayerManager::OnSourceModShutdown()
   122 {
   123 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &PlayerManager::OnClientConnect, false);
   124 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, serverClients, this, &PlayerManager::OnClientPutInServer, true);
   125 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &PlayerManager::OnClientDisconnect, false);
   126 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &PlayerManager::OnClientDisconnect_Post, true);
   127 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientCommand, serverClients, this, &PlayerManager::OnClientCommand, false);
   128 	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, serverClients, this, &PlayerManager::OnClientSettingsChanged, true);
   129 	SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, ServerActivate, gamedll, this, &PlayerManager::OnServerActivate, true);
   130 
   131 	/* Release forwards */
   132 	g_Forwards.ReleaseForward(m_clconnect);
   133 	g_Forwards.ReleaseForward(m_clputinserver);
   134 	g_Forwards.ReleaseForward(m_cldisconnect);
   135 	g_Forwards.ReleaseForward(m_cldisconnect_post);
   136 	g_Forwards.ReleaseForward(m_clcommand);
   137 	g_Forwards.ReleaseForward(m_clinfochanged);
   138 	g_Forwards.ReleaseForward(m_clauth);
   139 	g_Forwards.ReleaseForward(m_onActivate);
   140 	g_Forwards.ReleaseForward(m_onActivate2);
   141 
   142 	g_Forwards.ReleaseForward(PreAdminCheck);
   143 	g_Forwards.ReleaseForward(PostAdminCheck);
   144 
   145 	delete [] m_Players;
   146 }
   147 
   148 ConfigResult PlayerManager::OnSourceModConfigChanged(const char *key, 
   149 												 const char *value, 
   150 												 ConfigSource source, 
   151 												 char *error, 
   152 												 size_t maxlength)
   153 {
   154 	if (strcmp(key, "PassInfoVar") == 0)
   155 	{
   156 		if (strcmp(value, "_password") != 0)
   157 		{
   158 			m_PassInfoVar.assign(value);
   159 		}
   160 		return ConfigResult_Accept;
   161 	}
   162 	return ConfigResult_Ignore;
   163 }
   164 
   165 void PlayerManager::OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
   166 {
   167 	if (m_FirstPass)
   168 	{
   169 		/* Initialize all players */
   170 		m_maxClients = clientMax;
   171 		m_PlayerCount = 0;
   172 		m_Players = new CPlayer[m_maxClients + 1];
   173 		m_AuthQueue = new unsigned int[m_maxClients + 1];
   174 		m_FirstPass = false;
   175 
   176 		memset(m_AuthQueue, 0, sizeof(unsigned int) * (m_maxClients + 1));
   177 	}
   178 	m_onActivate->Execute(NULL);
   179 	m_onActivate2->Execute(NULL);
   180 
   181 	g_OnMapStarted = true;
   182 
   183 	SM_ExecuteAllConfigs();
   184 }
   185 
   186 bool PlayerManager::CheckSetAdmin(int index, CPlayer *pPlayer, AdminId id)
   187 {
   188 	const char *password = g_Admins.GetAdminPassword(id);
   189 	if (password != NULL)
   190 	{
   191 		if (m_PassInfoVar.size() < 1)
   192 		{
   193 			return false;
   194 		}
   195 
   196 		/* Whoa... the user needs a password! */
   197 		const char *given = engine->GetClientConVarValue(index, m_PassInfoVar.c_str());
   198 		if (!given || strcmp(given, password) != 0)
   199 		{
   200 			return false;
   201 		}
   202 	}
   203 
   204 	pPlayer->SetAdminId(id, false);
   205 
   206 	return true;
   207 }
   208 
   209 bool PlayerManager::CheckSetAdminName(int index, CPlayer *pPlayer, AdminId id)
   210 {
   211 	const char *password = g_Admins.GetAdminPassword(id);
   212 	if (password == NULL)
   213 	{
   214 		return false;
   215 	}
   216 
   217 	if (m_PassInfoVar.size() < 1)
   218 	{
   219 		return false;
   220 	}
   221 
   222 	/* Whoa... the user needs a password! */
   223 	const char *given = engine->GetClientConVarValue(index, m_PassInfoVar.c_str());
   224 	if (!given || strcmp(given, password) != 0)
   225 	{
   226 		return false;
   227 	}
   228 
   229 	pPlayer->SetAdminId(id, false);
   230 
   231 	return true;
   232 }
   233 
   234 void PlayerManager::RunAuthChecks()
   235 {
   236 	CPlayer *pPlayer;
   237 	const char *authstr;
   238 	unsigned int removed = 0;
   239 	for (unsigned int i=1; i<=m_AuthQueue[0]; i++)
   240 	{
   241 		pPlayer = GetPlayerByIndex(m_AuthQueue[i]);
   242 		authstr = engine->GetPlayerNetworkIDString(pPlayer->m_pEdict);
   243 		if (authstr && authstr[0] != '\0'
   244 			&& (strcmp(authstr, "STEAM_ID_PENDING") != 0))
   245 		{
   246 			/* Set authorization */
   247 			pPlayer->Authorize(authstr);
   248 
   249 			/* Mark as removed from queue */
   250 			unsigned int client = m_AuthQueue[i];
   251 			m_AuthQueue[i] = 0;
   252 			removed++;
   253 
   254 			/* Send to extensions */
   255 			List<IClientListener *>::iterator iter;
   256 			IClientListener *pListener;
   257 			for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   258 			{
   259 				pListener = (*iter);
   260 				pListener->OnClientAuthorized(client, authstr);
   261 				if (!pPlayer->IsConnected())
   262 				{
   263 					break;
   264 				}
   265 			}
   266 
   267 			/* Send to plugins if player is still connected */
   268 			if (pPlayer->IsConnected() && m_clauth->GetFunctionCount())
   269 			{
   270 				/* :TODO: handle the case of a player disconnecting in the middle */
   271 				m_clauth->PushCell(client);
   272 				m_clauth->PushString(authstr);
   273 				m_clauth->Execute(NULL);
   274 			}
   275 
   276 			if (pPlayer->IsConnected())
   277 			{
   278 				pPlayer->Authorize_Post();
   279 			}
   280 		}
   281 	}
   282 
   283 	/* Clean up the queue */
   284 	if (removed)
   285 	{
   286 		/* We don't have to compact the list if it's empty */
   287 		if (removed != m_AuthQueue[0])
   288 		{
   289 			unsigned int diff = 0;
   290 			for (unsigned int i=1; i<=m_AuthQueue[0]; i++)
   291 			{
   292 				/* If this member is removed... */
   293 				if (m_AuthQueue[i] == 0)
   294 				{
   295 					/* Increase the differential */
   296 					diff++;
   297 				} else {
   298 					/* diff cannot increase faster than i+1 */
   299 					assert(i > diff);
   300 					assert(i - diff >= 1);
   301 					/* move this index down */
   302 					m_AuthQueue[i - diff] = m_AuthQueue[i];
   303 				}
   304 			}
   305 			m_AuthQueue[0] -= removed;
   306 		} else {
   307 			m_AuthQueue[0] = 0;
   308 			g_SourceMod.SetAuthChecking(false);
   309 		}
   310 	}
   311 }
   312 
   313 bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen)
   314 {
   315 	int client = engine->IndexOfEdict(pEntity);
   316 
   317 	List<IClientListener *>::iterator iter;
   318 	IClientListener *pListener = NULL;
   319 	for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   320 	{
   321 		pListener = (*iter);
   322 		if (!pListener->InterceptClientConnect(client, reject, maxrejectlen))
   323 		{
   324 			return false;
   325 		}
   326 	}
   327 
   328 	cell_t res = 1;
   329 
   330 	m_Players[client].Initialize(pszName, pszAddress, pEntity);
   331 	m_clconnect->PushCell(client);
   332 	m_clconnect->PushStringEx(reject, maxrejectlen, SM_PARAM_STRING_UTF8, SM_PARAM_COPYBACK);
   333 	m_clconnect->PushCell(maxrejectlen);
   334 	m_clconnect->Execute(&res, NULL);
   335 
   336 	if (res)
   337 	{
   338 		if (!m_Players[client].IsAuthorized())
   339 		{
   340 			m_AuthQueue[++m_AuthQueue[0]] = client;
   341 			g_SourceMod.SetAuthChecking(true);
   342 		}
   343 	} else {
   344 		RETURN_META_VALUE(MRES_SUPERCEDE, false);
   345 	}
   346 
   347 	m_UserIdLookUp[engine->GetPlayerUserId(pEntity)] = client;
   348 
   349 	return true;
   350 }
   351 
   352 bool PlayerManager::OnClientConnect_Post(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen)
   353 {
   354 	int client = engine->IndexOfEdict(pEntity);
   355 	bool orig_value = META_RESULT_ORIG_RET(bool);
   356 	CPlayer *pPlayer = GetPlayerByIndex(client);
   357 	if (orig_value)
   358 	{
   359 		List<IClientListener *>::iterator iter;
   360 		IClientListener *pListener = NULL;
   361 		for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   362 		{
   363 			pListener = (*iter);
   364 			pListener->OnClientConnected(client);
   365 			if (!pPlayer->IsConnected())
   366 			{
   367 				break;
   368 			}
   369 		}
   370 	}
   371 
   372 	return true;
   373 }
   374 
   375 void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername)
   376 {
   377 	cell_t res;
   378 	int client = engine->IndexOfEdict(pEntity);
   379 
   380 	CPlayer *pPlayer = GetPlayerByIndex(client);
   381 	/* If they're not connected, they're a bot */
   382 	if (!pPlayer->IsConnected())
   383 	{
   384 		/* Run manual connection routines */
   385 		char error[255];
   386 		const char *authid = engine->GetPlayerNetworkIDString(pEntity);
   387 		pPlayer->Authorize(authid);
   388 		if (!OnClientConnect(pEntity, playername, "127.0.0.1", error, sizeof(error)))
   389 		{
   390 			/* :TODO: kick the bot if it's rejected */
   391 			return;
   392 		}
   393 		List<IClientListener *>::iterator iter;
   394 		IClientListener *pListener = NULL;
   395 		for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   396 		{
   397 			pListener = (*iter);
   398 			pListener->OnClientConnected(client);
   399 			/* See if bot was kicked */
   400 			if (!pPlayer->IsConnected())
   401 			{
   402 				return;
   403 			}
   404 		}
   405 		/* Now do authorization */
   406 		for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   407 		{
   408 			pListener = (*iter);
   409 			pListener->OnClientAuthorized(client, authid);
   410 			/* See if bot was kicked */
   411 			if (!pPlayer->IsConnected())
   412 			{
   413 				return;
   414 			}
   415 		}
   416 		/* Finally, tell plugins */
   417 		if (m_clauth->GetFunctionCount())
   418 		{
   419 			m_clauth->PushCell(client);
   420 			m_clauth->PushString(authid);
   421 			m_clauth->Execute(NULL);
   422 			if (!pPlayer->IsConnected())
   423 			{
   424 				return;
   425 			}
   426 		}
   427 		pPlayer->Authorize_Post();
   428 		if (!pPlayer->IsConnected())
   429 		{
   430 			return;
   431 		}
   432 	}
   433 
   434 	if (playerinfo)
   435 	{
   436 		pPlayer->m_Info = playerinfo->GetPlayerInfo(pEntity);
   437 	}
   438 
   439 	List<IClientListener *>::iterator iter;
   440 	IClientListener *pListener = NULL;
   441 	for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   442 	{
   443 		pListener = (*iter);
   444 		pListener->OnClientPutInServer(client);
   445 		/* See if player was kicked */
   446 		if (!pPlayer->IsConnected())
   447 		{
   448 			return;
   449 		}
   450 	}
   451 
   452 	m_Players[client].Connect();
   453 	m_PlayerCount++;
   454 	m_clputinserver->PushCell(client);
   455 	m_clputinserver->Execute(&res, NULL);
   456 }
   457 
   458 void PlayerManager::OnSourceModLevelEnd()
   459 {
   460 	/* Disconnect all bots still in game */
   461 	for (int i=1; i<=m_maxClients; i++)
   462 	{
   463 		if (m_Players[i].IsConnected() && m_Players[i].IsFakeClient())
   464 		{
   465 			OnClientDisconnect(m_Players[i].GetEdict());
   466 		}
   467 	}
   468 }
   469 
   470 void PlayerManager::OnClientDisconnect(edict_t *pEntity)
   471 {
   472 	cell_t res;
   473 	int client = engine->IndexOfEdict(pEntity);
   474 
   475 	if (m_Players[client].IsConnected())
   476 	{
   477 		m_cldisconnect->PushCell(client);
   478 		m_cldisconnect->Execute(&res, NULL);
   479 	}
   480 
   481 	if (m_Players[client].IsInGame())
   482 	{
   483 		m_PlayerCount--;
   484 	}
   485 
   486 	List<IClientListener *>::iterator iter;
   487 	IClientListener *pListener = NULL;
   488 	for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   489 	{
   490 		pListener = (*iter);
   491 		pListener->OnClientDisconnecting(client);
   492 	}
   493 
   494 	/**
   495 	 * Remove client from auth queue if necessary
   496 	 */
   497 	if (!m_Players[client].IsAuthorized())
   498 	{
   499 		for (unsigned int i=1; i<=m_AuthQueue[0]; i++)
   500 		{
   501 			if (m_AuthQueue[i] == (unsigned)client)
   502 			{
   503 				/* Move everything ahead of us back by one */
   504 				for (unsigned int j=i+1; j<=m_AuthQueue[0]; j++)
   505 				{
   506 					m_AuthQueue[j-1] = m_AuthQueue[j];
   507 				}
   508 				/* Remove us and break */
   509 				m_AuthQueue[0]--;
   510 				break;
   511 			}
   512 		}
   513 	}
   514 
   515 	m_Players[client].Disconnect();
   516 	m_UserIdLookUp[engine->GetPlayerUserId(pEntity)] = 0;
   517 }
   518 
   519 void PlayerManager::OnClientDisconnect_Post(edict_t *pEntity)
   520 {
   521 	cell_t res;
   522 	int client = engine->IndexOfEdict(pEntity);
   523 
   524 	m_cldisconnect_post->PushCell(client);
   525 	m_cldisconnect_post->Execute(&res, NULL);
   526 
   527 	List<IClientListener *>::iterator iter;
   528 	IClientListener *pListener = NULL;
   529 	for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
   530 	{
   531 		pListener = (*iter);
   532 		pListener->OnClientDisconnected(client);
   533 	}
   534 }
   535 
   536 void PlayerManager::OnClientCommand(edict_t *pEntity)
   537 {
   538 	int client = engine->IndexOfEdict(pEntity);
   539 	cell_t res = Pl_Continue;
   540 
   541 	bool result = g_ValveMenuStyle.OnClientCommand(client);
   542 	if (result)
   543 	{
   544 		res = Pl_Handled;
   545 	} else {
   546 		result = g_RadioMenuStyle.OnClientCommand(client);
   547 		if (result)
   548 		{
   549 			res = Pl_Handled;
   550 		}
   551 	}
   552 
   553 	int args = engine->Cmd_Argc() - 1;
   554 
   555 	cell_t res2 = Pl_Continue;
   556 	m_clcommand->PushCell(client);
   557 	m_clcommand->PushCell(args);
   558 	m_clcommand->Execute(&res2, NULL);
   559 
   560 	if (res2 > res)
   561 	{
   562 		res = res2;
   563 	}
   564 
   565 	if (res >= Pl_Stop)
   566 	{
   567 		RETURN_META(MRES_SUPERCEDE);
   568 	}
   569 
   570 	res = g_ConCmds.DispatchClientCommand(client, (ResultType)res);
   571 
   572 	if (res >= Pl_Handled)
   573 	{
   574 		RETURN_META(MRES_SUPERCEDE);
   575 	}
   576 }
   577 
   578 void PlayerManager::OnClientSettingsChanged(edict_t *pEntity)
   579 {
   580 	cell_t res;
   581 	int client = engine->IndexOfEdict(pEntity);
   582 
   583 	m_clinfochanged->PushCell(engine->IndexOfEdict(pEntity));
   584 	m_clinfochanged->Execute(&res, NULL);
   585 
   586 	IPlayerInfo *info = m_Players[client].GetPlayerInfo();
   587 	const char *new_name = info ? info->GetName() : engine->GetClientConVarValue(client, "name");
   588 	const char *old_name = m_Players[client].m_Name.c_str();
   589 
   590 	if (strcmp(old_name, new_name) != 0)
   591 	{
   592 		AdminId id = g_Admins.FindAdminByIdentity("name", new_name);
   593 		if (id != INVALID_ADMIN_ID && m_Players[client].GetAdminId() != id)
   594 		{
   595 			if (!CheckSetAdminName(client, &m_Players[client], id))
   596 			{
   597 				m_Players[client].Kick("Your name is reserved by SourceMod; set your password to use it.");
   598 				RETURN_META(MRES_IGNORED);
   599 			}
   600 		} else if ((id = g_Admins.FindAdminByIdentity("name", old_name)) != INVALID_ADMIN_ID) {
   601 			if (id == m_Players[client].GetAdminId())
   602 			{
   603 				/* This player is changing their name; force them to drop admin privileges! */
   604 				m_Players[client].SetAdminId(INVALID_ADMIN_ID, false);
   605 			}
   606 		}
   607 		m_Players[client].SetName(new_name);
   608 	}
   609 	
   610 	if (m_PassInfoVar.size() > 0)
   611 	{
   612 		/* Try for a password change */
   613 		const char *old_pass = m_Players[client].m_LastPassword.c_str();
   614 		const char *new_pass = engine->GetClientConVarValue(client, m_PassInfoVar.c_str());
   615 		if (strcmp(old_pass, new_pass) != 0)
   616 		{
   617 			m_Players[client].m_LastPassword.assign(new_pass);
   618 			if (m_Players[client].IsInGame() && m_Players[client].IsAuthorized())
   619 			{
   620 				/* If there is already an admin id assigned, this will just bail out. */
   621 				m_Players[client].DoBasicAdminChecks();
   622 			}
   623 		}
   624 	}
   625 }
   626 
   627 int PlayerManager::GetMaxClients()
   628 {
   629 	return m_maxClients;
   630 }
   631 
   632 CPlayer *PlayerManager::GetPlayerByIndex(int client) const
   633 {
   634 	if (client > m_maxClients || client < 1)
   635 	{
   636 		return NULL;
   637 	}
   638 	return &m_Players[client];
   639 }
   640 
   641 int PlayerManager::GetNumPlayers()
   642 {
   643 	return m_PlayerCount;
   644 }
   645 
   646 int PlayerManager::GetClientOfUserId(int userid)
   647 {
   648 	if (userid < 0 || userid > USHRT_MAX)
   649 	{
   650 		return 0;
   651 	}
   652 	
   653 	int client = m_UserIdLookUp[userid];
   654 
   655 	/* Verify the userid.  The cache can get messed up with older
   656 	 * Valve engines.  :TODO: If this gets fixed, do an old engine 
   657 	 * check before invoking this backwards compat code.
   658 	 */
   659 	if (client)
   660 	{
   661 		CPlayer *player = GetPlayerByIndex(client);
   662 		if (player && player->IsConnected())
   663 		{
   664 			int realUserId = engine->GetPlayerUserId(player->GetEdict());
   665 			if (realUserId == userid)
   666 			{
   667 				return client;
   668 			}
   669 		}
   670 	}
   671 
   672 	/* If we can't verify the userid, we have to do a manual loop */
   673 	CPlayer *player;
   674 	for (int i = 1; i <= m_maxClients; i++)
   675 	{
   676 		player = GetPlayerByIndex(i);
   677 		if (!player || !player->IsConnected())
   678 		{
   679 			continue;
   680 		}
   681 		if (engine->GetPlayerUserId(player->GetEdict()) == userid)
   682 		{
   683 			m_UserIdLookUp[userid] = i;
   684 			return i;
   685 		}
   686 	}
   687 
   688 	return 0;
   689 }
   690 
   691 void PlayerManager::AddClientListener(IClientListener *listener)
   692 {
   693 	m_hooks.push_back(listener);
   694 }
   695 
   696 void PlayerManager::RemoveClientListener(IClientListener *listener)
   697 {
   698 	m_hooks.remove(listener);
   699 }
   700 
   701 IGamePlayer *PlayerManager::GetGamePlayer(edict_t *pEdict)
   702 {
   703 	int index = engine->IndexOfEdict(pEdict);
   704 	return GetGamePlayer(index);
   705 }
   706 
   707 IGamePlayer *PlayerManager::GetGamePlayer(int client)
   708 {
   709 	return GetPlayerByIndex(client);
   710 }
   711 
   712 void PlayerManager::ClearAdminId(AdminId id)
   713 {
   714 	for (int i=1; i<=m_maxClients; i++)
   715 	{
   716 		if (m_Players[i].m_Admin == id)
   717 		{
   718 			m_Players[i].DumpAdmin(true);
   719 		}
   720 	}
   721 }
   722 
   723 void PlayerManager::ClearAllAdmins()
   724 {
   725 	for (int i=1; i<=m_maxClients; i++)
   726 	{
   727 		m_Players[i].DumpAdmin(true);
   728 	}
   729 }
   730 
   731 const char *PlayerManager::GetPassInfoVar()
   732 {
   733 	return m_PassInfoVar.c_str();
   734 }
   735 
   736 void PlayerManager::RecheckAnyAdmins()
   737 {
   738 	for (int i=1; i<=m_maxClients; i++)
   739 	{
   740 		if (m_Players[i].IsInGame() && m_Players[i].IsAuthorized())
   741 		{
   742 			m_Players[i].DoBasicAdminChecks();
   743 		}
   744 	}
   745 }
   746 
   747 
   748 /*******************
   749  *** PLAYER CODE ***
   750  *******************/
   751 
   752 CPlayer::CPlayer()
   753 {
   754 	m_IsConnected = false;
   755 	m_IsInGame = false;
   756 	m_IsAuthorized = false;
   757 	m_pEdict = NULL;
   758 	m_Admin = INVALID_ADMIN_ID;
   759 	m_TempAdmin = false;
   760 	m_Info = NULL;
   761 	m_bAdminCheckSignalled = false;
   762 	m_LastPassword.clear();
   763 }
   764 
   765 void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity)
   766 {
   767 	m_IsConnected = true;
   768 	m_Name.assign(name);
   769 	m_Ip.assign(ip);
   770 	m_pEdict = pEntity;
   771 	m_iIndex = engine->IndexOfEdict(pEntity);
   772 
   773 	char ip2[24], *ptr;
   774 	strncopy(ip2, ip, sizeof(ip2));
   775 	if ((ptr = strchr(ip2, ':')) != NULL)
   776 	{
   777 		*ptr = '\0';
   778 	}
   779 	m_IpNoPort.assign(ip2);
   780 }
   781 
   782 void CPlayer::Connect()
   783 {
   784 	if (m_IsInGame)
   785 	{
   786 		return;
   787 	}
   788 
   789 	m_IsInGame = true;
   790 
   791 	const char *var = g_Players.GetPassInfoVar();
   792 	int client = engine->IndexOfEdict(m_pEdict);
   793 	if (var[0] != '\0')
   794 	{
   795 		const char *pass = engine->GetClientConVarValue(client, var);
   796 		m_LastPassword.assign(pass ? pass : "");
   797 	} else {
   798 		m_LastPassword.assign("");
   799 	}
   800 
   801 	if (m_IsAuthorized)
   802 	{
   803 		DoPostConnectAuthorization();
   804 	}
   805 }
   806 
   807 void CPlayer::Authorize(const char *steamid)
   808 {
   809 	if (m_IsAuthorized)
   810 	{
   811 		return;
   812 	}
   813 
   814 	m_IsAuthorized = true;
   815 	m_AuthID.assign(steamid);
   816 }
   817 
   818 void CPlayer::Disconnect()
   819 {
   820 	DumpAdmin(false);
   821 	m_IsConnected = false;
   822 	m_IsInGame = false;
   823 	m_IsAuthorized = false;
   824 	m_Name.clear();
   825 	m_Ip.clear();
   826 	m_AuthID.clear();
   827 	m_pEdict = NULL;
   828 	m_Info = NULL;
   829 	m_bAdminCheckSignalled = false;
   830 }
   831 
   832 void CPlayer::SetName(const char *name)
   833 {
   834 	m_Name.assign(name);
   835 }
   836 
   837 const char *CPlayer::GetName()
   838 {
   839 	return (m_Info) ? m_Info->GetName(): m_Name.c_str();
   840 }
   841 
   842 const char *CPlayer::GetIPAddress()
   843 {
   844 	return m_Ip.c_str();
   845 }
   846 
   847 const char *CPlayer::GetAuthString()
   848 {
   849 	return m_AuthID.c_str();
   850 }
   851 
   852 edict_t *CPlayer::GetEdict()
   853 {
   854 	return m_pEdict;
   855 }
   856 
   857 bool CPlayer::IsInGame()
   858 {
   859 	return m_IsInGame;
   860 }
   861 
   862 bool CPlayer::IsConnected()
   863 {
   864 	return m_IsConnected;
   865 }
   866 
   867 bool CPlayer::IsAuthorized()
   868 {
   869 	return m_IsAuthorized;
   870 }
   871 
   872 IPlayerInfo *CPlayer::GetPlayerInfo()
   873 {
   874 	return m_Info;
   875 }
   876 
   877 bool CPlayer::IsFakeClient()
   878 {
   879 	return (strcmp(m_AuthID.c_str(), "BOT") == 0);
   880 }
   881 
   882 void CPlayer::SetAdminId(AdminId id, bool temporary)
   883 {
   884 	if (!m_IsConnected)
   885 	{
   886 		return;
   887 	}
   888 
   889 	DumpAdmin(false);
   890 
   891 	m_Admin = id;
   892 	m_TempAdmin = temporary;
   893 }
   894 
   895 AdminId CPlayer::GetAdminId()
   896 {
   897 	return m_Admin;
   898 }
   899 
   900 void CPlayer::DumpAdmin(bool deleting)
   901 {
   902 	if (m_Admin != INVALID_ADMIN_ID)
   903 	{
   904 		if (m_TempAdmin && !deleting)
   905 		{
   906 			g_Admins.InvalidateAdmin(m_Admin);
   907 		}
   908 		m_Admin = INVALID_ADMIN_ID;
   909 		m_TempAdmin = false;
   910 	}
   911 }
   912 
   913 void CPlayer::Kick(const char *str)
   914 {
   915 	INetChannel *pNetChan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(m_iIndex));
   916 	IClient *pClient = static_cast<IClient *>(pNetChan->GetMsgHandler());
   917 	pClient->Disconnect("%s", str);
   918 }
   919 
   920 void CPlayer::Authorize_Post()
   921 {
   922 	if (m_IsInGame)
   923 	{
   924 		DoPostConnectAuthorization();
   925 	}
   926 }
   927 
   928 void CPlayer::DoPostConnectAuthorization()
   929 {
   930 	cell_t result = 0;
   931 	PreAdminCheck->PushCell(m_iIndex);
   932 	PreAdminCheck->Execute(&result);
   933 
   934 	/* Defer, for better or worse */
   935 	if ((ResultType)result >= Pl_Handled)
   936 	{
   937 		return;
   938 	}
   939 
   940 	/* Sanity check */
   941 	if (!IsConnected())
   942 	{
   943 		return;
   944 	}
   945 
   946 	/* Otherwise, go ahead and do admin checks */
   947 	DoBasicAdminChecks();
   948 
   949 	/* Send the notification out */
   950 	NotifyPostAdminChecks();
   951 }
   952 
   953 void CPlayer::NotifyPostAdminChecks()
   954 {
   955 	if (m_bAdminCheckSignalled)
   956 	{
   957 		return;
   958 	}
   959 
   960 	/* Block beforehand so they can't double-call */
   961 	m_bAdminCheckSignalled = true;
   962 
   963 	PostAdminCheck->PushCell(m_iIndex);
   964 	PostAdminCheck->Execute(NULL);
   965 }
   966 
   967 void CPlayer::DoBasicAdminChecks()
   968 {
   969 	if (GetAdminId() != INVALID_ADMIN_ID)
   970 	{
   971 		return;
   972 	}
   973 
   974 	/* First check the name */
   975 	AdminId id;
   976 	int client = engine->IndexOfEdict(m_pEdict);
   977 
   978 	if ((id = g_Admins.FindAdminByIdentity("name", GetName())) != INVALID_ADMIN_ID)
   979 	{
   980 		if (!g_Players.CheckSetAdminName(client, this, id))
   981 		{
   982 			int userid = engine->GetPlayerUserId(m_pEdict);
   983 			g_Timers.CreateTimer(&s_KickPlayerTimer, 0.1f, (void *)userid, 0);
   984 		}
   985 		return;
   986 	}
   987 	
   988 	/* Check IP */
   989 	if ((id = g_Admins.FindAdminByIdentity("ip", m_IpNoPort.c_str())) != INVALID_ADMIN_ID)
   990 	{
   991 		if (g_Players.CheckSetAdmin(client, this, id))
   992 		{
   993 			return;
   994 		}
   995 	}
   996 
   997 	/* Check IP address */
   998 	if ((id = g_Admins.FindAdminByIdentity("steam", m_AuthID.c_str())) != INVALID_ADMIN_ID)
   999 	{
  1000 		if (g_Players.CheckSetAdmin(client, this, id))
  1001 		{
  1002 			return;
  1003 		}
  1004 	}
  1005 }