dvander@370: /** dvander@370: * =============================================================== dvander@370: * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. dvander@370: * =============================================================== dvander@370: * dvander@370: * This file is not open source and may not be copied without explicit dvander@370: * written permission of AlliedModders LLC. This file may not be redistributed dvander@370: * in whole or significant part. dvander@370: * For information, see LICENSE.txt or http://www.sourcemod.net/license.php dvander@370: * dvander@370: * Version: $Id$ dvander@370: */ dvander@370: faluco@226: #include "CPlayerManager.h" faluco@226: #include "ForwardSys.h" dvander@435: #include "ShareSys.h" dvander@458: #include "AdminCache.h" dvander@500: #include "CConCmdManager.h" faluco@226: dvander@448: CPlayerManager g_Players; faluco@242: faluco@226: SH_DECL_HOOK5(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, edict_t *, const char *, const char *, char *, int); faluco@226: SH_DECL_HOOK2_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, edict_t *, const char *); faluco@226: SH_DECL_HOOK1_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, edict_t *); faluco@226: SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *); faluco@242: SH_DECL_HOOK1_void(IServerGameClients, ClientSettingsChanged, SH_NOATTRIB, 0, edict_t *); faluco@268: SH_DECL_HOOK3_void(IServerGameDLL, ServerActivate, SH_NOATTRIB, 0, edict_t *, int, int); faluco@226: dvander@448: CPlayerManager::CPlayerManager() dvander@448: { dvander@448: m_AuthQueue = NULL; dvander@448: m_FirstPass = true; dvander@448: } dvander@448: dvander@448: CPlayerManager::~CPlayerManager() dvander@448: { dvander@448: delete [] m_AuthQueue; dvander@448: } dvander@448: faluco@226: void CPlayerManager::OnSourceModAllInitialized() faluco@226: { faluco@226: SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &CPlayerManager::OnClientConnect, false); dvander@435: SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &CPlayerManager::OnClientConnect_Post, true); faluco@226: SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, serverClients, this, &CPlayerManager::OnClientPutInServer, true); faluco@226: SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &CPlayerManager::OnClientDisconnect, false); faluco@226: SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &CPlayerManager::OnClientDisconnect_Post, true); faluco@226: SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientCommand, serverClients, this, &CPlayerManager::OnClientCommand, false); faluco@242: SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, serverClients, this, &CPlayerManager::OnClientSettingsChanged, true); faluco@268: SH_ADD_HOOK_MEMFUNC(IServerGameDLL, ServerActivate, gamedll, this, &CPlayerManager::OnServerActivate, true); faluco@226: dvander@435: g_ShareSys.AddInterface(NULL, this); dvander@435: faluco@226: ParamType p1[] = {Param_Cell, Param_String, Param_Cell}; faluco@226: ParamType p2[] = {Param_Cell}; faluco@226: dvander@448: m_clconnect = g_Forwards.CreateForward("OnClientConnect", ET_Event, 3, p1); dvander@448: m_clputinserver = g_Forwards.CreateForward("OnClientPutInServer", ET_Ignore, 1, p2); dvander@448: m_cldisconnect = g_Forwards.CreateForward("OnClientDisconnect", ET_Ignore, 1, p2); faluco@226: m_cldisconnect_post = g_Forwards.CreateForward("OnClientDisconnect_Post", ET_Ignore, 1, p2); dvander@500: m_clcommand = g_Forwards.CreateForward("OnClientCommand", ET_Hook, 2, NULL, Param_Cell, Param_Cell); faluco@242: m_clinfochanged = g_Forwards.CreateForward("OnClientSettingsChanged", ET_Ignore, 1, p2); dvander@500: m_clauth = g_Forwards.CreateForward("OnClientAuthorized", ET_Ignore, 2, NULL, Param_Cell, Param_String); faluco@226: } faluco@226: faluco@226: void CPlayerManager::OnSourceModShutdown() faluco@226: { faluco@226: SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &CPlayerManager::OnClientConnect, false); faluco@226: SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, serverClients, this, &CPlayerManager::OnClientPutInServer, true); faluco@226: SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &CPlayerManager::OnClientDisconnect, false); faluco@226: SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, serverClients, this, &CPlayerManager::OnClientDisconnect_Post, true); faluco@226: SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientCommand, serverClients, this, &CPlayerManager::OnClientCommand, false); faluco@242: SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, serverClients, this, &CPlayerManager::OnClientSettingsChanged, true); faluco@268: SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, ServerActivate, gamedll, this, &CPlayerManager::OnServerActivate, true); faluco@226: faluco@226: /* Release forwards */ faluco@226: g_Forwards.ReleaseForward(m_clconnect); faluco@226: g_Forwards.ReleaseForward(m_clputinserver); faluco@226: g_Forwards.ReleaseForward(m_cldisconnect); faluco@226: g_Forwards.ReleaseForward(m_cldisconnect_post); faluco@226: g_Forwards.ReleaseForward(m_clcommand); faluco@242: g_Forwards.ReleaseForward(m_clinfochanged); dvander@448: g_Forwards.ReleaseForward(m_clauth); faluco@242: faluco@242: delete [] m_Players; faluco@226: } faluco@226: faluco@268: void CPlayerManager::OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax) faluco@268: { faluco@269: if (m_FirstPass) faluco@269: { faluco@269: /* Initialize all players */ faluco@269: m_maxClients = clientMax; faluco@269: m_PlayerCount = 0; faluco@269: m_Players = new CPlayer[m_maxClients + 1]; dvander@448: m_AuthQueue = new unsigned int[m_maxClients + 1]; faluco@269: m_FirstPass = false; dvander@448: dvander@448: memset(m_AuthQueue, 0, sizeof(unsigned int) * (m_maxClients + 1)); dvander@448: } dvander@448: } dvander@448: dvander@448: void CPlayerManager::RunAuthChecks() dvander@448: { dvander@448: CPlayer *pPlayer; dvander@448: const char *authstr; dvander@448: unsigned int removed = 0; dvander@448: for (unsigned int i=1; i<=m_AuthQueue[0]; i++) dvander@448: { dvander@448: pPlayer = GetPlayerByIndex(m_AuthQueue[i]); dvander@448: authstr = engine->GetPlayerNetworkIDString(pPlayer->m_pEdict); dvander@448: if (authstr && authstr[0] != '\0' dvander@448: && (strcmp(authstr, "STEAM_ID_PENDING") != 0)) dvander@448: { dvander@448: /* Set authorization */ dvander@448: pPlayer->m_AuthID.assign(authstr); dvander@448: pPlayer->m_IsAuthorized = true; dvander@448: dvander@448: /* Send to extensions */ dvander@448: List::iterator iter; dvander@448: IClientListener *pListener; dvander@448: for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) dvander@448: { dvander@448: pListener = (*iter); dvander@448: pListener->OnClientAuthorized(m_AuthQueue[i], authstr); dvander@448: } dvander@448: dvander@448: /* Send to plugins */ dvander@448: if (m_clauth->GetFunctionCount()) dvander@448: { dvander@448: m_clauth->PushCell(m_AuthQueue[i]); dvander@448: m_clauth->PushString(authstr); dvander@448: m_clauth->Execute(NULL); dvander@448: } dvander@448: dvander@448: /* Mark as removed from queue */ dvander@448: m_AuthQueue[i] = 0; dvander@448: removed++; dvander@448: } dvander@448: } dvander@448: dvander@448: /* Clean up the queue */ dvander@448: if (removed) dvander@448: { dvander@448: /* We don't have to compcat the list if it's empty */ dvander@448: if (removed != m_AuthQueue[0]) dvander@448: { dvander@448: unsigned int diff = 0; dvander@448: for (unsigned int i=1; i<=m_AuthQueue[0]; i++) dvander@448: { dvander@448: /* If this member is removed... */ dvander@448: if (m_AuthQueue[i] == 0) dvander@448: { dvander@448: /* Increase the differential */ dvander@448: diff++; dvander@448: } else { dvander@448: /* diff cannot increase faster than i+1 */ dvander@448: assert(i > diff); dvander@448: assert(i - diff >= 1); dvander@448: /* move this index down */ dvander@448: m_AuthQueue[i - diff] = m_AuthQueue[i]; dvander@448: } dvander@448: } dvander@448: m_AuthQueue[0] -= removed; dvander@448: } else { dvander@448: m_AuthQueue[0] = 0; dvander@448: g_SourceMod.SetAuthChecking(false); dvander@448: } faluco@269: } faluco@268: } faluco@268: faluco@226: bool CPlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen) faluco@226: { dvander@435: int client = engine->IndexOfEdict(pEntity); dvander@435: dvander@435: List::iterator iter; dvander@435: IClientListener *pListener = NULL; dvander@435: for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) dvander@435: { dvander@435: pListener = (*iter); dvander@435: if (!pListener->InterceptClientConnect(client, reject, maxrejectlen)) dvander@435: { dvander@435: return false; dvander@435: } dvander@435: } dvander@435: faluco@226: cell_t res = 1; faluco@226: faluco@242: m_Players[client].Initialize(pszName, pszAddress, pEntity); faluco@242: m_clconnect->PushCell(client); faluco@226: m_clconnect->PushStringEx(reject, maxrejectlen, SM_PARAM_STRING_UTF8, SM_PARAM_COPYBACK); faluco@226: m_clconnect->PushCell(maxrejectlen); faluco@226: m_clconnect->Execute(&res, NULL); faluco@226: dvander@448: if (res) dvander@448: { dvander@448: m_AuthQueue[++m_AuthQueue[0]] = client; dvander@448: g_SourceMod.SetAuthChecking(true); dvander@448: } dvander@448: dvander@500: //:todo: this must meta return faluco@226: return (res) ? true : false; faluco@226: } faluco@226: dvander@435: bool CPlayerManager::OnClientConnect_Post(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen) dvander@435: { dvander@435: int client = engine->IndexOfEdict(pEntity); dvander@435: bool orig_value = META_RESULT_ORIG_RET(bool); dvander@435: if (orig_value) dvander@435: { dvander@435: List::iterator iter; dvander@435: IClientListener *pListener = NULL; dvander@435: for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) dvander@435: { dvander@435: pListener = (*iter); dvander@435: pListener->OnClientConnected(client); dvander@435: } dvander@435: } dvander@435: dvander@435: return true; dvander@435: } dvander@435: faluco@226: void CPlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername) faluco@226: { faluco@226: cell_t res; faluco@242: int client = engine->IndexOfEdict(pEntity); faluco@226: dvander@448: CPlayer *pPlayer = GetPlayerByIndex(client); dvander@448: if (!pPlayer->IsConnected()) dvander@448: { dvander@448: /* Run manual connection routines */ dvander@448: char error[255]; dvander@448: if (!OnClientConnect(pEntity, playername, "127.0.0.1", error, sizeof(error))) dvander@448: { dvander@448: /* :TODO: kick the bot if it's rejected */ dvander@448: return; dvander@448: } dvander@448: List::iterator iter; dvander@448: IClientListener *pListener = NULL; dvander@448: for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) dvander@448: { dvander@448: pListener = (*iter); dvander@448: pListener->OnClientConnected(client); dvander@448: } dvander@448: } dvander@448: dvander@435: List::iterator iter; dvander@435: IClientListener *pListener = NULL; dvander@435: for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) dvander@435: { dvander@435: pListener = (*iter); dvander@435: pListener->OnClientPutInServer(client); dvander@435: } dvander@435: faluco@242: m_Players[client].Connect(); faluco@242: m_PlayerCount++; faluco@242: m_clputinserver->PushCell(client); faluco@226: m_clputinserver->Execute(&res, NULL); faluco@226: } faluco@226: faluco@226: void CPlayerManager::OnClientAuthorized() faluco@226: { faluco@226: //:TODO: faluco@226: } faluco@226: faluco@226: void CPlayerManager::OnClientDisconnect(edict_t *pEntity) faluco@226: { faluco@226: cell_t res; faluco@242: int client = engine->IndexOfEdict(pEntity); faluco@226: dvander@435: if (m_Players[client].IsConnected()) faluco@242: { faluco@242: m_cldisconnect->PushCell(client); faluco@242: m_cldisconnect->Execute(&res, NULL); faluco@242: } dvander@435: dvander@435: if (m_Players[client].IsInGame()) faluco@242: { faluco@242: m_PlayerCount--; faluco@242: } dvander@435: dvander@435: List::iterator iter; dvander@435: IClientListener *pListener = NULL; dvander@435: for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) dvander@435: { dvander@435: pListener = (*iter); dvander@435: pListener->OnClientDisconnecting(client); dvander@435: } dvander@435: dvander@448: /** dvander@448: * Remove client from auth queue if necessary dvander@448: */ dvander@448: if (!m_Players[client].IsAuthorized()) dvander@448: { dvander@448: for (unsigned int i=1; i<=m_AuthQueue[0]; i++) dvander@448: { dvander@464: if (m_AuthQueue[i] == (unsigned)client) dvander@448: { dvander@448: /* Move everything ahead of us back by one */ dvander@448: for (unsigned int j=i+1; j<=m_AuthQueue[0]; j++) dvander@448: { dvander@448: m_AuthQueue[j-1] = m_AuthQueue[j]; dvander@448: } dvander@448: /* Remove us and break */ dvander@448: m_AuthQueue[0]--; dvander@448: break; dvander@448: } dvander@448: } dvander@448: } dvander@448: faluco@242: m_Players[client].Disconnect(); faluco@226: } faluco@226: faluco@226: void CPlayerManager::OnClientDisconnect_Post(edict_t *pEntity) faluco@226: { faluco@226: cell_t res; dvander@435: int client = engine->IndexOfEdict(pEntity); faluco@226: dvander@435: m_cldisconnect_post->PushCell(client); faluco@226: m_cldisconnect_post->Execute(&res, NULL); dvander@435: dvander@435: List::iterator iter; dvander@435: IClientListener *pListener = NULL; dvander@435: for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) dvander@435: { dvander@435: pListener = (*iter); dvander@435: pListener->OnClientDisconnected(client); dvander@435: } faluco@226: } faluco@226: faluco@226: void CPlayerManager::OnClientCommand(edict_t *pEntity) faluco@226: { dvander@500: cell_t res = Pl_Continue; dvander@500: int client = engine->IndexOfEdict(pEntity); dvander@500: dvander@500: int args = engine->Cmd_Argc() - 1; dvander@500: dvander@500: m_clcommand->PushCell(client); dvander@500: m_clcommand->PushCell(args); dvander@500: m_clcommand->Execute(&res, NULL); faluco@226: dvander@500: if (res >= Pl_Stop) dvander@500: { dvander@500: RETURN_META(MRES_SUPERCEDE); dvander@500: } dvander@500: dvander@500: res = g_ConCmds.DispatchClientCommand(client, (ResultType)res); dvander@500: dvander@500: if (res >= Pl_Handled) dvander@500: { dvander@500: RETURN_META(MRES_SUPERCEDE); dvander@500: } faluco@242: } faluco@242: faluco@242: void CPlayerManager::OnClientSettingsChanged(edict_t *pEntity) faluco@242: { faluco@242: cell_t res; faluco@242: int client = engine->IndexOfEdict(pEntity); faluco@242: faluco@242: m_clinfochanged->PushCell(engine->IndexOfEdict(pEntity)); faluco@242: m_clinfochanged->Execute(&res, NULL); dvander@435: m_Players[client].SetName(engine->GetClientConVarValue(client, "name")); dvander@435: } dvander@435: dvander@435: int CPlayerManager::GetMaxClients() dvander@435: { dvander@435: return m_maxClients; dvander@435: } dvander@435: dvander@435: CPlayer *CPlayerManager::GetPlayerByIndex(int client) const dvander@435: { dvander@456: if (client > m_maxClients || client < 1) faluco@242: { dvander@435: return NULL; faluco@242: } dvander@435: return &m_Players[client]; faluco@242: } dvander@435: dvander@435: int CPlayerManager::GetNumPlayers() dvander@435: { dvander@435: return m_PlayerCount; dvander@435: } dvander@435: dvander@435: void CPlayerManager::AddClientListener(IClientListener *listener) dvander@435: { dvander@435: m_hooks.push_back(listener); dvander@435: } dvander@435: dvander@435: void CPlayerManager::RemoveClientListener(IClientListener *listener) dvander@435: { dvander@435: m_hooks.remove(listener); dvander@435: } dvander@435: dvander@435: IGamePlayer *CPlayerManager::GetGamePlayer(edict_t *pEdict) dvander@435: { dvander@435: int index = engine->IndexOfEdict(pEdict); dvander@435: return GetGamePlayer(index); dvander@435: } dvander@435: dvander@435: IGamePlayer *CPlayerManager::GetGamePlayer(int client) dvander@435: { dvander@435: return GetPlayerByIndex(client); dvander@435: } dvander@435: dvander@458: void CPlayerManager::ClearAdminId(AdminId id) dvander@458: { dvander@458: for (int i=1; i<=m_maxClients; i++) dvander@458: { dvander@458: if (m_Players[i].m_Admin == id) dvander@458: { dvander@458: m_Players[i].DumpAdmin(true); dvander@458: } dvander@458: } dvander@458: } dvander@458: dvander@458: void CPlayerManager::ClearAllAdmins() dvander@458: { dvander@458: for (int i=1; i<=m_maxClients; i++) dvander@458: { dvander@458: m_Players[i].DumpAdmin(true); dvander@458: } dvander@458: } dvander@458: dvander@458: dvander@435: /******************* dvander@435: *** PLAYER CODE *** dvander@435: *******************/ dvander@435: dvander@435: CPlayer::CPlayer() dvander@435: { dvander@435: m_IsConnected = false; dvander@435: m_IsInGame = false; dvander@435: m_IsAuthorized = false; dvander@448: m_pEdict = NULL; dvander@458: m_Admin = INVALID_ADMIN_ID; dvander@458: m_TempAdmin = false; dvander@435: } dvander@435: dvander@435: void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity) dvander@435: { dvander@435: m_IsConnected = true; dvander@435: m_Name.assign(name); dvander@435: m_Ip.assign(ip); dvander@448: m_pEdict = pEntity; dvander@435: } dvander@435: dvander@435: void CPlayer::Connect() dvander@435: { dvander@435: m_IsInGame = true; dvander@435: } dvander@435: dvander@435: void CPlayer::Authorize(const char *steamid) dvander@435: { dvander@435: m_IsAuthorized = true; dvander@435: m_AuthID.assign(steamid); dvander@435: } dvander@435: dvander@435: void CPlayer::Disconnect() dvander@435: { dvander@458: DumpAdmin(false); dvander@435: m_IsConnected = false; dvander@435: m_IsInGame = false; dvander@435: m_IsAuthorized = false; dvander@435: m_Name.clear(); dvander@435: m_Ip.clear(); dvander@435: m_AuthID.clear(); dvander@448: m_pEdict = NULL; dvander@435: } dvander@435: dvander@435: void CPlayer::SetName(const char *name) dvander@435: { dvander@435: m_Name.assign(name); dvander@435: } dvander@435: dvander@435: const char *CPlayer::GetName() const dvander@435: { dvander@435: return m_Name.c_str(); dvander@435: } dvander@435: dvander@435: const char *CPlayer::GetIPAddress() const dvander@435: { dvander@435: return m_Ip.c_str(); dvander@435: } dvander@435: dvander@435: const char *CPlayer::GetAuthString() const dvander@435: { dvander@435: return m_AuthID.c_str(); dvander@435: } dvander@435: dvander@435: edict_t *CPlayer::GetEdict() const dvander@435: { dvander@448: return m_pEdict; dvander@435: } dvander@435: dvander@435: bool CPlayer::IsInGame() const dvander@435: { dvander@435: return m_IsInGame; dvander@435: } dvander@435: dvander@435: bool CPlayer::IsConnected() const dvander@435: { dvander@435: return m_IsConnected; dvander@435: } dvander@435: dvander@435: bool CPlayer::IsAuthorized() const dvander@435: { dvander@435: return m_IsAuthorized; dvander@435: } dvander@435: dvander@435: bool CPlayer::IsFakeClient() const dvander@435: { dvander@435: return (strcmp(m_AuthID.c_str(), "BOT") == 0); dvander@435: } dvander@458: dvander@458: void CPlayer::SetAdminId(AdminId id, bool temporary) dvander@458: { dvander@458: if (!m_IsConnected) dvander@458: { dvander@458: return; dvander@458: } dvander@458: dvander@458: DumpAdmin(false); dvander@458: dvander@458: m_Admin = id; dvander@458: m_TempAdmin = temporary; dvander@458: } dvander@458: dvander@458: AdminId CPlayer::GetAdminId() const dvander@458: { dvander@458: return m_Admin; dvander@458: } dvander@458: dvander@458: void CPlayer::DumpAdmin(bool deleting) dvander@458: { dvander@458: if (m_Admin != INVALID_ADMIN_ID) dvander@458: { dvander@458: if (m_TempAdmin && !deleting) dvander@458: { dvander@458: g_Admins.InvalidateAdmin(m_Admin); dvander@458: } dvander@458: m_Admin = INVALID_ADMIN_ID; dvander@458: m_TempAdmin = false; dvander@458: } dvander@458: }