diff --git a/core/ConVarManager.cpp b/core/ConVarManager.cpp index d566f12f..374db25b 100644 --- a/core/ConVarManager.cpp +++ b/core/ConVarManager.cpp @@ -34,17 +34,10 @@ #include #include "logic_bridge.h" #include "sourcemod.h" +#include "provider.h" ConVarManager g_ConVarManager; -#if SOURCE_ENGINE == SE_DOTA -SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, CEntityIndex, EQueryCvarValueStatus, const char *, const char *); -SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, CEntityIndex, EQueryCvarValueStatus, const char *, const char *); -#elif SOURCE_ENGINE != SE_DARKMESSIAH -SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *); -SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *); -#endif - const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String}; typedef List ConVarList; NameHashSet convar_cache; @@ -83,7 +76,7 @@ public: ConVarReentrancyGuard *ConVarReentrancyGuard::chain = NULL; -ConVarManager::ConVarManager() : m_ConVarType(0), m_bIsDLLQueryHooked(false), m_bIsVSPQueryHooked(false) +ConVarManager::ConVarManager() : m_ConVarType(0) { } @@ -106,17 +99,6 @@ void ConVarManager::OnSourceModStartup(bool late) void ConVarManager::OnSourceModAllInitialized() { - /** - * Episode 2 has this function by default, but the older versions do not. - */ -#if SOURCE_ENGINE == SE_EPISODEONE - if (g_SMAPI->GetGameDLLVersion() >= 6) - { - SH_ADD_HOOK(IServerGameDLL, OnQueryCvarValueFinished, gamedll, SH_MEMBER(this, &ConVarManager::OnQueryCvarValueFinished), false); - m_bIsDLLQueryHooked = true; - } -#endif - g_Players.AddClientListener(this); scripts->AddPluginsListener(this); @@ -168,22 +150,6 @@ void ConVarManager::OnSourceModShutdown() } convar_cache.clear(); -#if SOURCE_ENGINE != SE_DARKMESSIAH - /* Unhook things */ - if (m_bIsDLLQueryHooked) - { - SH_REMOVE_HOOK(IServerGameDLL, OnQueryCvarValueFinished, gamedll, SH_MEMBER(this, &ConVarManager::OnQueryCvarValueFinished), false); - m_bIsDLLQueryHooked = false; - } - else if (m_bIsVSPQueryHooked) - { -#if SOURCE_ENGINE != SE_DOTA - SH_REMOVE_HOOK(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, SH_MEMBER(this, &ConVarManager::OnQueryCvarValueFinished), false); -#endif - m_bIsVSPQueryHooked = false; - } -#endif - g_Players.RemoveClientListener(this); /* Remove the 'convars' option from the 'sm' console command */ @@ -195,39 +161,6 @@ void ConVarManager::OnSourceModShutdown() handlesys->RemoveType(m_ConVarType, g_pCoreIdent); } -/** - * Orange Box will never use this. - */ -void ConVarManager::OnSourceModVSPReceived() -{ - /** - * Don't bother if the DLL is already hooked. - */ - if (m_bIsDLLQueryHooked) - { - return; - } - - /* For later MM:S versions, use the updated API, since it's cleaner. */ -#if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11 - int engine = g_SMAPI->GetSourceEngineBuild(); - if (engine == SOURCE_ENGINE_ORIGINAL || vsp_version < 2) - { - return; - } -#else - if (g_HL2.IsOriginalEngine() || vsp_version < 2) - { - return; - } -#endif - -#if SOURCE_ENGINE != SE_DARKMESSIAH && SOURCE_ENGINE != SE_DOTA - SH_ADD_HOOK(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, SH_MEMBER(this, &ConVarManager::OnQueryCvarValueFinished), false); - m_bIsVSPQueryHooked = true; -#endif -} - bool convar_cache_lookup(const char *name, ConVarInfo **pVar) { return convar_cache.retrieve(name, pVar); @@ -607,35 +540,13 @@ void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFuncti QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback, Handle_t hndl) { - QueryCvarCookie_t cookie = 0; - -#if SOURCE_ENGINE != SE_DARKMESSIAH - /* Call StartQueryCvarValue() in either the IVEngineServer or IServerPluginHelpers depending on situation */ - if (m_bIsDLLQueryHooked) - { -#if SOURCE_ENGINE == SE_DOTA - cookie = engine->StartQueryCvarValue(CEntityIndex(IndexOfEdict(pPlayer)), name); -#else - cookie = engine->StartQueryCvarValue(pPlayer, name); -#endif - } -#if SOURCE_ENGINE != SE_DOTA - else if (m_bIsVSPQueryHooked) - { - cookie = serverpluginhelpers->StartQueryCvarValue(pPlayer, name); - } -#endif - else - { - return InvalidQueryCvarCookie; - } + QueryCvarCookie_t cookie = sCoreProviderImpl.QueryClientConVar(IndexOfEdict(pPlayer), name); if (pCallback != NULL) { ConVarQuery query = { cookie, pCallback, (cell_t) hndl, IndexOfEdict(pPlayer) }; m_ConVarQueries.push_back(query); } -#endif return cookie; } @@ -716,23 +627,16 @@ void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue, float bool ConVarManager::IsQueryingSupported() { - return (m_bIsDLLQueryHooked || m_bIsVSPQueryHooked); + return sCoreProviderImpl.IsClientConVarQueryingSupported(); } #if SOURCE_ENGINE != SE_DARKMESSIAH -#if SOURCE_ENGINE == SE_DOTA -void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, CEntityIndex player, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue) -#else -void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue) -#endif // SE_DOTA +void ConVarManager::OnClientQueryFinished(QueryCvarCookie_t cookie, + int client, + EQueryCvarValueStatus result, + const char *cvarName, + const char *cvarValue) { -#if SOURCE_ENGINE == SE_CSGO - if (g_Players.HandleConVarQuery(cookie, pPlayer, result, cvarName, cvarValue)) - { - return; - } -#endif - IPluginFunction *pCallback = NULL; cell_t value = 0; List::iterator iter; @@ -753,11 +657,7 @@ void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t * cell_t ret; pCallback->PushCell(cookie); -#if SOURCE_ENGINE == SE_DOTA - pCallback->PushCell(player.Get()); -#else - pCallback->PushCell(IndexOfEdict(pPlayer)); -#endif + pCallback->PushCell(client); pCallback->PushCell(result); pCallback->PushString(cvarName); diff --git a/core/ConVarManager.h b/core/ConVarManager.h index eeb3128f..414a0c8a 100644 --- a/core/ConVarManager.h +++ b/core/ConVarManager.h @@ -44,11 +44,6 @@ #include "concmd_cleaner.h" #include "PlayerManager.h" -#if SOURCE_ENGINE == SE_DARKMESSIAH -class EQueryCvarValueStatus; -typedef int QueryCvarCookie_t; -#endif - using namespace SourceHook; class IConVarChangeListener @@ -100,7 +95,6 @@ public: // SMGlobalClass void OnSourceModStartup(bool late); void OnSourceModAllInitialized(); void OnSourceModShutdown(); - void OnSourceModVSPReceived(); public: // IHandleTypeDispatch void OnHandleDestroy(HandleType_t type, void *object); bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize); @@ -149,29 +143,24 @@ public: // Called via game hooks. void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue); +#if SOURCE_ENGINE != SE_DARKMESSIAH + void OnClientQueryFinished( + QueryCvarCookie_t cookie, + int client, + EQueryCvarValueStatus result, + const char *cvarName, + const char *cvarValue); +#endif private: /** * Adds a convar to a plugin's list. */ static void AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar); - - /** - * Callback for when StartQueryCvarValue() has finished. - */ -#if SOURCE_ENGINE == SE_DOTA - void OnQueryCvarValueFinished(QueryCvarCookie_t cookie, CEntityIndex player, EQueryCvarValueStatus result, - const char *cvarName, const char *cvarValue); -#elif SOURCE_ENGINE != SE_DARKMESSIAH - void OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, - const char *cvarName, const char *cvarValue); -#endif private: HandleType_t m_ConVarType; List m_ConVars; List m_ConVarQueries; - bool m_bIsDLLQueryHooked; - bool m_bIsVSPQueryHooked; }; extern ConVarManager g_ConVarManager; diff --git a/core/GameHooks.cpp b/core/GameHooks.cpp index 594f386d..0178d00f 100644 --- a/core/GameHooks.cpp +++ b/core/GameHooks.cpp @@ -34,35 +34,97 @@ SH_DECL_HOOK3_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar SH_DECL_HOOK2_void(ICvar, CallGlobalChangeCallback, SH_NOATTRIB, false, ConVar *, const char *); #endif +#if SOURCE_ENGINE == SE_DOTA +SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, CEntityIndex, EQueryCvarValueStatus, const char *, const char *); +SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, CEntityIndex, EQueryCvarValueStatus, const char *, const char *); +#elif SOURCE_ENGINE != SE_DARKMESSIAH +SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *); +SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *); +#endif + GameHooks::GameHooks() + : query_hook_mode_(QueryHookMode::Unavailable) { } void GameHooks::Start() { + // Hook ICvar::CallGlobalChangeCallbacks. #if SOURCE_ENGINE >= SE_ORANGEBOX - SH_ADD_HOOK(ICvar, CallGlobalChangeCallbacks, icvar, SH_STATIC(OnConVarChanged), false); + hooks_ += SH_ADD_HOOK(ICvar, CallGlobalChangeCallbacks, icvar, SH_STATIC(OnConVarChanged), false); #else - SH_ADD_HOOK(ICvar, CallGlobalChangeCallback, icvar, SH_STATIC(OnConVarChanged), false); + hooks_ += SH_ADD_HOOK(ICvar, CallGlobalChangeCallback, icvar, SH_STATIC(OnConVarChanged), false); +#endif + + // Episode 2 has this function by default, but the older versions do not. +#if SOURCE_ENGINE == SE_EPISODEONE + if (g_SMAPI->GetGameDLLVersion() >= 6) { + hooks_ += SH_ADD_HOOK(IServerGameDLL, OnQueryCvarValueFinished, gamedll, SH_MEMBER(this, &GameHooks::OnQueryCvarValueFinished), false); + query_hook_mode_ = QueryHookMode::DLL; + } +#endif +} + +void GameHooks::OnVSPReceived() +{ + if (query_hook_mode_ != QueryHookMode::Unavailable) + return; + + // For later MM:S versions, use the updated API, since it's cleaner. +#if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11 + if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL || vsp_version < 2) + return; +#else + if (g_HL2.IsOriginalEngine() || vsp_version < 2) + return; +#endif + +#if SOURCE_ENGINE != SE_DARKMESSIAH && SOURCE_ENGINE != SE_DOTA + hooks_ += SH_ADD_HOOK(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, SH_MEMBER(this, &GameHooks::OnQueryCvarValueFinished), false); + query_hook_mode_ = QueryHookMode::VSP; #endif } void GameHooks::Shutdown() { -#if SOURCE_ENGINE >= SE_ORANGEBOX - SH_REMOVE_HOOK(ICvar, CallGlobalChangeCallbacks, icvar, SH_STATIC(OnConVarChanged), false); -#else - SH_REMOVE_HOOK(ICvar, CallGlobalChangeCallback, icvar, SH_STATIC(OnConVarChanged), false); -#endif + for (size_t i = 0; i < hooks_.length(); i++) + SH_REMOVE_HOOK_ID(hooks_[i]); + hooks_.clear(); + + query_hook_mode_ = QueryHookMode::Unavailable; } #if SOURCE_ENGINE >= SE_ORANGEBOX void GameHooks::OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue) -{ #else void GameHooks::OnConVarChanged(ConVar *pConVar, const char *oldValue) +#endif { +#if SOURCE_ENGINE < SE_ORANGEBOX float flOldValue = atof(oldValue); #endif g_ConVarManager.OnConVarChanged(pConVar, oldValue, flOldValue); } + +#if SOURCE_ENGINE != SE_DARKMESSIAH +# if SOURCE_ENGINE == SE_DOTA +void GameHooks::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, CEntityIndex player, EQueryCvarValueStatus result, + const char *cvarName, const char *cvarValue) +# else +void GameHooks::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, + const char *cvarName, const char *cvarValue) +# endif +{ +# if SOURCE_ENGINE == SE_DOTA + int client = player.Get(); +# else + int client = IndexOfEdict(pPlayer); +# endif + +# if SOURCE_ENGINE == SE_CSGO + if (g_Players.HandleConVarQuery(cookie, client, result, cvarName, cvarValue)) + return; +# endif + g_ConVarManager.OnClientQueryFinished(cookie, client, result, cvarName, cvarValue); +} +#endif diff --git a/core/GameHooks.h b/core/GameHooks.h index 537a5912..1ddab8cd 100644 --- a/core/GameHooks.h +++ b/core/GameHooks.h @@ -27,10 +27,23 @@ #ifndef _INCLUDE_SOURCEMOD_PROVIDER_GAME_HOOKS_H_ #define _INCLUDE_SOURCEMOD_PROVIDER_GAME_HOOKS_H_ +// Needed for CEntityIndex, edict_t, etc. +#include +#include +#include +#include +#include + class ConVar; namespace SourceMod { +enum class QueryHookMode { + Unavailable, + DLL, + VSP +}; + class GameHooks { public: @@ -38,13 +51,41 @@ public: void Start(); void Shutdown(); + void OnVSPReceived(); + QueryHookMode GetQueryHookMode() const { + return query_hook_mode_; + } + +private: // Static callback that Valve's ConVar object executes when the convar's value changes. #if SOURCE_ENGINE >= SE_ORANGEBOX static void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue); #else static void OnConVarChanged(ConVar *pConVar, const char *oldValue); #endif + + // Callback for when StartQueryCvarValue() has finished. +#if SOURCE_ENGINE == SE_DOTA + void OnQueryCvarValueFinished(QueryCvarCookie_t cookie, CEntityIndex player, EQueryCvarValueStatus result, + const char *cvarName, const char *cvarValue); +#elif SOURCE_ENGINE != SE_DARKMESSIAH + void OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, + const char *cvarName, const char *cvarValue); +#endif + +private: + class HookList : public ke::Vector + { + public: + HookList &operator += (int hook_id) { + this->append(hook_id); + return *this; + } + }; + HookList hooks_; + + QueryHookMode query_hook_mode_; }; } // namespace SourceMod diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp index ea40a8c6..c00d2364 100644 --- a/core/PlayerManager.cpp +++ b/core/PlayerManager.cpp @@ -2010,7 +2010,7 @@ void CmdMaxplayersCallback() } #if SOURCE_ENGINE == SE_CSGO -bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue) +bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue) { for (int i = 1; i <= m_maxClients; i++) { diff --git a/core/PlayerManager.h b/core/PlayerManager.h index 4c16c79d..068d69f9 100644 --- a/core/PlayerManager.h +++ b/core/PlayerManager.h @@ -233,7 +233,7 @@ public: return m_bInCCKVHook; } #if SOURCE_ENGINE == SE_CSGO - bool HandleConVarQuery(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue); + bool HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue); #endif private: #if SOURCE_ENGINE == SE_DOTA diff --git a/core/logic/intercom.h b/core/logic/intercom.h index dc4d9d3c..7bb0a1a5 100644 --- a/core/logic/intercom.h +++ b/core/logic/intercom.h @@ -52,7 +52,7 @@ using namespace SourceHook; * Add 1 to the RHS of this expression to bump the intercom file * This is to prevent mismatching core/logic binaries */ -#define SM_LOGIC_MAGIC (0x0F47C0DE - 47) +#define SM_LOGIC_MAGIC (0x0F47C0DE - 48) #if defined SM_LOGIC # define IVEngineClass IVEngineServer @@ -315,6 +315,10 @@ public: virtual void ConPrint(const char *message) = 0; virtual void ConsolePrintVa(const char *fmt, va_list ap) = 0; + // Game engine helper functions. + virtual bool IsClientConVarQueryingSupported() = 0; + virtual int QueryClientConVar(int client, const char *cvar) = 0; + // Metamod:Source functions. virtual int LoadMMSPlugin(const char *file, bool *ok, char *error, size_t maxlength) = 0; virtual void UnloadMMSPlugin(int id) = 0; diff --git a/core/logic_bridge.cpp b/core/logic_bridge.cpp index f2b6b2bb..17279c5a 100644 --- a/core/logic_bridge.cpp +++ b/core/logic_bridge.cpp @@ -637,6 +637,32 @@ void CoreProviderImpl::UnloadMMSPlugin(int id) g_pMMPlugins->Unload(id, true, ignore, sizeof(ignore)); } +bool CoreProviderImpl::IsClientConVarQueryingSupported() +{ + return hooks_.GetQueryHookMode() != QueryHookMode::Unavailable; +} + +int CoreProviderImpl::QueryClientConVar(int client, const char *cvar) +{ +#if SOURCE_ENGINE != SE_DARKMESSIAH + switch (hooks_.GetQueryHookMode()) { + case QueryHookMode::DLL: +# if SOURCE_ENGINE == SE_DOTA + return ::engine->StartQueryCvarValue(CEntityIndex(client), cvar); +# else + return ::engine->StartQueryCvarValue(PEntityOfEntIndex(client), cvar); +# endif + case QueryHookMode::VSP: +# if SOURCE_ENGINE != SE_DOTA + return serverpluginhelpers->StartQueryCvarValue(PEntityOfEntIndex(client), cvar); +# endif + default: + return InvalidQueryCvarCookie; + } +#endif + return -1; +} + void CoreProviderImpl::InitializeBridge() { ::serverGlobals.universalTime = g_pUniversalTime; @@ -721,6 +747,11 @@ void CoreProviderImpl::InitializeHooks() hooks_.Start(); } +void CoreProviderImpl::OnVSPReceived() +{ + hooks_.OnVSPReceived(); +} + void CoreProviderImpl::ShutdownHooks() { hooks_.Shutdown(); diff --git a/core/provider.h b/core/provider.h index bfdda45a..6f469fbb 100644 --- a/core/provider.h +++ b/core/provider.h @@ -43,6 +43,7 @@ public: void InitializeHooks(); void ShutdownHooks(); + void OnVSPReceived(); // Provider implementation. ConVar *FindConVar(const char *name) override; @@ -61,6 +62,8 @@ public: void ConsolePrintVa(const char *fmt, va_list ap) override; int LoadMMSPlugin(const char *file, bool *ok, char *error, size_t maxlength) override; void UnloadMMSPlugin(int id) override; + int QueryClientConVar(int client, const char *cvar) override; + bool IsClientConVarQueryingSupported() override; private: ke::Ref logic_; diff --git a/core/sm_globals.h b/core/sm_globals.h index 03e749cf..e462028b 100644 --- a/core/sm_globals.h +++ b/core/sm_globals.h @@ -159,13 +159,6 @@ public: { } - /** - * @brief Called when SourceMod receives a pointer to IServerPluginCallbacks from SourceMM - */ - virtual void OnSourceModVSPReceived() - { - } - /** * @brief Called once all MM:S plugins are loaded. */ diff --git a/core/sourcemm_api.cpp b/core/sourcemm_api.cpp index f8f88a1d..e395539f 100644 --- a/core/sourcemm_api.cpp +++ b/core/sourcemm_api.cpp @@ -36,6 +36,7 @@ #include "compat_wrappers.h" #include "logic_bridge.h" #include +#include "provider.h" SourceMod_Core g_SourceMod_Core; IVEngineServer *engine = NULL; @@ -207,12 +208,7 @@ void SourceMod_Core::OnVSPListening(IServerPluginCallbacks *iface) #endif /* Notify! */ - SMGlobalClass *pBase = SMGlobalClass::head; - while (pBase) - { - pBase->OnSourceModVSPReceived(); - pBase = pBase->m_pGlobalClassNext; - } + sCoreProviderImpl.OnVSPReceived(); } #if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11 diff --git a/public/compat_wrappers.h b/public/compat_wrappers.h index abc3b86f..a7b2d005 100644 --- a/public/compat_wrappers.h +++ b/public/compat_wrappers.h @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. @@ -34,6 +34,11 @@ #include +#if SOURCE_ENGINE == SE_DARKMESSIAH +class EQueryCvarValueStatus; +typedef int QueryCvarCookie_t; +#endif + #if SOURCE_ENGINE >= SE_ORANGEBOX #define CONVAR_REGISTER(object) ConVar_Register(0, object)