diff --git a/extensions/sdktools/extension.cpp b/extensions/sdktools/extension.cpp index 2b21b7ab..60016827 100644 --- a/extensions/sdktools/extension.cpp +++ b/extensions/sdktools/extension.cpp @@ -44,6 +44,7 @@ #include #include "clientnatives.h" #include "teamnatives.h" +#include "filesystem.h" /** * @file extension.cpp * @brief Implements SDK Tools extension code. @@ -68,6 +69,7 @@ IVoiceServer *voiceserver = NULL; IPlayerInfoManager *playerinfomngr = NULL; ICvar *icvar = NULL; IServer *iserver = NULL; +IBaseFileSystem *basefilesystem = NULL; CGlobalVars *gpGlobals; ISoundEmitterSystemBase *soundemitterbase = NULL; @@ -258,6 +260,7 @@ bool SDKTools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool GET_V_IFACE_ANY(GetEngineFactory, voiceserver, IVoiceServer, INTERFACEVERSION_VOICESERVER); GET_V_IFACE_ANY(GetServerFactory, playerinfomngr, IPlayerInfoManager, INTERFACEVERSION_PLAYERINFOMANAGER); GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetFileSystemFactory, basefilesystem, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION); #if SOURCE_ENGINE >= SE_ORANGEBOX GET_V_IFACE_ANY(GetServerFactory, servertools, IServerTools, VSERVERTOOLS_INTERFACE_VERSION); @@ -466,9 +469,10 @@ const char *SDKTools::GetExtensionDateString() return SOURCEMOD_BUILD_TIME; } -void SDKTools::OnClientPutInServer(int client) +bool SDKTools::InterceptClientConnect(int client, char *error, size_t maxlength) { - g_Hooks.OnClientPutInServer(client); + g_Hooks.OnClientConnect(client); + return true; } #if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_CSGO @@ -487,6 +491,11 @@ void SDKTools::OnSendClientCommand(edict_t *pPlayer, const char *szFormat) } #endif +void SDKTools::OnClientPutInServer(int client) +{ + g_Hooks.OnClientPutInServer(client); +} + class SDKTools_API : public ISDKTools { public: diff --git a/extensions/sdktools/extension.h b/extensions/sdktools/extension.h index 99c1b574..4a8ea6d5 100644 --- a/extensions/sdktools/extension.h +++ b/extensions/sdktools/extension.h @@ -89,6 +89,7 @@ public: public: //IConCommandBaseAccessor bool RegisterConCommandBase(ConCommandBase *pVar); public: //IClientListner + bool InterceptClientConnect(int client, char *error, size_t maxlength); void OnClientPutInServer(int client); void OnClientDisconnecting(int client); public: // IVoiceServer @@ -129,6 +130,7 @@ extern IVoiceServer *voiceserver; extern IPlayerInfoManager *playerinfomngr; extern ICvar *icvar; extern IServer *iserver; +extern IBaseFileSystem *basefilesystem; extern CGlobalVars *gpGlobals; #if SOURCE_ENGINE >= SE_ORANGEBOX extern IServerTools *servertools; diff --git a/extensions/sdktools/hooks.cpp b/extensions/sdktools/hooks.cpp index 2405716e..67e549fc 100644 --- a/extensions/sdktools/hooks.cpp +++ b/extensions/sdktools/hooks.cpp @@ -37,18 +37,32 @@ #include "utlvector.h" #include #include "usercmd.h" +#include "filesystem.h" #define FEATURECAP_PLAYERRUNCMD_11PARAMS "SDKTools PlayerRunCmd 11Params" CHookManager g_Hooks; static bool PRCH_enabled = false; static bool PRCH_used = false; +static bool FILE_used = false; SH_DECL_MANUALHOOK2_void(PlayerRunCmdHook, 0, 0, 0, CUserCmd *, IMoveHelper *); +SH_DECL_HOOK2(IBaseFileSystem, FileExists, SH_NOATTRIB, 0, bool, const char*, const char *); +#if SOURCE_ENGINE >= SE_ALIENSWARM || SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2 +SH_DECL_HOOK3(INetChannel, SendFile, SH_NOATTRIB, 0, bool, const char *, unsigned int, bool); +#else +SH_DECL_HOOK2(INetChannel, SendFile, SH_NOATTRIB, 0, bool, const char *, unsigned int); +#endif +SH_DECL_HOOK2_void(INetChannel, ProcessPacket, SH_NOATTRIB, 0, struct netpacket_s *, bool); + +SourceHook::CallClass *basefilesystemPatch = NULL; CHookManager::CHookManager() { m_usercmdsFwd = NULL; + m_netFileSendFwd = NULL; + m_netFileReceiveFwd = NULL; + m_pActiveNetChannel = NULL; } void CHookManager::Initialize() @@ -65,6 +79,13 @@ void CHookManager::Initialize() PRCH_enabled = false; } + SH_ADD_HOOK(IBaseFileSystem, FileExists, basefilesystem, SH_MEMBER(this, &CHookManager::FileExists), false); + + basefilesystemPatch = SH_GET_CALLCLASS(basefilesystem); + + m_netFileSendFwd = forwards->CreateForward("OnFileSend", ET_Event, 2, NULL, Param_Cell, Param_String); + m_netFileReceiveFwd = forwards->CreateForward("OnFileReceive", ET_Event, 2, NULL, Param_Cell, Param_String); + plsys->AddPluginsListener(this); sharesys->AddCapabilityProvider(myself, this, FEATURECAP_PLAYERRUNCMD_11PARAMS); @@ -85,16 +106,25 @@ void CHookManager::Initialize() void CHookManager::Shutdown() { forwards->ReleaseForward(m_usercmdsFwd); + forwards->ReleaseForward(m_netFileSendFwd); + forwards->ReleaseForward(m_netFileReceiveFwd); plsys->RemovePluginsListener(this); sharesys->DropCapabilityProvider(myself, this, FEATURECAP_PLAYERRUNCMD_11PARAMS); } +void CHookManager::OnClientConnect(int client) +{ + NetChannelHook(client); +} + void CHookManager::OnClientPutInServer(int client) { - if (!PRCH_enabled) - return; + PlayerRunCmdHook(client); +} +void CHookManager::PlayerRunCmdHook(int client) +{ if (!PRCH_used) return; @@ -193,47 +223,194 @@ void CHookManager::PlayerRunCmd(CUserCmd *ucmd, IMoveHelper *moveHelper) RETURN_META(MRES_IGNORED); } +void CHookManager::NetChannelHook(int client) +{ + if (!FILE_used) + return; + + INetChannel *pNetChannel = static_cast(engine->GetPlayerNetInfo(client)); + if (pNetChannel == NULL) + { + return; + } + + /* Normal NetChannel Hooks. */ + { + CVTableHook hook(pNetChannel); + size_t iter; + for (iter = 0; iter < m_netChannelHooks.length(); ++iter) + { + /* We can technically skip 2; even technical-ier, this could be a bool; but Valve. */ + if (hook == m_netChannelHooks[iter]) + { + break; + } + } + + if (iter == m_netChannelHooks.length()) + { + int hookid = SH_ADD_VPHOOK(INetChannel, SendFile, pNetChannel, SH_MEMBER(this, &CHookManager::SendFile), false); + hook.SetHookID(hookid); + m_netChannelHooks.append(new CVTableHook(hook)); + + hookid = SH_ADD_VPHOOK(INetChannel, ProcessPacket, pNetChannel, SH_MEMBER(this, &CHookManager::ProcessPacket), false); + hook.SetHookID(hookid); + m_netChannelHooks.append(new CVTableHook(hook)); + + hookid = SH_ADD_VPHOOK(INetChannel, ProcessPacket, pNetChannel, SH_MEMBER(this, &CHookManager::ProcessPacket_Post), true); + hook.SetHookID(hookid); + m_netChannelHooks.append(new CVTableHook(hook)); + } + } +} + +void CHookManager::ProcessPacket(struct netpacket_s *packet, bool bHasHeader) +{ + if (m_netFileReceiveFwd->GetFunctionCount() == 0) + { + RETURN_META(MRES_IGNORED); + } + + m_pActiveNetChannel = META_IFACEPTR(INetChannel); + RETURN_META(MRES_IGNORED); +} + +bool CHookManager::FileExists(const char *filename, const char *pathID) +{ + if (m_pActiveNetChannel == NULL || m_netFileReceiveFwd->GetFunctionCount() == 0) + { + RETURN_META_VALUE(MRES_IGNORED, false); + } + + bool ret = SH_CALL(basefilesystemPatch, &IBaseFileSystem::FileExists)(filename, pathID); + if (ret == true) /* If the File Exists, the engine historically bails out. */ + { + RETURN_META_VALUE(MRES_IGNORED, false); + } + + int userid = 0; + IClient *pClient = (IClient *)m_pActiveNetChannel->GetMsgHandler(); + if (pClient != NULL) + { + userid = pClient->GetUserID(); + } + + cell_t res = Pl_Continue; + m_netFileReceiveFwd->PushCell(playerhelpers->GetClientOfUserId(userid)); + m_netFileReceiveFwd->PushString(filename); + m_netFileReceiveFwd->Execute(&res); + + if (res != Pl_Continue) + { + RETURN_META_VALUE(MRES_SUPERCEDE, true); + } + + RETURN_META_VALUE(MRES_IGNORED, false); +} + +void CHookManager::ProcessPacket_Post(struct netpacket_s* packet, bool bHasHeader) +{ + m_pActiveNetChannel = NULL; + RETURN_META(MRES_IGNORED); +} + +#if SOURCE_ENGINE >= SE_ALIENSWARM || SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2 +bool CHookManager::SendFile(const char *filename, unsigned int transferID, bool isReplayDemo) +#else +bool CHookManager::SendFile(const char *filename, unsigned int transferID) +#endif +{ + if (m_netFileSendFwd->GetFunctionCount() == 0) + { + RETURN_META_VALUE(MRES_IGNORED, false); + } + + INetChannel *pNetChannel = META_IFACEPTR(INetChannel); + if (pNetChannel == NULL) + { + RETURN_META_VALUE(MRES_IGNORED, false); + } + + int userid = 0; + IClient *pClient = (IClient *)m_pActiveNetChannel->GetMsgHandler(); + if (pClient != NULL) + { + userid = pClient->GetUserID(); + } + + cell_t res = Pl_Continue; + m_netFileSendFwd->PushCell(playerhelpers->GetClientOfUserId(userid)); + m_netFileSendFwd->PushString(filename); + m_netFileSendFwd->Execute(&res); + + if (res != Pl_Continue) + { + /* Mimic the Engine. */ +#if SOURCE_ENGINE >= SE_ALIENSWARM || SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2 + pNetChannel->DenyFile(filename, transferID, isReplayDemo); +#else + pNetChannel->DenyFile(filename, transferID); +#endif + RETURN_META_VALUE(MRES_SUPERCEDE, false); + } + + RETURN_META_VALUE(MRES_IGNORED, false); +} + void CHookManager::OnPluginLoaded(IPlugin *plugin) { - if (!PRCH_enabled) - return; - - if (PRCH_used) - return; - - if (!m_usercmdsFwd->GetFunctionCount()) - return; - - PRCH_used = true; - - int MaxClients = playerhelpers->GetMaxClients(); - for (int i = 1; i <= MaxClients; i++) + if (PRCH_enabled && !PRCH_used && m_usercmdsFwd->GetFunctionCount()) { - if (playerhelpers->GetGamePlayer(i)->IsInGame()) + PRCH_used = true; + + int MaxClients = playerhelpers->GetMaxClients(); + for (int i = 1; i <= MaxClients; i++) { - OnClientPutInServer(i); + if (playerhelpers->GetGamePlayer(i)->IsInGame()) + { + OnClientPutInServer(i); + } + } + } + + if (!FILE_used && (m_netFileSendFwd->GetFunctionCount() || m_netFileReceiveFwd->GetFunctionCount())) + { + FILE_used = true; + + int MaxClients = playerhelpers->GetMaxClients(); + for (int i = 1; i <= MaxClients; i++) + { + if (playerhelpers->GetGamePlayer(i)->IsConnected()) + { + OnClientConnect(i); + } } } } void CHookManager::OnPluginUnloaded(IPlugin *plugin) { - if (!PRCH_enabled) - return; - - if (!PRCH_used) - return; - - if (m_usercmdsFwd->GetFunctionCount()) - return; - - for (size_t i = 0; i < m_runUserCmdHooks.length(); ++i) + if (PRCH_used && !m_usercmdsFwd->GetFunctionCount()) { - delete m_runUserCmdHooks[i]; + for (size_t i = 0; i < m_runUserCmdHooks.length(); ++i) + { + delete m_runUserCmdHooks[i]; + } + + m_runUserCmdHooks.clear(); + PRCH_used = false; } - m_runUserCmdHooks.clear(); - PRCH_used = false; + if (FILE_used && !m_netFileSendFwd->GetFunctionCount() && !m_netFileReceiveFwd->GetFunctionCount()) + { + for (size_t i = 0; i < m_netChannelHooks.length(); ++i) + { + delete m_netChannelHooks[i]; + } + + m_netChannelHooks.clear(); + FILE_used = false; + } } FeatureStatus CHookManager::GetFeatureStatus(FeatureType type, const char *name) diff --git a/extensions/sdktools/hooks.h b/extensions/sdktools/hooks.h index 28e1f286..50c3cd6a 100644 --- a/extensions/sdktools/hooks.h +++ b/extensions/sdktools/hooks.h @@ -36,6 +36,8 @@ class CUserCmd; #include "extension.h" #include +#include "inetchannel.h" +#include "iclient.h" #include "vtable_hook_helper.h" class CHookManager : IPluginsListener, IFeatureProvider @@ -44,17 +46,35 @@ public: CHookManager(); void Initialize(); void Shutdown(); + void OnClientConnect(int client); void OnClientPutInServer(int client); void PlayerRunCmd(CUserCmd *ucmd, IMoveHelper *moveHelper); +public: /* NetChannel/Related Hooks */ + bool FileExists(const char *filename, const char *pathID); +#if SOURCE_ENGINE >= SE_ALIENSWARM || SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2 + bool SendFile(const char *filename, unsigned int transferID, bool isReplayDemo); +#else + bool SendFile(const char *filename, unsigned int transferID); +#endif + void ProcessPacket(struct netpacket_s *packet, bool bHasHeader); + void ProcessPacket_Post(struct netpacket_s *packet, bool bHasHeader); public: //IPluginsListener void OnPluginLoaded(IPlugin *plugin); void OnPluginUnloaded(IPlugin *plugin); public: //IFeatureProvider virtual FeatureStatus GetFeatureStatus(FeatureType type, const char *name); +private: + void PlayerRunCmdHook(int client); + void NetChannelHook(int client); + private: IForward *m_usercmdsFwd; + IForward *m_netFileSendFwd; + IForward *m_netFileReceiveFwd; ke::Vector m_runUserCmdHooks; + ke::Vector m_netChannelHooks; + INetChannel *m_pActiveNetChannel; }; extern CHookManager g_Hooks; diff --git a/plugins/include/sdktools_hooks.inc b/plugins/include/sdktools_hooks.inc index 26abcbff..53315e2e 100644 --- a/plugins/include/sdktools_hooks.inc +++ b/plugins/include/sdktools_hooks.inc @@ -56,4 +56,24 @@ * @note To see if all 11 params are avaliable, use FeatureType_Capability and * FEATURECAP_PLAYERRUNCMD_11PARAMS. */ -forward Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon, &subtype, &cmdnum, &tickcount, &seed, mouse[2]); \ No newline at end of file +forward Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon, &subtype, &cmdnum, &tickcount, &seed, mouse[2]); + +/** + * @brief Called when a client requests a file from the server. + * + * @param client Client index. + * @param sFile Requested file path. + * + * @return Plugin_Handled to block the transfer, Plugin_Continue to let it proceed. + */ +forward Action:OnFileSend(client, const String:sFile[]); + +/** + * @brief Called when a client sends a file to the server. + * + * @param client Client index. + * @param sFile Requested file path. + * + * @return Plugin_Handled to block the transfer, Plugin_Continue to let it proceed. + */ +forward Action:OnFileReceive(client, const String:sFile[]);