Merge pull request #360 from alliedmodders/cckv
Add support for listening to, blocking, changing, and faking ClientCommandKeyValues.
This commit is contained in:
commit
d30cab04c3
@ -2,7 +2,7 @@
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
|
||||
* Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
@ -46,6 +46,7 @@
|
||||
#include "ConsoleDetours.h"
|
||||
#include "logic_bridge.h"
|
||||
#include <sourcemod_version.h>
|
||||
#include "smn_keyvalues.h"
|
||||
|
||||
PlayerManager g_Players;
|
||||
bool g_OnMapStarted = false;
|
||||
@ -76,6 +77,9 @@ SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *)
|
||||
#endif
|
||||
SH_DECL_HOOK1_void(IServerGameClients, ClientSettingsChanged, SH_NOATTRIB, 0, edict_t *);
|
||||
#endif // SE_DOTA
|
||||
#if SOURCE_ENGINE >= SE_EYE && SOURCE_ENGINE != SE_DOTA
|
||||
SH_DECL_HOOK2_void(IServerGameClients, ClientCommandKeyValues, SH_NOATTRIB, 0, edict_t *, KeyValues *);
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
SH_DECL_HOOK0_void(IServerGameDLL, ServerActivate, SH_NOATTRIB, 0);
|
||||
@ -136,6 +140,7 @@ PlayerManager::PlayerManager()
|
||||
m_SourceTVUserId = -1;
|
||||
m_ReplayUserId = -1;
|
||||
|
||||
m_bInCCKVHook = false;
|
||||
m_bAuthstringValidation = true; // use steam auth by default
|
||||
|
||||
m_UserIdLookUp = new int[USHRT_MAX+1];
|
||||
@ -171,6 +176,10 @@ void PlayerManager::OnSourceModAllInitialized()
|
||||
SH_ADD_HOOK(IServerGameClients, ClientDisconnect, serverClients, SH_MEMBER(this, &PlayerManager::OnClientDisconnect), false);
|
||||
SH_ADD_HOOK(IServerGameClients, ClientDisconnect, serverClients, SH_MEMBER(this, &PlayerManager::OnClientDisconnect_Post), true);
|
||||
SH_ADD_HOOK(IServerGameClients, ClientCommand, serverClients, SH_MEMBER(this, &PlayerManager::OnClientCommand), false);
|
||||
#if SOURCE_ENGINE >= SE_EYE && SOURCE_ENGINE != SE_DOTA
|
||||
SH_ADD_HOOK(IServerGameClients, ClientCommandKeyValues, serverClients, SH_MEMBER(this, &PlayerManager::OnClientCommandKeyValues), false);
|
||||
SH_ADD_HOOK(IServerGameClients, ClientCommandKeyValues, serverClients, SH_MEMBER(this, &PlayerManager::OnClientCommandKeyValues_Post), true);
|
||||
#endif
|
||||
SH_ADD_HOOK(IServerGameClients, ClientSettingsChanged, serverClients, SH_MEMBER(this, &PlayerManager::OnClientSettingsChanged), true);
|
||||
SH_ADD_HOOK(IServerGameDLL, ServerActivate, gamedll, SH_MEMBER(this, &PlayerManager::OnServerActivate), true);
|
||||
#if SOURCE_ENGINE >= SE_LEFT4DEAD && SOURCE_ENGINE != SE_DOTA
|
||||
@ -190,6 +199,8 @@ void PlayerManager::OnSourceModAllInitialized()
|
||||
m_cldisconnect = forwardsys->CreateForward("OnClientDisconnect", ET_Ignore, 1, p2);
|
||||
m_cldisconnect_post = forwardsys->CreateForward("OnClientDisconnect_Post", ET_Ignore, 1, p2);
|
||||
m_clcommand = forwardsys->CreateForward("OnClientCommand", ET_Hook, 2, NULL, Param_Cell, Param_Cell);
|
||||
m_clcommandkv = forwardsys->CreateForward("OnClientCommandKeyValues", ET_Hook, 2, NULL, Param_Cell, Param_Cell);
|
||||
m_clcommandkv_post = forwardsys->CreateForward("OnClientCommandKeyValues_Post", ET_Ignore, 2, NULL, Param_Cell, Param_Cell);
|
||||
m_clinfochanged = forwardsys->CreateForward("OnClientSettingsChanged", ET_Ignore, 1, p2);
|
||||
m_clauth = forwardsys->CreateForward("OnClientAuthorized", ET_Ignore, 2, NULL, Param_Cell, Param_String);
|
||||
m_onActivate = forwardsys->CreateForward("OnServerLoad", ET_Ignore, 0, NULL);
|
||||
@ -218,6 +229,10 @@ void PlayerManager::OnSourceModShutdown()
|
||||
SH_REMOVE_HOOK(IServerGameClients, ClientDisconnect, serverClients, SH_MEMBER(this, &PlayerManager::OnClientDisconnect), false);
|
||||
SH_REMOVE_HOOK(IServerGameClients, ClientDisconnect, serverClients, SH_MEMBER(this, &PlayerManager::OnClientDisconnect_Post), true);
|
||||
SH_REMOVE_HOOK(IServerGameClients, ClientCommand, serverClients, SH_MEMBER(this, &PlayerManager::OnClientCommand), false);
|
||||
#if SOURCE_ENGINE >= SE_EYE && SOURCE_ENGINE != SE_DOTA
|
||||
SH_REMOVE_HOOK(IServerGameClients, ClientCommandKeyValues, serverClients, SH_MEMBER(this, &PlayerManager::OnClientCommandKeyValues), false);
|
||||
SH_REMOVE_HOOK(IServerGameClients, ClientCommandKeyValues, serverClients, SH_MEMBER(this, &PlayerManager::OnClientCommandKeyValues_Post), true);
|
||||
#endif
|
||||
SH_REMOVE_HOOK(IServerGameClients, ClientSettingsChanged, serverClients, SH_MEMBER(this, &PlayerManager::OnClientSettingsChanged), true);
|
||||
SH_REMOVE_HOOK(IServerGameDLL, ServerActivate, gamedll, SH_MEMBER(this, &PlayerManager::OnServerActivate), true);
|
||||
#if SOURCE_ENGINE >= SE_LEFT4DEAD && SOURCE_ENGINE != SE_DOTA
|
||||
@ -233,6 +248,8 @@ void PlayerManager::OnSourceModShutdown()
|
||||
forwardsys->ReleaseForward(m_cldisconnect);
|
||||
forwardsys->ReleaseForward(m_cldisconnect_post);
|
||||
forwardsys->ReleaseForward(m_clcommand);
|
||||
forwardsys->ReleaseForward(m_clcommandkv);
|
||||
forwardsys->ReleaseForward(m_clcommandkv_post);
|
||||
forwardsys->ReleaseForward(m_clinfochanged);
|
||||
forwardsys->ReleaseForward(m_clauth);
|
||||
forwardsys->ReleaseForward(m_onActivate);
|
||||
@ -1219,6 +1236,86 @@ void PlayerManager::OnClientCommand(edict_t *pEntity)
|
||||
}
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE >= SE_EYE && SOURCE_ENGINE != SE_DOTA
|
||||
static bool s_LastCCKVAllowed = true;
|
||||
|
||||
void PlayerManager::OnClientCommandKeyValues(edict_t *pEntity, KeyValues *pCommand)
|
||||
{
|
||||
int client = IndexOfEdict(pEntity);
|
||||
|
||||
cell_t res = Pl_Continue;
|
||||
CPlayer *pPlayer = &m_Players[client];
|
||||
|
||||
if (!pPlayer->IsInGame())
|
||||
{
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
KeyValueStack *pStk = new KeyValueStack;
|
||||
pStk->pBase = pCommand;
|
||||
pStk->pCurRoot.push(pStk->pBase);
|
||||
pStk->m_bDeleteOnDestroy = false;
|
||||
|
||||
Handle_t hndl = handlesys->CreateHandle(g_KeyValueType, pStk, g_pCoreIdent, g_pCoreIdent, NULL);
|
||||
|
||||
m_bInCCKVHook = true;
|
||||
m_clcommandkv->PushCell(client);
|
||||
m_clcommandkv->PushCell(hndl);
|
||||
m_clcommandkv->Execute(&res);
|
||||
m_bInCCKVHook = false;
|
||||
|
||||
HandleSecurity sec(g_pCoreIdent, g_pCoreIdent);
|
||||
|
||||
// Deletes pStk
|
||||
handlesys->FreeHandle(hndl, &sec);
|
||||
|
||||
if (res >= Pl_Handled)
|
||||
{
|
||||
s_LastCCKVAllowed = false;
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
|
||||
s_LastCCKVAllowed = true;
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
void PlayerManager::OnClientCommandKeyValues_Post(edict_t *pEntity, KeyValues *pCommand)
|
||||
{
|
||||
if (!s_LastCCKVAllowed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int client = IndexOfEdict(pEntity);
|
||||
|
||||
CPlayer *pPlayer = &m_Players[client];
|
||||
|
||||
if (!pPlayer->IsInGame())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
KeyValueStack *pStk = new KeyValueStack;
|
||||
pStk->pBase = pCommand;
|
||||
pStk->pCurRoot.push(pStk->pBase);
|
||||
pStk->m_bDeleteOnDestroy = false;
|
||||
|
||||
Handle_t hndl = handlesys->CreateHandle(g_KeyValueType, pStk, g_pCoreIdent, g_pCoreIdent, NULL);
|
||||
|
||||
m_bInCCKVHook = true;
|
||||
m_clcommandkv_post->PushCell(client);
|
||||
m_clcommandkv_post->PushCell(hndl);
|
||||
m_clcommandkv_post->Execute();
|
||||
m_bInCCKVHook = false;
|
||||
|
||||
HandleSecurity sec(g_pCoreIdent, g_pCoreIdent);
|
||||
|
||||
// Deletes pStk
|
||||
handlesys->FreeHandle(hndl, &sec);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
void PlayerManager::OnClientSettingsChanged(CEntityIndex index)
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
@ -181,6 +181,10 @@ public:
|
||||
void OnClientDisconnect_Post(edict_t *pEntity);
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
void OnClientCommand(edict_t *pEntity, const CCommand &args);
|
||||
#if SOURCE_ENGINE >= SE_EYE && SOURCE_ENGINE != SE_DOTA
|
||||
void OnClientCommandKeyValues(edict_t *pEntity, KeyValues *pCommand);
|
||||
void OnClientCommandKeyValues_Post(edict_t *pEntity, KeyValues *pCommand);
|
||||
#endif
|
||||
#else
|
||||
void OnClientCommand(edict_t *pEntity);
|
||||
#endif
|
||||
@ -224,6 +228,10 @@ public:
|
||||
unsigned int GetReplyTo();
|
||||
unsigned int SetReplyTo(unsigned int reply);
|
||||
void MaxPlayersChanged(int newvalue = -1);
|
||||
inline bool InClientCommandKeyValuesHook()
|
||||
{
|
||||
return m_bInCCKVHook;
|
||||
}
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
bool HandleConVarQuery(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue);
|
||||
#endif
|
||||
@ -242,6 +250,8 @@ private:
|
||||
IForward *m_cldisconnect_post;
|
||||
IForward *m_clputinserver;
|
||||
IForward *m_clcommand;
|
||||
IForward *m_clcommandkv;
|
||||
IForward *m_clcommandkv_post;
|
||||
IForward *m_clinfochanged;
|
||||
IForward *m_clauth;
|
||||
IForward *m_onActivate;
|
||||
@ -262,6 +272,7 @@ private:
|
||||
bool m_bIsReplayActive;
|
||||
int m_SourceTVUserId;
|
||||
int m_ReplayUserId;
|
||||
bool m_bInCCKVHook;
|
||||
};
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "ConCommandBaseIterator.h"
|
||||
#include "logic_bridge.h"
|
||||
#include <sm_namehashset.h>
|
||||
#include "smn_keyvalues.h"
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
|
||||
#include <netmessages.pb.h>
|
||||
@ -1292,6 +1293,50 @@ static cell_t ConVar_ReplicateToClient(IPluginContext *pContext, const cell_t *p
|
||||
return SendConVarValue(pContext, new_params);
|
||||
}
|
||||
|
||||
static cell_t FakeClientCommandKeyValues(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
#if SOURCE_ENGINE >= SE_EYE && SOURCE_ENGINE != SE_DOTA
|
||||
int client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer)
|
||||
{
|
||||
return pContext->ThrowNativeError("Client index %d is invalid", client);
|
||||
}
|
||||
else if (!pPlayer->IsConnected())
|
||||
{
|
||||
return pContext->ThrowNativeError("Client %d is not connected", client);
|
||||
}
|
||||
|
||||
Handle_t hndl = static_cast<Handle_t>(params[2]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
KeyValueStack *pStk;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr = handlesys->ReadHandle(hndl, g_KeyValueType, &sec, (void **) &pStk))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid key value handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
if (g_Players.InClientCommandKeyValuesHook())
|
||||
{
|
||||
SH_CALL(serverClients, &IServerGameClients::ClientCommandKeyValues)(pPlayer->GetEdict(), pStk->pBase);
|
||||
}
|
||||
else
|
||||
{
|
||||
serverClients->ClientCommandKeyValues(pPlayer->GetEdict(), pStk->pBase);
|
||||
}
|
||||
|
||||
return 1;
|
||||
#else
|
||||
return pContext->ThrowNativeError("FakeClientCommandKeyValues is not supported on this game.");
|
||||
#endif
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(consoleNatives)
|
||||
{
|
||||
{"CreateConVar", sm_CreateConVar},
|
||||
@ -1332,6 +1377,7 @@ REGISTER_NATIVES(consoleNatives)
|
||||
{"SendConVarValue", SendConVarValue},
|
||||
{"AddCommandListener", AddCommandListener},
|
||||
{"RemoveCommandListener", RemoveCommandListener},
|
||||
{"FakeClientCommandKeyValues", FakeClientCommandKeyValues},
|
||||
|
||||
// Transitional syntax support.
|
||||
{"ConVar.BoolValue.get", sm_GetConVarBool},
|
||||
|
@ -2,7 +2,7 @@
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
@ -29,6 +29,8 @@
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "smn_keyvalues.h"
|
||||
|
||||
#include "sourcemod.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "sm_stringutil.h"
|
||||
@ -39,12 +41,6 @@
|
||||
|
||||
HandleType_t g_KeyValueType;
|
||||
|
||||
struct KeyValueStack
|
||||
{
|
||||
KeyValues *pBase;
|
||||
CStack<KeyValues *> pCurRoot;
|
||||
};
|
||||
|
||||
class KeyValueNatives :
|
||||
public SMGlobalClass,
|
||||
public IHandleTypeDispatch
|
||||
@ -62,7 +58,11 @@ public:
|
||||
void OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
KeyValueStack *pStk = reinterpret_cast<KeyValueStack *>(object);
|
||||
pStk->pBase->deleteThis();
|
||||
if (pStk->m_bDeleteOnDestroy)
|
||||
{
|
||||
pStk->pBase->deleteThis();
|
||||
}
|
||||
|
||||
delete pStk;
|
||||
}
|
||||
int CalcKVSizeR(KeyValues *pv)
|
||||
|
51
core/smn_keyvalues.h
Normal file
51
core/smn_keyvalues.h
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_KVWRAPPER_H_
|
||||
#define _INCLUDE_SOURCEMOD_KVWRAPPER_H_
|
||||
|
||||
#include <IHandleSys.h>
|
||||
#include <sh_stack.h>
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
class KeyValues;
|
||||
|
||||
struct KeyValueStack
|
||||
{
|
||||
KeyValues *pBase;
|
||||
SourceHook::CStack<KeyValues *> pCurRoot;
|
||||
bool m_bDeleteOnDestroy = true;
|
||||
};
|
||||
|
||||
extern HandleType_t g_KeyValueType;;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_KVWRAPPER_H_
|
@ -138,10 +138,34 @@ forward void OnClientDisconnect_Post(client);
|
||||
*
|
||||
* @param client Client index.
|
||||
* @param args Number of arguments.
|
||||
* @noreturn
|
||||
* @return Plugin_Handled blocks the command from being sent,
|
||||
* and Plugin_Continue resumes normal functionality.
|
||||
*/
|
||||
forward Action:OnClientCommand(client, args);
|
||||
|
||||
/**
|
||||
* Called when a client is sending a KeyValues command.
|
||||
*
|
||||
* @param client Client index.
|
||||
* @param kv Editable KeyValues data to be sent as the command.
|
||||
* (This handle should not be stored and will be closed
|
||||
* after this forward completes.)
|
||||
* @return Plugin_Handled blocks the command from being sent,
|
||||
* and Plugin_Continue resumes normal functionality.
|
||||
*/
|
||||
forward Action OnClientCommandKeyValues(int client, KeyValues kv);
|
||||
|
||||
/**
|
||||
* Called after a client has sent a KeyValues command.
|
||||
*
|
||||
* @param client Client index.
|
||||
* @param kv KeyValues data sent as the command.
|
||||
* (This handle should not be stored and will be closed
|
||||
* after this forward completes.)
|
||||
* @noreturn
|
||||
*/
|
||||
forward void OnClientCommandKeyValues_Post(int client, KeyValues kv);
|
||||
|
||||
/**
|
||||
* Called whenever the client's settings are changed.
|
||||
*
|
||||
|
@ -190,6 +190,17 @@ native FakeClientCommand(client, const String:fmt[], any:...);
|
||||
*/
|
||||
native FakeClientCommandEx(client, const String:fmt[], any:...);
|
||||
|
||||
/**
|
||||
* Executes a KeyValues client command on the server without being networked.
|
||||
*
|
||||
* @param client Index of the client.
|
||||
* @param kv KeyValues data to be sent.
|
||||
* @noreturn
|
||||
* @error Invalid client index, client not connected,
|
||||
* or unsupported on current game.
|
||||
*/
|
||||
native void FakeClientCommandKeyValues(int client, KeyValues kv);
|
||||
|
||||
/**
|
||||
* Sends a message to the server console.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user