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