Add FileTransfer Hooks to SDKTools (r=Drifter).

This commit is contained in:
Kyle Sanderson 2015-09-12 13:01:33 -07:00
parent 8777d0d0da
commit 274e7bd329
5 changed files with 261 additions and 33 deletions

View File

@ -44,6 +44,7 @@
#include <ISDKTools.h>
#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:

View File

@ -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;

View File

@ -37,18 +37,32 @@
#include "utlvector.h"
#include <shareddefs.h>
#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<IBaseFileSystem> *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<INetChannel *>(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)

View File

@ -36,6 +36,8 @@ class CUserCmd;
#include "extension.h"
#include <am-vector.h>
#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<CVTableHook *> m_runUserCmdHooks;
ke::Vector<CVTableHook *> m_netChannelHooks;
INetChannel *m_pActiveNetChannel;
};
extern CHookManager g_Hooks;

View File

@ -57,3 +57,23 @@
* FEATURECAP_PLAYERRUNCMD_11PARAMS.
*/
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[]);