diff --git a/core/CPlayerManager.cpp b/core/CPlayerManager.cpp index 65bc9083..2d423a68 100644 --- a/core/CPlayerManager.cpp +++ b/core/CPlayerManager.cpp @@ -15,7 +15,7 @@ #include "ForwardSys.h" #include "ShareSys.h" -CPlayerManager g_PlayerManager; +CPlayerManager g_Players; SH_DECL_HOOK5(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, edict_t *, const char *, const char *, char *, int); SH_DECL_HOOK2_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, edict_t *, const char *); @@ -24,6 +24,17 @@ SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *) SH_DECL_HOOK1_void(IServerGameClients, ClientSettingsChanged, SH_NOATTRIB, 0, edict_t *); SH_DECL_HOOK3_void(IServerGameDLL, ServerActivate, SH_NOATTRIB, 0, edict_t *, int, int); +CPlayerManager::CPlayerManager() +{ + m_AuthQueue = NULL; + m_FirstPass = true; +} + +CPlayerManager::~CPlayerManager() +{ + delete [] m_AuthQueue; +} + void CPlayerManager::OnSourceModAllInitialized() { SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, serverClients, this, &CPlayerManager::OnClientConnect, false); @@ -37,28 +48,17 @@ void CPlayerManager::OnSourceModAllInitialized() g_ShareSys.AddInterface(NULL, this); - /* Register OnClientConnect */ ParamType p1[] = {Param_Cell, Param_String, Param_Cell}; - m_clconnect = g_Forwards.CreateForward("OnClientConnect", ET_Event, 3, p1); - - /* Register OnClientPutInServer */ ParamType p2[] = {Param_Cell}; + ParamType p3[] = {Param_Cell, Param_String}; + + m_clconnect = g_Forwards.CreateForward("OnClientConnect", ET_Event, 3, p1); m_clputinserver = g_Forwards.CreateForward("OnClientPutInServer", ET_Ignore, 1, p2); - - /* Register OnClientDisconnect */ m_cldisconnect = g_Forwards.CreateForward("OnClientDisconnect", ET_Ignore, 1, p2); - - /* Register OnClientDisconnect_Post */ m_cldisconnect_post = g_Forwards.CreateForward("OnClientDisconnect_Post", ET_Ignore, 1, p2); - - /* Register OnClientCommand */ m_clcommand = g_Forwards.CreateForward("OnClientCommand", ET_Hook, 1, p2); - - /* Register OnClientSettingsChanged */ m_clinfochanged = g_Forwards.CreateForward("OnClientSettingsChanged", ET_Ignore, 1, p2); - - /* Register OnClientAuthorized */ - //:TODO: + m_clauth = g_Forwards.CreateForward("OnClientAuthorized", ET_Ignore, 2, p2); } void CPlayerManager::OnSourceModShutdown() @@ -78,6 +78,7 @@ void CPlayerManager::OnSourceModShutdown() g_Forwards.ReleaseForward(m_cldisconnect_post); g_Forwards.ReleaseForward(m_clcommand); g_Forwards.ReleaseForward(m_clinfochanged); + g_Forwards.ReleaseForward(m_clauth); delete [] m_Players; } @@ -90,7 +91,79 @@ void CPlayerManager::OnServerActivate(edict_t *pEdictList, int edictCount, int c m_maxClients = clientMax; m_PlayerCount = 0; m_Players = new CPlayer[m_maxClients + 1]; + m_AuthQueue = new unsigned int[m_maxClients + 1]; m_FirstPass = false; + + memset(m_AuthQueue, 0, sizeof(unsigned int) * (m_maxClients + 1)); + } +} + +void CPlayerManager::RunAuthChecks() +{ + CPlayer *pPlayer; + const char *authstr; + unsigned int removed = 0; + for (unsigned int i=1; i<=m_AuthQueue[0]; i++) + { + pPlayer = GetPlayerByIndex(m_AuthQueue[i]); + authstr = engine->GetPlayerNetworkIDString(pPlayer->m_pEdict); + if (authstr && authstr[0] != '\0' + && (strcmp(authstr, "STEAM_ID_PENDING") != 0)) + { + /* Set authorization */ + pPlayer->m_AuthID.assign(authstr); + pPlayer->m_IsAuthorized = true; + + /* Send to extensions */ + List::iterator iter; + IClientListener *pListener; + for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) + { + pListener = (*iter); + pListener->OnClientAuthorized(m_AuthQueue[i], authstr); + } + + /* Send to plugins */ + if (m_clauth->GetFunctionCount()) + { + m_clauth->PushCell(m_AuthQueue[i]); + m_clauth->PushString(authstr); + m_clauth->Execute(NULL); + } + + /* Mark as removed from queue */ + m_AuthQueue[i] = 0; + removed++; + } + } + + /* Clean up the queue */ + if (removed) + { + /* We don't have to compcat the list if it's empty */ + if (removed != m_AuthQueue[0]) + { + unsigned int diff = 0; + for (unsigned int i=1; i<=m_AuthQueue[0]; i++) + { + /* If this member is removed... */ + if (m_AuthQueue[i] == 0) + { + /* Increase the differential */ + diff++; + } else { + /* diff cannot increase faster than i+1 */ + assert(i > diff); + assert(i - diff >= 1); + /* move this index down */ + m_AuthQueue[i - diff] = m_AuthQueue[i]; + } + } + m_AuthQueue[0] -= removed; + } else { + m_AuthQueue[0] = 0; + g_SourceMod.SetAuthChecking(false); + } } } @@ -117,6 +190,12 @@ bool CPlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, cons m_clconnect->PushCell(maxrejectlen); m_clconnect->Execute(&res, NULL); + if (res) + { + m_AuthQueue[++m_AuthQueue[0]] = client; + g_SourceMod.SetAuthChecking(true); + } + return (res) ? true : false; } @@ -143,6 +222,25 @@ void CPlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playernam cell_t res; int client = engine->IndexOfEdict(pEntity); + CPlayer *pPlayer = GetPlayerByIndex(client); + if (!pPlayer->IsConnected()) + { + /* Run manual connection routines */ + char error[255]; + if (!OnClientConnect(pEntity, playername, "127.0.0.1", error, sizeof(error))) + { + /* :TODO: kick the bot if it's rejected */ + return; + } + List::iterator iter; + IClientListener *pListener = NULL; + for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) + { + pListener = (*iter); + pListener->OnClientConnected(client); + } + } + List::iterator iter; IClientListener *pListener = NULL; for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) @@ -186,6 +284,27 @@ void CPlayerManager::OnClientDisconnect(edict_t *pEntity) pListener->OnClientDisconnecting(client); } + /** + * Remove client from auth queue if necessary + */ + if (!m_Players[client].IsAuthorized()) + { + for (unsigned int i=1; i<=m_AuthQueue[0]; i++) + { + if (m_AuthQueue[i] == client) + { + /* Move everything ahead of us back by one */ + for (unsigned int j=i+1; j<=m_AuthQueue[0]; j++) + { + m_AuthQueue[j-1] = m_AuthQueue[j]; + } + /* Remove us and break */ + m_AuthQueue[0]--; + break; + } + } + } + m_Players[client].Disconnect(); } @@ -275,7 +394,7 @@ CPlayer::CPlayer() m_IsConnected = false; m_IsInGame = false; m_IsAuthorized = false; - m_PlayerEdict = NULL; + m_pEdict = NULL; } void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity) @@ -283,7 +402,7 @@ void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity) m_IsConnected = true; m_Name.assign(name); m_Ip.assign(ip); - m_PlayerEdict = pEntity; + m_pEdict = pEntity; } void CPlayer::Connect() @@ -305,7 +424,7 @@ void CPlayer::Disconnect() m_Name.clear(); m_Ip.clear(); m_AuthID.clear(); - m_PlayerEdict = NULL; + m_pEdict = NULL; } void CPlayer::SetName(const char *name) @@ -330,7 +449,7 @@ const char *CPlayer::GetAuthString() const edict_t *CPlayer::GetEdict() const { - return m_PlayerEdict; + return m_pEdict; } bool CPlayer::IsInGame() const diff --git a/core/CPlayerManager.h b/core/CPlayerManager.h index 2a2201d1..4ef1ace9 100644 --- a/core/CPlayerManager.h +++ b/core/CPlayerManager.h @@ -21,6 +21,7 @@ #include #include #include +#include using namespace SourceHook; @@ -51,7 +52,7 @@ private: String m_Name; String m_Ip; String m_AuthID; - edict_t *m_PlayerEdict; + edict_t *m_pEdict; }; class CPlayerManager : @@ -59,12 +60,14 @@ class CPlayerManager : public IPlayerManager { public: - CPlayerManager() : m_FirstPass(true) {} + CPlayerManager(); + ~CPlayerManager(); public: //SMGlobalClass void OnSourceModAllInitialized(); void OnSourceModShutdown(); public: CPlayer *GetPlayerByIndex(int client) const; + void RunAuthChecks(); public: bool OnClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen); bool OnClientConnect_Post(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen); @@ -100,12 +103,14 @@ private: IForward *m_clputinserver; IForward *m_clcommand; IForward *m_clinfochanged; + IForward *m_clauth; CPlayer *m_Players; int m_maxClients; int m_PlayerCount; bool m_FirstPass; + unsigned int *m_AuthQueue; }; -extern CPlayerManager g_PlayerManager; +extern CPlayerManager g_Players; #endif //_INCLUDE_SOURCEMOD_CPLAYERMANAGER_H_ diff --git a/core/sm_stringutil.cpp b/core/sm_stringutil.cpp index 83d2b40d..1839fa5c 100644 --- a/core/sm_stringutil.cpp +++ b/core/sm_stringutil.cpp @@ -72,7 +72,7 @@ try_serverlang: pCtx->ThrowNativeError("Translation failure: English language not found."); goto error_out; } - } else if ((target >= 1) && (target <= g_PlayerManager.GetMaxClients())) { + } else if ((target >= 1) && (target <= g_Players.GetMaxClients())) { langname = "en"; //:TODO: read player's lang if (!langname || !g_Translator.GetLanguageByCode(langname, &langid)) { diff --git a/core/smn_player.cpp b/core/smn_player.cpp index ac1c8873..0d46b02b 100644 --- a/core/smn_player.cpp +++ b/core/smn_player.cpp @@ -18,37 +18,37 @@ static cell_t sm_GetClientCount(IPluginContext *pCtx, const cell_t *params) { if (params[1]) { - return g_PlayerManager.NumPlayers(); + return g_Players.NumPlayers(); } - int maxplayers = g_PlayerManager.MaxClients(); + int maxplayers = g_Players.MaxClients(); int count = 0; for (int i=1; i<=maxplayers; ++i) { - CPlayer *pPlayer = g_PlayerManager.GetPlayerByIndex(i); + CPlayer *pPlayer = g_Players.GetPlayerByIndex(i); if ((pPlayer->IsConnected()) && !(pPlayer->IsInGame())) { count++; } } - return (g_PlayerManager.NumPlayers() + count); + return (g_Players.NumPlayers() + count); } static cell_t sm_GetMaxClients(IPluginContext *pCtx, const cell_t *params) { - return g_PlayerManager.MaxClients(); + return g_Players.MaxClients(); } static cell_t sm_GetClientName(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; - if ((index < 1) || (index > g_PlayerManager.GetMaxClients())) + if ((index < 1) || (index > g_Players.GetMaxClients())) { return pCtx->ThrowNativeError("Invalid client index %d.", index); } - CPlayer *pPlayer = g_PlayerManager.GetPlayerByIndex(index); + CPlayer *pPlayer = g_Players.GetPlayerByIndex(index); if (!pPlayer->IsConnected()) { return pCtx->ThrowNativeError("Client %d is not connected.", index); @@ -61,12 +61,12 @@ static cell_t sm_GetClientName(IPluginContext *pCtx, const cell_t *params) static cell_t sm_GetClientIP(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; - if ((index < 1) || (index > g_PlayerManager.GetMaxClients())) + if ((index < 1) || (index > g_Players.GetMaxClients())) { return pCtx->ThrowNativeError("Invalid client index %d.", index); } - CPlayer *pPlayer = g_PlayerManager.GetPlayerByIndex(index); + CPlayer *pPlayer = g_Players.GetPlayerByIndex(index); if (!pPlayer->IsConnected()) { return pCtx->ThrowNativeError("Client %d is not connected.", index); @@ -87,12 +87,12 @@ static cell_t sm_GetClientIP(IPluginContext *pCtx, const cell_t *params) static cell_t sm_GetClientAuthStr(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; - if ((index < 1) || (index > g_PlayerManager.GetMaxClients())) + if ((index < 1) || (index > g_Players.GetMaxClients())) { return pCtx->ThrowNativeError("Invalid client index %d.", index); } - CPlayer *pPlayer = g_PlayerManager.GetPlayerByIndex(index); + CPlayer *pPlayer = g_Players.GetPlayerByIndex(index); if (!pPlayer->IsConnected()) { return pCtx->ThrowNativeError("Client %d is not connected.", index); @@ -105,45 +105,45 @@ static cell_t sm_GetClientAuthStr(IPluginContext *pCtx, const cell_t *params) static cell_t sm_IsPlayerConnected(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; - if ((index < 1) || (index > g_PlayerManager.GetMaxClients())) + if ((index < 1) || (index > g_Players.GetMaxClients())) { return pCtx->ThrowNativeError("Invalid client index %d.", index); } - return (g_PlayerManager.GetPlayerByIndex(index)->IsConnected()) ? 1 : 0; + return (g_Players.GetPlayerByIndex(index)->IsConnected()) ? 1 : 0; } static cell_t sm_IsPlayerIngame(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; - if ((index < 1) || (index > g_PlayerManager.GetMaxClients())) + if ((index < 1) || (index > g_Players.GetMaxClients())) { return pCtx->ThrowNativeError("Invalid client index %d.", index); } - return (g_PlayerManager.GetPlayerByIndex(index)->IsInGame()) ? 1 : 0; + return (g_Players.GetPlayerByIndex(index)->IsInGame()) ? 1 : 0; } static cell_t sm_IsPlayerAuthorized(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; - if ((index < 1) || (index > g_PlayerManager.GetMaxClients())) + if ((index < 1) || (index > g_Players.GetMaxClients())) { return pCtx->ThrowNativeError("Invalid client index %d.", index); } - return (g_PlayerManager.GetPlayerByIndex(index)->IsAuthorized()) ? 1 : 0; + return (g_Players.GetPlayerByIndex(index)->IsAuthorized()) ? 1 : 0; } static cell_t sm_IsPlayerFakeClient(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; - if ((index < 1) || (index > g_PlayerManager.GetMaxClients())) + if ((index < 1) || (index > g_Players.GetMaxClients())) { return pCtx->ThrowNativeError("Invalid client index %d.", index); } - CPlayer *pPlayer = g_PlayerManager.GetPlayerByIndex(index); + CPlayer *pPlayer = g_Players.GetPlayerByIndex(index); if (!pPlayer->IsConnected()) { return pCtx->ThrowNativeError("Client %d is not connected.", index); @@ -172,12 +172,12 @@ static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params) static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; - if ((index < 1) || (index > g_PlayerManager.GetMaxClients())) + if ((index < 1) || (index > g_Players.GetMaxClients())) { return pCtx->ThrowNativeError("Invalid client index %d.", index); } - CPlayer *pPlayer = g_PlayerManager.GetPlayerByIndex(index); + CPlayer *pPlayer = g_Players.GetPlayerByIndex(index); if (!pPlayer->IsInGame()) { return pCtx->ThrowNativeError("Client %d is not in game.", index); diff --git a/core/sourcemm_api.cpp b/core/sourcemm_api.cpp index 1bbd5f0b..bb3af1a3 100644 --- a/core/sourcemm_api.cpp +++ b/core/sourcemm_api.cpp @@ -21,6 +21,7 @@ IVEngineServer *engine = NULL; IServerGameDLL *gamedll = NULL; IServerGameClients *serverClients = NULL; ISmmPluginManager *g_pMMPlugins = NULL; +CGlobalVars *gpGlobals = NULL; PLUGIN_EXPOSE(SourceMod, g_SourceMod_Core); @@ -41,6 +42,8 @@ bool SourceMod_Core::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen return false; } + gpGlobals = ismm->pGlobals(); + return g_SourceMod.InitializeSourceMod(error, maxlen, late); } diff --git a/core/sourcemm_api.h b/core/sourcemm_api.h index 12e6cf3b..6b1842e3 100644 --- a/core/sourcemm_api.h +++ b/core/sourcemm_api.h @@ -45,6 +45,7 @@ extern IVEngineServer *engine; extern IServerGameDLL *gamedll; extern IServerGameClients *serverClients; extern ISmmPluginManager *g_pMMPlugins; +extern CGlobalVars *gpGlobals; PLUGIN_GLOBALVARS(); diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index a09ccbc5..72e57d91 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -23,9 +23,11 @@ #include "ExtensionSys.h" #include "AdminCache.h" #include "sm_stringutil.h" +#include "CPlayerManager.h" SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool); SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false); +SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, false, bool); SourcePawnEngine g_SourcePawn; SourceModBase g_SourceMod; @@ -35,6 +37,8 @@ SourceHook::String g_BaseDir; ISourcePawnEngine *g_pSourcePawn = &g_SourcePawn; IVirtualMachine *g_pVM; IdentityToken_t *g_pCoreIdent = NULL; +float g_LastTime = 0.0f; +float g_LastAuthCheck = 0.0f; typedef int (*GIVEENGINEPOINTER)(ISourcePawnEngine *); typedef unsigned int (*GETEXPORTCOUNT)(); @@ -150,6 +154,7 @@ void SourceModBase::StartSourceMod(bool late) /* First initialize the global hooks we need */ SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false); SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false); + SH_ADD_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, this, &SourceModBase::GameFrame, false); /* Notify! */ SMGlobalClass *pBase = SMGlobalClass::head; @@ -178,6 +183,8 @@ bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, ch { m_IsMapLoading = true; m_ExecPluginReload = true; + g_LastTime = 0.0f; + g_LastAuthCheck = 0.0f; g_Logger.MapChange(pMapName); @@ -191,6 +198,25 @@ bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, ch RETURN_META_VALUE(MRES_IGNORED, true); } +void SourceModBase::GameFrame(bool simulating) +{ + /** + * Note: This is all hardcoded rather than delegated to save + * precious CPU cycles. + */ + float curtime = gpGlobals->curtime; + if (curtime - g_LastTime > 0.1f) + { + if (m_CheckingAuth + && (gpGlobals->curtime - g_LastAuthCheck > 0.7f)) + { + g_LastAuthCheck = gpGlobals->curtime; + g_Players.RunAuthChecks(); + } + g_LastTime = curtime; + } +} + void SourceModBase::LevelShutdown() { if (m_ExecPluginReload) @@ -280,6 +306,7 @@ void SourceModBase::CloseSourceMod() SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false); SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false); + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, this, &SourceModBase::GameFrame, false); /* Rest In Peace */ ShutdownJIT(); @@ -349,6 +376,11 @@ void SourceModBase::SetGlobalTarget(unsigned int index) m_target = index; } +void SourceModBase::SetAuthChecking(bool set) +{ + m_CheckingAuth = set; +} + unsigned int SourceModBase::GetGlobalTarget() const { return m_target; diff --git a/core/sourcemod.h b/core/sourcemod.h index bbe290a2..a906b759 100644 --- a/core/sourcemod.h +++ b/core/sourcemod.h @@ -37,8 +37,8 @@ public: void StartSourceMod(bool late); /** - * @brief Shuts down all SourceMod components - */ + * @brief Shuts down all SourceMod components + */ void CloseSourceMod(); /** @@ -47,8 +47,8 @@ public: bool LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background); /** - * @brief Level shutdown hook - */ + * @brief Level shutdown hook + */ void LevelShutdown(); /** @@ -57,15 +57,14 @@ public: bool IsMapLoading(); /** - * @brief Stores the global target index. - */ + * @brief Stores the global target index. + */ void SetGlobalTarget(unsigned int index); /** - * @brief Returns the global target index. - */ + * @brief Returns the global target index. + */ unsigned int GetGlobalTarget() const; - public: //ISourceMod const char *GetModPath(); const char *GetSourceModPath(); @@ -73,17 +72,20 @@ public: //ISourceMod void LogMessage(IExtension *pExt, const char *format, ...); void LogError(IExtension *pExt, const char *format, ...); size_t FormatString(char *buffer, size_t maxlength, IPluginContext *pContext, const cell_t *params, unsigned int param); + void SetAuthChecking(bool set); private: /** * @brief Loading plugins */ void DoGlobalPluginLoads(); + void GameFrame(bool simulating); private: char m_SMBaseDir[PLATFORM_MAX_PATH+1]; char m_SMRelDir[PLATFORM_MAX_PATH+1]; bool m_IsMapLoading; bool m_ExecPluginReload; unsigned int m_target; + bool m_CheckingAuth; }; extern SourceModBase g_SourceMod; diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc index 33876e00..21fcd148 100644 --- a/plugins/include/sourcemod.inc +++ b/plugins/include/sourcemod.inc @@ -136,6 +136,15 @@ forward OnClientCommand(client); */ forward OnClientSettingsChanged(client); +/** + * Called when a client receives a Steam ID. + * @note This is called by bots, but the ID will be "BOT" + * + * @param client Player index. + * @param auth Player auth string. + */ +forward OnClientAuthorized(client, const String:auth[]); + /** * Returns the maximum number of clients allowed on the server. *