diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp index 871564c06..c48a38ff3 100644 --- a/core/PlayerManager.cpp +++ b/core/PlayerManager.cpp @@ -68,6 +68,15 @@ 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); +#if defined ORANGEBOX_BUILD +SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &); +#else +extern bool __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0); +extern bool __SourceHook_FHRemoveConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0); +#endif + +ConCommand *maxplayersCmd = NULL; + class KickPlayerTimer : public ITimedEvent { public: @@ -137,6 +146,32 @@ void PlayerManager::OnSourceModAllInitialized() m_bIsListenServer = !engine->IsDedicatedServer(); m_ListenClient = 0; + + g_ConVarManager.AddConVarChangeListener("tv_enable", this); + + ConCommandBase *pBase = icvar->GetCommands(); + ConCommand *pCmd = NULL; + while (pBase) + { + if (strcmp(pBase->GetName(), "maxplayers") == 0) + { + /* Don't want to return convar with same name */ + if (!pBase->IsCommand()) + { + break; + } + + pCmd = (ConCommand *)pBase; + break; + } + pBase = const_cast(pBase->GetNext()); + } + + if (pCmd != NULL) + { + SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CmdMaxplayersCallback, true); + maxplayersCmd = pCmd; + } } void PlayerManager::OnSourceModShutdown() @@ -165,6 +200,13 @@ void PlayerManager::OnSourceModShutdown() g_Forwards.ReleaseForward(PostAdminFilter); delete [] m_Players; + + g_ConVarManager.RemoveConVarChangeListener("tv_enable", this); + + if (maxplayersCmd != NULL) + { + SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, maxplayersCmd, CmdMaxplayersCallback, true); + } } ConfigResult PlayerManager::OnSourceModConfigChanged(const char *key, @@ -202,11 +244,11 @@ void PlayerManager::OnServerActivate(edict_t *pEdictList, int edictCount, int cl /* Initialize all players */ m_maxClients = clientMax; m_PlayerCount = 0; - m_Players = new CPlayer[m_maxClients + 1]; - m_AuthQueue = new unsigned int[m_maxClients + 1]; + m_Players = new CPlayer[ABSOLUTE_PLAYER_LIMIT + 1]; + m_AuthQueue = new unsigned int[ABSOLUTE_PLAYER_LIMIT + 1]; m_FirstPass = true; - memset(m_AuthQueue, 0, sizeof(unsigned int) * (m_maxClients + 1)); + memset(m_AuthQueue, 0, sizeof(unsigned int) * (ABSOLUTE_PLAYER_LIMIT + 1)); g_NumPlayersToAuth = &m_AuthQueue[0]; } @@ -1203,6 +1245,68 @@ void PlayerManager::ProcessCommandTarget(cmd_target_info_t *info) } } +void PlayerManager::OnConVarChanged( ConVar *pConVar, const char *oldValue, float flOldValue ) +{ + if (pConVar->GetBool() && !atoi(oldValue)) + { + MaxPlayersChanged(gpGlobals->maxClients + 1); + } + else + { + MaxPlayersChanged(); + } +} + +void PlayerManager::OnSourceModMaxPlayersChanged( int newvalue ) +{ + m_maxClients = newvalue; +} + +void PlayerManager::MaxPlayersChanged( int newvalue /*= -1*/ ) +{ + if (newvalue == -1) + { + newvalue = gpGlobals->maxClients; + } + + if (newvalue == MaxClients()) + { + return; + } + + /* Notify the rest of core */ + SMGlobalClass *pBase = SMGlobalClass::head; + while (pBase) + { + pBase->OnSourceModMaxPlayersChanged(newvalue); + pBase = pBase->m_pGlobalClassNext; + } + + /* Notify Extensions */ + List::iterator iter; + IClientListener *pListener = NULL; + for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++) + { + pListener = (*iter); + if (pListener->GetClientListenerVersion() >= 8) + { + pListener->OnMaxPlayersChanged(newvalue); + } + } +} + +#if defined ORANGEBOX_BUILD +void CmdMaxplayersCallback(const CCommand &command) +{ +#else +void CmdMaxplayersCallback() +{ +#endif + + g_Players.MaxPlayersChanged(); +} + + /******************* *** PLAYER CODE *** *******************/ diff --git a/core/PlayerManager.h b/core/PlayerManager.h index 79b27d4fc..8fcb56068 100644 --- a/core/PlayerManager.h +++ b/core/PlayerManager.h @@ -41,6 +41,7 @@ #include #include #include +#include "ConVarManager.h" using namespace SourceHook; @@ -110,7 +111,8 @@ private: class PlayerManager : public SMGlobalClass, - public IPlayerManager + public IPlayerManager, + public IConVarChangeListener { friend class CPlayer; public: @@ -121,6 +123,7 @@ public: //SMGlobalClass void OnSourceModShutdown(); void OnSourceModLevelEnd(); ConfigResult OnSourceModConfigChanged(const char *key, const char *value, ConfigSource source, char *error, size_t maxlength); + void OnSourceModMaxPlayersChanged(int newvalue); public: CPlayer *GetPlayerByIndex(int client) const; void RunAuthChecks(); @@ -153,6 +156,8 @@ public: //IPlayerManager void RegisterCommandTargetProcessor(ICommandTargetProcessor *pHandler); void UnregisterCommandTargetProcessor(ICommandTargetProcessor *pHandler); void ProcessCommandTarget(cmd_target_info_t *info); +public: // IConVarChangeListener + void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue); public: inline int MaxClients() { @@ -172,6 +177,7 @@ public: void RecheckAnyAdmins(); unsigned int GetReplyTo(); unsigned int SetReplyTo(unsigned int reply); + void MaxPlayersChanged(int newvalue = -1); private: void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax); void InvalidatePlayer(CPlayer *pPlayer); @@ -198,6 +204,12 @@ private: int m_ListenClient; }; +#if defined ORANGEBOX_BUILD +void CmdMaxplayersCallback(const CCommand &command); +#else +void CmdMaxplayersCallback(); +#endif + extern PlayerManager g_Players; extern bool g_OnMapStarted; extern const unsigned int *g_NumPlayersToAuth; diff --git a/core/sm_globals.h b/core/sm_globals.h index 8165a1f6f..3365c25b6 100644 --- a/core/sm_globals.h +++ b/core/sm_globals.h @@ -74,6 +74,7 @@ class SMGlobalClass friend class SourceModBase; friend class CoreConfig; friend class CExtensionManager; + friend class PlayerManager; public: SMGlobalClass(); public: @@ -167,6 +168,15 @@ public: virtual void OnSourceModIdentityDropped(IdentityToken_t *pToken) { } + + /** + * @brief Called when the server maxplayers changes + * + * @param newvalue New maxplayers value. + */ + virtual void OnSourceModMaxPlayersChanged(int newvalue) + { + } private: SMGlobalClass *m_pGlobalClassNext; static SMGlobalClass *head; diff --git a/extensions/topmenus/TopMenu.cpp b/extensions/topmenus/TopMenu.cpp index 486a992a8..a4b971877 100644 --- a/extensions/topmenus/TopMenu.cpp +++ b/extensions/topmenus/TopMenu.cpp @@ -93,7 +93,7 @@ unsigned int TopMenu::CalcMemUsage() size += m_Config.strings.GetMemTable()->MemUsage(); size += (m_Config.cats.size() * sizeof(int)); - size += (sizeof(topmenu_player_t) * m_max_clients); + size += (sizeof(topmenu_player_t) * (ABSOLUTE_PLAYER_LIMIT + 1)); size += (m_SortedCats.size() * sizeof(unsigned int)); size += (m_UnsortedCats.size() * sizeof(unsigned int)); size += (m_Categories.size() * (sizeof(topmenu_category_t *) + sizeof(topmenu_category_t))); @@ -904,8 +904,8 @@ void TopMenu::SortCategoriesIfNeeded() void TopMenu::CreatePlayers(int max_clients) { m_max_clients = max_clients; - m_clients = (topmenu_player_t *)malloc(sizeof(topmenu_player_t) * (max_clients + 1)); - memset(m_clients, 0, sizeof(topmenu_player_t) * (max_clients + 1)); + m_clients = (topmenu_player_t *)malloc(sizeof(topmenu_player_t) * (ABSOLUTE_PLAYER_LIMIT + 1)); + memset(m_clients, 0, sizeof(topmenu_player_t) * (ABSOLUTE_PLAYER_LIMIT + 1)); } void TopMenu::TearDownClient(topmenu_player_t *player) @@ -1100,6 +1100,11 @@ unsigned int TopMenu::FindCategory(const char *name) return obj->object_id; } +void TopMenu::OnMaxPlayersChanged( int newvalue ) +{ + m_max_clients = newvalue; +} + int _SortObjectNamesDescending(const void *ptr1, const void *ptr2) { obj_by_name_t *obj1 = (obj_by_name_t *)ptr1; diff --git a/extensions/topmenus/TopMenu.h b/extensions/topmenus/TopMenu.h index 3177434d0..60989e29c 100644 --- a/extensions/topmenus/TopMenu.h +++ b/extensions/topmenus/TopMenu.h @@ -42,6 +42,8 @@ using namespace SourceHook; using namespace SourceMod; +#define ABSOLUTE_PLAYER_LIMIT 255 // not 256, so we can send the limit as a byte + struct config_category_t { int name; @@ -162,6 +164,7 @@ private: void OnClientDisconnected(int client); void OnServerActivated(int max_clients); bool OnIdentityRemoval(IdentityToken_t *owner); + void OnMaxPlayersChanged(int newvalue); private: config_root_t m_Config; /* Configuration from file */ topmenu_player_t *m_clients; /* Client array */ diff --git a/extensions/topmenus/TopMenuManager.cpp b/extensions/topmenus/TopMenuManager.cpp index fb55f1bf2..8b6114738 100644 --- a/extensions/topmenus/TopMenuManager.cpp +++ b/extensions/topmenus/TopMenuManager.cpp @@ -117,3 +117,13 @@ void TopMenuManager::DestroyTopMenu(ITopMenu *topmenu) delete pMenu; } + +void TopMenuManager::OnMaxPlayersChanged( int newvalue ) +{ + List::iterator iter; + + for (iter = m_TopMenus.begin(); iter != m_TopMenus.end(); iter++) + { + (*iter)->OnMaxPlayersChanged(newvalue); + } +} diff --git a/extensions/topmenus/TopMenuManager.h b/extensions/topmenus/TopMenuManager.h index 5e03b71e3..3225a7532 100644 --- a/extensions/topmenus/TopMenuManager.h +++ b/extensions/topmenus/TopMenuManager.h @@ -58,6 +58,7 @@ public: void OnClientDisconnected(int client); void OnServerActivated(int max_clients); void OnPluginUnloaded(IPlugin *plugin); + void OnMaxPlayersChanged(int newvalue); private: List m_TopMenus; }; diff --git a/public/IPlayerHelpers.h b/public/IPlayerHelpers.h index 344206f09..911bffdd9 100644 --- a/public/IPlayerHelpers.h +++ b/public/IPlayerHelpers.h @@ -41,7 +41,7 @@ #include #define SMINTERFACE_PLAYERMANAGER_NAME "IPlayerManager" -#define SMINTERFACE_PLAYERMANAGER_VERSION 7 +#define SMINTERFACE_PLAYERMANAGER_VERSION 8 struct edict_t; class IPlayerInfo; @@ -301,6 +301,15 @@ namespace SourceMod virtual void OnClientPostAdminCheck(int client) { } + + /** + * @brief Notifies the extension that the maxplayers value has changed + * + * @param newvalue New maxplayers value. + */ + virtual void OnMaxPlayersChanged(int newvalue) + { + } }; #define COMMAND_FILTER_ALIVE (1<<0) /**< Only allow alive players */