diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp index c48a38ff3..67a51b5a8 100644 --- a/core/PlayerManager.cpp +++ b/core/PlayerManager.cpp @@ -251,6 +251,8 @@ void PlayerManager::OnServerActivate(edict_t *pEdictList, int edictCount, int cl memset(m_AuthQueue, 0, sizeof(unsigned int) * (ABSOLUTE_PLAYER_LIMIT + 1)); g_NumPlayersToAuth = &m_AuthQueue[0]; + + g_PluginSys.SyncMaxClients(clientMax); } g_Extensions.CallOnCoreMapStart(pEdictList, edictCount, clientMax); m_onActivate->Execute(NULL); diff --git a/core/smn_core.cpp b/core/smn_core.cpp index 84692bca7..e75ae368e 100644 --- a/core/smn_core.cpp +++ b/core/smn_core.cpp @@ -622,6 +622,11 @@ static cell_t FindPluginByNumber(IPluginContext *pContext, const cell_t *params) return pPlugin->GetMyHandle(); } +static cell_t VerifyCoreVersion(IPluginContext *pContext, const cell_t *params) +{ + return 4; +} + REGISTER_NATIVES(coreNatives) { {"AutoExecConfig", AutoExecConfig}, @@ -645,6 +650,7 @@ REGISTER_NATIVES(coreNatives) {"LogToFileEx", LogToFileEx}, {"GetExtensionFileStatus", GetExtensionFileStatus}, {"FindPluginByNumber", FindPluginByNumber}, + {"VerifyCoreVersion", VerifyCoreVersion}, {NULL, NULL}, }; diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index 6fb2ca907..091a71c8f 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -68,6 +68,7 @@ CPlugin::CPlugin(const char *file) m_LibraryMissing = false; m_bGotAllLoaded = false; m_pPhrases = g_Translator.CreatePhraseCollection(); + m_MaxClientsVar = NULL; } CPlugin::~CPlugin() @@ -218,7 +219,7 @@ void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...) } } -void CPlugin::UpdateInfo() +bool CPlugin::UpdateInfo() { /* Now grab the info */ uint32_t idx; @@ -265,7 +266,7 @@ void CPlugin::UpdateInfo() }; __version_info *info; cell_t local_addr; - const char *pDate, *pTime; + const char *pDate, *pTime, *pFileVers; pDate = ""; pTime = ""; @@ -278,11 +279,34 @@ void CPlugin::UpdateInfo() base->LocalToString(info->time, (char **)&pTime); UTIL_Format(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime); } + if (m_FileVersion > 4) + { + base->LocalToString(info->filevers, (char **)&pFileVers); + SetErrorState(Plugin_Failed, "Newer SourceMod required (%s or higher)", pFileVers); + return false; + } } else { m_FileVersion = 0; } + + if ((err = base->FindPubvarByName("MaxClients", &idx)) == SP_ERROR_NONE) + { + base->GetPubvarByIndex(idx, &m_MaxClientsVar); + } + + return true; +} + +void CPlugin::SyncMaxClients(int max_clients) +{ + if (m_MaxClientsVar == NULL) + { + return; + } + + *m_MaxClientsVar->offs = max_clients; } void CPlugin::Call_OnPluginStart() @@ -294,6 +318,8 @@ void CPlugin::Call_OnPluginStart() m_status = Plugin_Running; + SyncMaxClients(g_Players.MaxClients()); + cell_t result; IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginStart"); if (!pFunction) @@ -427,6 +453,10 @@ PluginType CPlugin::GetType() const sm_plugininfo_t *CPlugin::GetPublicInfo() { + if (GetStatus() >= Plugin_Created) + { + return NULL; + } return &m_info; } @@ -959,11 +989,18 @@ LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool de "Unable to load plugin (error %d: %s)", err, g_pSourcePawn2->GetErrorString(err)); + pPlugin->m_status = Plugin_BadLoad; } else { - pPlugin->UpdateInfo(); - pPlugin->m_status = Plugin_Created; + if (pPlugin->UpdateInfo()) + { + pPlugin->m_status = Plugin_Created; + } + else + { + UTIL_Format(error, maxlength, "%s", pPlugin->m_errormsg); + } } } @@ -1052,10 +1089,13 @@ void CPluginManager::LoadAutoPlugin(const char *plugin) if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure) { g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error); - pl->SetErrorState(Plugin_Failed, "%s", error); + pl->SetErrorState( + pl->GetStatus() == Plugin_BadLoad ? Plugin_BadLoad : Plugin_Failed, + "%s", + error); } - if (res == LoadRes_Successful) + if (res == LoadRes_Successful || res == LoadRes_Failure) { AddPlugin(pl); } @@ -1458,7 +1498,7 @@ bool CPluginManager::UnloadPlugin(IPlugin *plugin) } IPluginContext *pContext = plugin->GetBaseContext(); - if (pContext->IsInExec()) + if (pContext != NULL && pContext->IsInExec()) { char buffer[255]; UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename()); @@ -1925,14 +1965,21 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &c { len += UTIL_Format(buffer, sizeof(buffer), " %02d", id); } - len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); - if (IS_STR_FILLED(info->version)) + if (pl->GetStatus() < Plugin_Created) { - len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version); + len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); + if (IS_STR_FILLED(info->version)) + { + len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version); + } + if (IS_STR_FILLED(info->author)) + { + UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author); + } } - if (IS_STR_FILLED(info->author)) + else { - UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author); + UTIL_Format(&buffer[len], sizeof(buffer)-len, " %s", pl->m_filename); } g_RootMenu.ConsolePrint("%s", buffer); } @@ -2025,8 +2072,16 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &c } char name[PLATFORM_MAX_PATH]; - const sm_plugininfo_t *info = pl->GetPublicInfo(); - strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); + + if (pl->GetStatus() < Plugin_Created) + { + const sm_plugininfo_t *info = pl->GetPublicInfo(); + UTIL_Format(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); + } + else + { + UTIL_Format(name, sizeof(name), "%s", pl->GetFilename()); + } if (UnloadPlugin(pl)) { @@ -2172,12 +2227,15 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &c else { g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg); - g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")", - info->name ? info->name : "", - info->version ? info->version : ""); - if (IS_STR_FILLED(info->url)) + if (pl->GetStatus() < Plugin_Created) { - g_RootMenu.ConsolePrint(" File URL: %s", info->url); + g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")", + info->name ? info->name : "", + info->version ? info->version : ""); + if (IS_STR_FILLED(info->url)) + { + g_RootMenu.ConsolePrint(" File URL: %s", info->url); + } } } @@ -2551,3 +2609,18 @@ CPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg) return pl; } + +void CPluginManager::OnSourceModMaxPlayersChanged(int newvalue) +{ + SyncMaxClients(newvalue); +} + +void CPluginManager::SyncMaxClients(int max_clients) +{ + List::iterator iter; + + for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++) + { + (*iter)->SyncMaxClients(max_clients); + } +} diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index ea662b393..aa29ffaae 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -234,8 +234,9 @@ public: m_Libraries.push_back(name); } void LibraryActions(bool dropping); + void SyncMaxClients(int max_clients); protected: - void UpdateInfo(); + bool UpdateInfo(); void SetTimeStamp(time_t t); void DependencyDropped(CPlugin *pOwner); private: @@ -261,6 +262,7 @@ private: char m_DateTime[256]; IPluginRuntime *m_pRuntime; IPluginContext *m_pContext; + sp_pubvar_t *m_MaxClientsVar; }; class CPluginManager : @@ -307,6 +309,7 @@ public: //IPluginManager public: //SMGlobalClass void OnSourceModAllInitialized(); void OnSourceModShutdown(); + void OnSourceModMaxPlayersChanged(int newvalue); public: //IHandleTypeDispatch void OnHandleDestroy(HandleType_t type, void *object); bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize); @@ -390,6 +393,8 @@ public: void UnloadAll(); CPlugin *FindPluginByConsoleArg(const char *arg); + + void SyncMaxClients(int max_clients); private: LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength); diff --git a/plugins/basechat.sp b/plugins/basechat.sp index 3d968352b..307e1f3a2 100644 --- a/plugins/basechat.sp +++ b/plugins/basechat.sp @@ -237,8 +237,7 @@ public Action:Command_SmHsay(client, args) decl String:nameBuf[MAX_NAME_LENGTH]; - new maxClients = GetMaxClients(); - for (new i = 1; i <= maxClients; i++) + for (new i = 1; i <= MaxClients; i++) { if (!IsClientConnected(i) || IsFakeClient(i)) { @@ -270,7 +269,6 @@ public Action:Command_SmTsay(client, args) GetClientName(client, name, sizeof(name)); new color = FindColor(colorStr); - new maxClients = GetMaxClients(); new String:nameBuf[MAX_NAME_LENGTH]; if (color == -1) @@ -279,7 +277,7 @@ public Action:Command_SmTsay(client, args) len = 0; } - for (new i = 1; i <= maxClients; i++) + for (new i = 1; i <= MaxClients; i++) { if (!IsClientConnected(i) || IsFakeClient(i)) { @@ -407,11 +405,9 @@ FindColor(String:color[]) SendChatToAll(client, String:message[]) { - new maxClients; new String:nameBuf[MAX_NAME_LENGTH]; - maxClients = GetMaxClients(); - for (new i = 1; i <= maxClients; i++) + for (new i = 1; i <= MaxClients; i++) { if (!IsClientConnected(i) || IsFakeClient(i)) { @@ -433,9 +429,8 @@ SendChatToAll(client, String:message[]) DisplayCenterTextToAll(client, String:message[]) { new String:nameBuf[MAX_NAME_LENGTH]; - new maxClients = GetMaxClients(); - for (new i = 1; i < maxClients; i++) + for (new i = 1; i < MaxClients; i++) { if (!IsClientConnected(i) || IsFakeClient(i)) { @@ -448,9 +443,7 @@ DisplayCenterTextToAll(client, String:message[]) SendChatToAdmins(String:name[], String:message[]) { - new iMaxClients = GetMaxClients(); - - for (new i = 1; i <= iMaxClients; i++) + for (new i = 1; i <= MaxClients; i++) { if (IsClientInGame(i)) { @@ -500,8 +493,7 @@ SendPanelToAll(String:name[], String:message[]) SetPanelCurrentKey(mSayPanel, 10); DrawPanelItem(mSayPanel, "Exit", ITEMDRAW_CONTROL); - new MaxClients = GetMaxClients(); - for(new i = 1; i < MaxClients; i++) + for(new i = 1; i <= MaxClients; i++) { if(IsClientInGame(i) && !IsFakeClient(i)) { diff --git a/plugins/include/clients.inc b/plugins/include/clients.inc index 01ba4b7b9..880eb0b0b 100644 --- a/plugins/include/clients.inc +++ b/plugins/include/clients.inc @@ -45,9 +45,19 @@ enum NetFlow NetFlow_Both, /**< Both values added together */ }; -#define MAXPLAYERS 64 /**< Maximum number of players that can be in server */ +/** + * MAXPLAYERS is not the same as MaxClients. + * MAXPLAYERS is a hardcoded value as an upper limit. MaxClients changes based on the server. + * + * Both GetMaxClients() and MaxClients are only available once the map is loaded, and should + * not be used in OnPluginStart(). + */ + +#define MAXPLAYERS 64 /**< Maximum number of players SourceMod supports */ #define MAX_NAME_LENGTH 32 /**< Maximum buffer required to store a client name */ +public const MaxClients; /**< Maximum number of players the server supports (dynamic) */ + /** * Called on client connection. * @@ -169,10 +179,16 @@ forward OnClientPostAdminFilter(client); forward OnClientPostAdminCheck(client); /** + * This function will be deprecated in a future release. Use the MaxClients variable instead. + * * Returns the maximum number of clients allowed on the server. This may * return 0 if called before OnMapStart(), and thus should not be called * in OnPluginStart(). * + * You should not globally cache the value to GetMaxClients() because it can change from + * SourceTV or TF2's arena mode. Use the "MaxClients" dynamic variable documented at the + * top of this file. + * * @return Maximum number of clients allowed. */ native GetMaxClients(); diff --git a/plugins/include/core.inc b/plugins/include/core.inc index 5ad7bcb5e..787cddb20 100644 --- a/plugins/include/core.inc +++ b/plugins/include/core.inc @@ -37,7 +37,9 @@ #include -#define SOURCEMOD_PLUGINAPI_VERSION 3 +/** If this gets changed, you need to update Core's check. */ +#define SOURCEMOD_PLUGINAPI_VERSION 4 + struct PlVers { version, @@ -136,6 +138,25 @@ struct SharedPlugin public Float:NULL_VECTOR[3]; /**< Pass this into certain functions to act as a C++ NULL */ public const String:NULL_STRING[1]; /**< pass this into certain functions to act as a C++ NULL */ +/** + * Horrible compatibility shim. + */ +public Extension:__ext_core = +{ + name = "Core", + file = "core", + autoload = 0, + required = 0, +}; + +native VerifyCoreVersion(); + +public __ext_core_SetNTVOptional() +{ + VerifyCoreVersion(); +} + + #define AUTOLOAD_EXTENSIONS #define REQUIRE_EXTENSIONS #define REQUIRE_PLUGIN