Compare commits

..

4 Commits

Author SHA1 Message Date
Roflmuffin
98661cd069 fix: public and silent triggers (finally) 2023-11-14 22:29:59 +10:00
Roflmuffin
86a5699b40 feat: re-add global command listener 2023-11-14 21:15:06 +10:00
Roflmuffin
414710d05c hotfix: wrap vfunc creation in try catch to prevent all vfuncs from erroring 2023-11-13 21:40:33 +10:00
Michael Wilson
b09c2b62c8 Improved Command Handling (#76) 2023-11-13 20:59:46 +10:00
18 changed files with 281 additions and 530 deletions

View File

@@ -75,8 +75,6 @@ SET(SOURCE_FILES
src/core/managers/entity_manager.h
src/core/managers/chat_manager.cpp
src/core/managers/chat_manager.h
src/core/managers/client_command_manager.cpp
src/core/managers/client_command_manager.h
src/core/managers/server_manager.cpp
src/core/managers/server_manager.h
src/scripting/natives/natives_server.cpp

View File

@@ -30,7 +30,7 @@ namespace CounterStrikeSharp.API.Core
}
}
public static IntPtr AddCommand(string name, string description, bool serveronly, int flags, InputArgument callback){
public static void AddCommand(string name, string description, bool serveronly, int flags, InputArgument callback){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(name);
@@ -41,7 +41,6 @@ namespace CounterStrikeSharp.API.Core
ScriptContext.GlobalScriptContext.SetIdentifier(0x807C6B9C);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (IntPtr)ScriptContext.GlobalScriptContext.GetResult(typeof(IntPtr));
}
}

View File

@@ -209,14 +209,10 @@ namespace CounterStrikeSharp.API.Core
{
var wrappedHandler = new Func<int, IntPtr, HookResult>((i, ptr) =>
{
if (i == -1)
{
return HookResult.Continue;
}
var caller = (i != -1) ? new CCSPlayerController(NativeAPI.GetEntityFromIndex(i + 1)) : null;
var entity = new CCSPlayerController(NativeAPI.GetEntityFromIndex(i + 1));
var command = new CommandInfo(ptr, entity);
return handler.Invoke(entity.IsValid ? entity : null, command);
var command = new CommandInfo(ptr, caller);
return handler.Invoke(caller, command);
});
var subscriber = new CallbackSubscriber(handler, wrappedHandler, () => { RemoveCommandListener(name, handler, mode); });

View File

@@ -26,7 +26,7 @@ public partial class CCSPlayerController
public void PrintToConsole(string message)
{
NativeAPI.PrintToConsole((int)EntityIndex.Value.Value, message);
NativeAPI.PrintToConsole((int)EntityIndex.Value.Value, $"{message}\n\0");
}
public void PrintToChat(string message)

View File

@@ -31,9 +31,15 @@ public partial class VirtualFunction
{
if (!_createdFunctions.TryGetValue(signature, out var function))
{
function = NativeAPI.CreateVirtualFunctionBySignature(IntPtr.Zero, Addresses.ServerPath, signature,
argumentTypes.Count(), (int)returnType, arguments);
_createdFunctions[signature] = function;
try
{
function = NativeAPI.CreateVirtualFunctionBySignature(IntPtr.Zero, Addresses.ServerPath, signature,
argumentTypes.Count(), (int)returnType, arguments);
_createdFunctions[signature] = function;
}
catch (Exception)
{
}
}
return function;

View File

@@ -354,7 +354,7 @@ namespace TestPlugin
[ConsoleCommand("cssharp_attribute", "This is a custom attribute event")]
public void OnCommand(CCSPlayerController? player, CommandInfo command)
{
Log("cssharp_attribute called!");
command.ReplyToCommand("cssharp_attribute called", true);
}
[ConsoleCommand("css_changelevel", "Changes map")]

View File

@@ -19,7 +19,6 @@
#include "memory_module.h"
#include "interfaces/cs2_interfaces.h"
#include "core/managers/entity_manager.h"
#include "core/managers/client_command_manager.h"
#include "core/managers/server_manager.h"
#include <public/game/server/iplayerinfo.h>
#include <public/entity2/entitysystem.h>
@@ -76,7 +75,6 @@ TimerSystem timerSystem;
ConCommandManager conCommandManager;
EntityManager entityManager;
ChatManager chatManager;
ClientCommandManager clientCommandManager;
ServerManager serverManager;
void Initialize() {

View File

@@ -48,7 +48,6 @@ class ChatCommands;
class HookManager;
class EntityManager;
class ChatManager;
class ClientCommandManager;
class ServerManager;
class CGameConfig;
@@ -92,7 +91,6 @@ extern EntityManager entityManager;
extern TimerSystem timerSystem;
extern ChatCommands chatCommands;
extern ChatManager chatManager;
extern ClientCommandManager clientCommandManager;
extern ServerManager serverManager;
extern HookManager hookManager;

View File

@@ -39,8 +39,7 @@ ChatManager::~ChatManager() {}
void ChatManager::OnAllInitialized()
{
m_pHostSay = reinterpret_cast<HostSay>(
modules::server->FindSignature(globals::gameConfig->GetSignature("Host_Say"))
);
modules::server->FindSignature(globals::gameConfig->GetSignature("Host_Say")));
if (m_pHostSay == nullptr) {
CSSHARP_CORE_ERROR("Failed to find signature for \'Host_Say\'");
@@ -57,71 +56,71 @@ void ChatManager::OnShutdown() {}
void DetourHostSay(CBaseEntity* pController, CCommand& args, bool teamonly, int unk1,
const char* unk2)
{
CCommand newArgs;
newArgs.Tokenize(args.Arg(1));
if (*args[1] == '/' || *args[1] == '!') {
globals::chatManager.OnSayCommandPost(pController, newArgs);
return;
}
m_pHostSay(pController, args, teamonly, unk1, unk2);
if (pController) {
auto pEvent = globals::gameEventManager->CreateEvent("player_chat", true);
if (pEvent) {
pEvent->SetBool("teamonly", teamonly);
pEvent->SetInt("userid", pController->GetEntityIndex().Get());
pEvent->SetInt("userid", pController->GetEntityIndex().Get() - 1);
pEvent->SetString("text", args[1]);
globals::gameEventManager->FireEvent(pEvent, true);
}
}
bool bSilent = *args[1] == '/';
bool bCommand = *args[1] == '!' || *args[1] == '/';
if (!bSilent) {
m_pHostSay(pController, args, teamonly, unk1, unk2);
}
if (bCommand)
{
char *pszMessage = (char *)(args.ArgS() + 2);
// Trailing slashes are only removed if Host_Say has been called.
if (bSilent)
pszMessage[V_strlen(pszMessage) - 1] = 0;
CCommand args;
args.Tokenize(pszMessage);
auto prefixedPhrase = std::string("css_") + args.Arg(0);
auto bValidWithPrefix = globals::conCommandManager.IsValidValveCommand(prefixedPhrase.c_str());
if (bValidWithPrefix) {
// Re-tokenize with a `css_` prefix if we have found that its a valid command.
args.Tokenize(("css_" + std::string(pszMessage)).c_str());
}
globals::chatManager.OnSayCommandPost(pController, args);
}
}
bool ChatManager::OnSayCommandPre(CBaseEntity* pController, CCommand& command) {
return false;
}
bool ChatManager::OnSayCommandPre(CBaseEntity* pController, CCommand& command) { return false; }
bool ChatManager::OnSayCommandPost(CBaseEntity* pController, CCommand& command)
void ChatManager::OnSayCommandPost(CBaseEntity* pController, CCommand& command)
{
const char* args = command.ArgS();
auto commandStr = command.Arg(0);
return InternalDispatch(pController, commandStr + 1, command);
return InternalDispatch(pController, commandStr, command);
}
bool ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhase,
void ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhase,
CCommand& fullCommand)
{
auto ppArgV = new const char*[fullCommand.ArgC()];
ppArgV[0] = strdup(szTriggerPhase);
for (int i = 1; i < fullCommand.ArgC(); i++) {
ppArgV[i] = fullCommand.Arg(i);
}
auto prefixedPhrase = std::string("css_") + szTriggerPhase;
auto command = globals::conCommandManager.FindCommand(prefixedPhrase.c_str());
if (command) {
ppArgV[0] = prefixedPhrase.c_str();
}
CCommand commandCopy(fullCommand.ArgC(), ppArgV);
if (pPlayerController == nullptr) {
auto result = globals::conCommandManager.InternalDispatch(CPlayerSlot(-1), &commandCopy);
delete[] ppArgV;
return result;
globals::conCommandManager.ExecuteCommandCallbacks(
fullCommand.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, CPlayerSlot(-1)),
fullCommand, HookMode::Pre);
return;
}
auto index = pPlayerController->GetEntityIndex().Get();
auto slot = CPlayerSlot(index - 1);
auto result = globals::conCommandManager.InternalDispatch(slot, &commandCopy);
delete[] ppArgV;
return result;
globals::conCommandManager.ExecuteCommandCallbacks(
fullCommand.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), fullCommand,
HookMode::Pre);
}
} // namespace counterstrikesharp

View File

@@ -53,10 +53,10 @@ class ChatManager : public GlobalClass
void OnShutdown() override;
bool OnSayCommandPre(CBaseEntity* pController, CCommand& args);
bool OnSayCommandPost(CBaseEntity* pController, CCommand& args);
void OnSayCommandPost(CBaseEntity* pController, CCommand& args);
private:
bool InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhrase,
void InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhrase,
CCommand& pFullCommand);
std::vector<ChatCommandInfo*> m_cmd_list;

View File

@@ -1,147 +0,0 @@
#include "core/managers/client_command_manager.h"
#include <public/eiface.h>
#include <algorithm>
#include "scripting/callback_manager.h"
#include "core/log.h"
namespace counterstrikesharp {
ClientCommandManager::ClientCommandManager() {}
ClientCommandManager::~ClientCommandManager() {}
void ClientCommandManager::OnAllInitialized()
{
m_global_cmd.callback_pre = globals::callbackManager.CreateCallback("OnClientCommandGlobalPre");
m_global_cmd.callback_post =
globals::callbackManager.CreateCallback("OnClientCommandGlobalPost");
}
void ClientCommandManager::OnShutdown() {}
bool ClientCommandManager::DispatchClientCommand(CPlayerSlot slot, const char* cmd,
const CCommand* args)
{
CSSHARP_CORE_TRACE("Dispatch client command {}", cmd);
auto* p_info = m_cmd_lookup[cmd];
bool result = false;
if (m_global_cmd.callback_pre->GetFunctionCount() > 0) {
m_global_cmd.callback_pre->ScriptContext().Reset();
m_global_cmd.callback_pre->ScriptContext().Push(slot.Get());
m_global_cmd.callback_pre->ScriptContext().Push(args);
for (auto fnMethodToCall : m_global_cmd.callback_pre->GetFunctions()) {
if (!fnMethodToCall)
continue;
fnMethodToCall(&m_global_cmd.callback_pre->ScriptContextStruct());
auto hookResult = m_global_cmd.callback_pre->ScriptContext().GetResult<HookResult>();
CSSHARP_CORE_TRACE("Received hook result from command callback {}:{}", cmd, hookResult);
if (hookResult >= HookResult::Stop) {
return true;
} else if (hookResult >= HookResult::Handled) {
result = true;
}
}
}
if (p_info && p_info->callback_pre) {
p_info->callback_pre->ScriptContext().Reset();
p_info->callback_pre->ScriptContext().Push(slot.Get());
p_info->callback_pre->ScriptContext().Push(args);
for (auto fnMethodToCall : p_info->callback_pre->GetFunctions()) {
if (!fnMethodToCall)
continue;
fnMethodToCall(&p_info->callback_pre->ScriptContextStruct());
auto hookResult = p_info->callback_pre->ScriptContext().GetResult<HookResult>();
CSSHARP_CORE_TRACE("Received hook result from command callback {}:{}", cmd, hookResult);
if (hookResult >= HookResult::Stop) {
return true;
} else if (hookResult >= HookResult::Handled) {
result = true;
}
}
}
if (m_global_cmd.callback_post->GetFunctionCount() > 0) {
m_global_cmd.callback_post->ScriptContext().Reset();
m_global_cmd.callback_post->ScriptContext().Push(slot.Get());
m_global_cmd.callback_post->ScriptContext().Push(args);
m_global_cmd.callback_post->Execute();
}
if (result && p_info && p_info->callback_post) {
p_info->callback_post->ScriptContext().Reset();
p_info->callback_post->ScriptContext().Push(slot.Get());
p_info->callback_post->ScriptContext().Push(args);
p_info->callback_post->Execute();
}
return result;
}
void ClientCommandManager::AddCommandListener(const char* cmd, CallbackT callback, bool bPost)
{
// Handle global command listeners that listen for every ClientCommand.
if (cmd == nullptr) {
if (bPost) {
m_global_cmd.callback_post->AddListener(callback);
return;
}
m_global_cmd.callback_pre->AddListener(callback);
return;
}
auto* p_info = m_cmd_lookup[std::string(cmd)];
if (!p_info) {
p_info = new ClientCommandInfo();
p_info->command = cmd;
p_info->callback_pre = globals::callbackManager.CreateCallback(cmd);
p_info->callback_post = globals::callbackManager.CreateCallback(cmd);
m_cmd_list.push_back(p_info);
m_cmd_lookup[cmd] = p_info;
}
if (bPost) {
p_info->callback_post->AddListener(callback);
} else {
p_info->callback_pre->AddListener(callback);
}
}
void ClientCommandManager::RemoveCommandListener(const char* cmd, CallbackT callback, bool bPost)
{
if (cmd == nullptr) {
if (bPost) {
m_global_cmd.callback_post->RemoveListener(callback);
return;
}
m_global_cmd.callback_pre->RemoveListener(callback);
return;
}
auto* p_info = m_cmd_lookup[std::string(cmd)];
if (!p_info) {
return;
}
if (bPost) {
p_info->callback_post->RemoveListener(callback);
} else {
p_info->callback_pre->RemoveListener(callback);
}
}
} // namespace counterstrikesharp

View File

@@ -1,45 +0,0 @@
#pragma once
#include <map>
#include <vector>
#include "core/globals.h"
#include "core/global_listener.h"
#include "scripting/script_engine.h"
#include <string>
#include "playerslot.h"
namespace counterstrikesharp {
class ScriptCallback;
class ClientCommandInfo {
friend class ClientCommandManager;
public:
ClientCommandInfo() {}
private:
std::string command;
ScriptCallback* callback_pre;
ScriptCallback* callback_post;
};
class ClientCommandManager : public GlobalClass {
public:
ClientCommandManager();
~ClientCommandManager();
void OnAllInitialized() override;
void OnShutdown() override;
bool DispatchClientCommand(CPlayerSlot slot, const char* cmd, const CCommand* args);
void AddCommandListener(const char* cmd, CallbackT callback, bool bPost);
void RemoveCommandListener(const char* cmd, CallbackT callback, bool bPost);
private:
std::vector<ClientCommandInfo*> m_cmd_list;
std::map<std::string, ClientCommandInfo*> m_cmd_lookup;
ClientCommandInfo m_global_cmd;
};
} // namespace counterstrikesharp

View File

@@ -157,307 +157,248 @@ CON_COMMAND(dump_schema, "dump schema symbols")
output << std::setw(2) << j << std::endl;
}
SH_DECL_HOOK2_void(ConCommandHandle, Dispatch, SH_NOATTRIB, false, const CCommandContext&,
const CCommand&);
SH_DECL_HOOK3_void(ICvar, DispatchConCommand, SH_NOATTRIB, 0, ConCommandHandle,
const CCommandContext&, const CCommand&);
void ConCommandInfo::HookChange(CallbackT cb, bool post)
ConCommandInfo::ConCommandInfo()
{
if (post) {
this->callback_post->AddListener(cb);
} else {
this->callback_pre->AddListener(cb);
}
callback_pre = globals::callbackManager.CreateCallback("");
callback_post = globals::callbackManager.CreateCallback("");
}
ConCommandInfo::~ConCommandInfo()
{
globals::callbackManager.ReleaseCallback(callback_pre);
globals::callbackManager.ReleaseCallback(callback_post);
}
ConCommandInfo::ConCommandInfo(bool bNoCallbacks) {
}
void ConCommandInfo::UnhookChange(CallbackT cb, bool post)
{
if (post) {
if (this->callback_post && this->callback_post->GetFunctionCount()) {
callback_post->RemoveListener(cb);
}
} else {
if (this->callback_pre && this->callback_pre->GetFunctionCount()) {
callback_pre->RemoveListener(cb);
}
}
}
ConCommandManager::ConCommandManager() : last_command_client(-1) {}
ConCommandManager::ConCommandManager() {}
ConCommandManager::~ConCommandManager() {}
void ConCommandManager::OnAllInitialized() {}
void ConCommandManager::OnAllInitialized()
{
SH_ADD_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand, false);
SH_ADD_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand_Post, true);
void ConCommandManager::OnShutdown() {}
m_global_cmd.callback_pre = globals::callbackManager.CreateCallback("OnClientCommandGlobalPre");
m_global_cmd.callback_post =
globals::callbackManager.CreateCallback("OnClientCommandGlobalPost");
}
void ConCommandManager::OnShutdown()
{
SH_REMOVE_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand, false);
SH_REMOVE_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand_Post, true);
globals::callbackManager.ReleaseCallback(m_global_cmd.callback_pre);
globals::callbackManager.ReleaseCallback(m_global_cmd.callback_post);
}
void CommandCallback(const CCommandContext& context, const CCommand& command)
{
bool rval = globals::conCommandManager.InternalDispatch(context.GetPlayerSlot(), &command);
if (rval) {
RETURN_META(MRES_SUPERCEDE);
}
// This is handled by the global hook
RETURN_META(MRES_SUPERCEDE);
}
void CommandCallback_Post(const CCommandContext& context, const CCommand& command)
void ConCommandManager::AddCommandListener(const char* name, CallbackT callback, HookMode mode)
{
bool rval = globals::conCommandManager.InternalDispatch_Post(context.GetPlayerSlot(), &command);
if (rval) {
RETURN_META(MRES_SUPERCEDE);
}
}
ConCommandInfo* ConCommandManager::AddOrFindCommand(const char* name, const char* description,
bool server_only, int flags)
{
ConCommandInfo* p_info = m_cmd_lookup[std::string(name)];
if (!p_info) {
CSSHARP_CORE_TRACE("[ConCommandManager] Could not find command in existing lookup {}",
name);
// auto found = std::find_if(m_cmd_list.begin(), m_cmd_list.end(),
// [&](ConCommandInfo* info) {
// return V_strcasecmp(info->command->GetName(), name) == 0;
// });
// if (found != m_cmd_list.end()) {
// return *found;
// }
p_info = new ConCommandInfo();
ConCommandHandle existingCommand = globals::cvars->FindCommand(name);
ConCommandRefAbstract pointerConCommand;
p_info->p_cmd = pointerConCommand;
if (!existingCommand.IsValid()) {
if (!description) {
description = "";
}
CSSHARP_CORE_TRACE("[ConCommandManager] Creating new command {}", name);
char* new_name = strdup(name);
char* new_desc = strdup(description);
CSSHARP_CORE_TRACE("[ConCommandManager] Creating new command {}, {}, {}, {}, {}",
(void*)&pointerConCommand, new_name, (void*)CommandCallback,
new_desc, flags);
auto conCommand =
new ConCommand(&pointerConCommand, new_name, CommandCallback, new_desc, flags);
CSSHARP_CORE_TRACE("[ConCommandManager] Creating callbacks for command {}", name);
p_info->command = conCommand;
p_info->callback_pre = globals::callbackManager.CreateCallback(name);
p_info->callback_post = globals::callbackManager.CreateCallback(name);
p_info->server_only = server_only;
CSSHARP_CORE_TRACE(
"[ConCommandManager] Adding hooks for command callback for command {}", name);
SH_ADD_HOOK(ConCommandHandle, Dispatch, &pointerConCommand.handle,
SH_STATIC(CommandCallback), false);
SH_ADD_HOOK(ConCommandHandle, Dispatch, &pointerConCommand.handle,
SH_STATIC(CommandCallback_Post), true);
CSSHARP_CORE_TRACE("[ConCommandManager] Adding command to internal lookup {}", name);
m_cmd_list.push_back(p_info);
m_cmd_lookup[name] = p_info;
if (name == nullptr) {
if (mode == HookMode::Pre) {
m_global_cmd.callback_pre->AddListener(callback);
} else {
p_info->callback_pre = globals::callbackManager.CreateCallback(name);
p_info->callback_post = globals::callbackManager.CreateCallback(name);
p_info->server_only = server_only;
m_global_cmd.callback_post->AddListener(callback);
}
return p_info;
return;
}
return p_info;
}
auto strName = std::string(name);
ConCommandInfo* pInfo = m_cmd_lookup[strName];
ConCommandInfo* ConCommandManager::AddCommand(const char* name, const char* description,
bool server_only, int flags, CallbackT callback)
{
ConCommandInfo* p_info = AddOrFindCommand(name, description, server_only, flags);
if (!p_info || !p_info->callback_pre) {
return nullptr;
if (!pInfo) {
pInfo = new ConCommandInfo();
m_cmd_lookup[strName] = pInfo;
ConCommandHandle hExistingCommand = globals::cvars->FindCommand(name);
if (hExistingCommand.IsValid()) {
pInfo->command = globals::cvars->GetCommand(hExistingCommand);
}
}
p_info->callback_pre->AddListener(callback);
return p_info;
if (mode == HookMode::Pre) {
pInfo->callback_pre->AddListener(callback);
} else {
pInfo->callback_post->AddListener(callback);
}
}
bool ConCommandManager::RemoveCommand(const char* name, CallbackT callback)
void ConCommandManager::RemoveCommandListener(const char* name, CallbackT callback, HookMode mode)
{
auto strName = std::string(strdup(name));
ConCommandInfo* p_info = m_cmd_lookup[strName];
if (!p_info)
if (name == nullptr) {
if (mode == HookMode::Pre) {
m_global_cmd.callback_pre->RemoveListener(callback);
} else {
m_global_cmd.callback_post->RemoveListener(callback);
}
return;
}
auto strName = std::string(name);
ConCommandInfo* pInfo = m_cmd_lookup[strName];
if (!pInfo) {
return;
}
if (mode == HookMode::Pre) {
pInfo->callback_pre->RemoveListener(callback);
} else {
pInfo->callback_post->RemoveListener(callback);
}
}
bool ConCommandManager::AddValveCommand(const char* name, const char* description, bool server_only,
int flags)
{
ConCommandHandle hExistingCommand = globals::cvars->FindCommand(name);
if (hExistingCommand.IsValid())
return false;
if (p_info->callback_pre && p_info->callback_pre->GetFunctionCount()) {
p_info->callback_pre->RemoveListener(callback);
ConCommandRefAbstract conCommandRefAbstract;
auto conCommand =
new ConCommand(&conCommandRefAbstract, strdup(name), CommandCallback, strdup(description), flags);
ConCommandInfo* pInfo = m_cmd_lookup[std::string(name)];
if (!pInfo) {
pInfo = new ConCommandInfo();
m_cmd_lookup[std::string(name)] = pInfo;
}
if (p_info->callback_post && p_info->callback_post->GetFunctionCount()) {
p_info->callback_post->RemoveListener(callback);
}
if (!p_info->callback_pre || p_info->callback_pre->GetFunctionCount() == 0) {
// It does not look like this actually removes the con command.
// You can still find with `find` command.
globals::cvars->UnregisterConCommand(p_info->p_cmd.handle);
}
pInfo->p_cmd = conCommandRefAbstract;
pInfo->command = conCommand;
pInfo->server_only = server_only;
return true;
}
ConCommandInfo* ConCommandManager::FindCommand(const char* name)
bool ConCommandManager::RemoveValveCommand(const char* name)
{
ConCommandInfo* p_info = m_cmd_lookup[std::string(name)];
auto hFoundCommand = globals::cvars->FindCommand(name);
if (p_info == nullptr) {
auto found = std::find_if(m_cmd_list.begin(), m_cmd_list.end(), [&](ConCommandInfo* info) {
return V_strcasecmp(info->command->GetName(), name) == 0;
});
if (found != m_cmd_list.end()) {
return *found;
}
ConCommandHandle p_cmd = globals::cvars->FindCommand(name);
if (!p_cmd.IsValid())
return nullptr;
p_info = new ConCommandInfo();
p_info->command = globals::cvars->GetCommand(p_cmd);
p_info->p_cmd = *p_info->command->GetRef();
p_info->callback_pre = globals::callbackManager.CreateCallback(name);
p_info->callback_post = globals::callbackManager.CreateCallback(name);
p_info->server_only = false;
m_cmd_list.push_back(p_info);
m_cmd_lookup[name] = p_info;
return p_info;
if (!hFoundCommand.IsValid()) {
return false;
}
return p_info;
globals::cvars->UnregisterConCommand(hFoundCommand);
auto pInfo = m_cmd_lookup[std::string(name)];
if (!pInfo) {
return true;
}
pInfo->command = nullptr;
return true;
}
int ConCommandManager::GetCommandClient() { return last_command_client; }
void ConCommandManager::SetCommandClient(int client) { last_command_client = client + 1; }
bool ConCommandManager::InternalDispatch(CPlayerSlot slot, const CCommand* args)
HookResult ConCommandManager::ExecuteCommandCallbacks(const char* name, const CCommandContext& ctx,
const CCommand& args, HookMode mode)
{
const char* cmd = args->Arg(0);
CSSHARP_CORE_TRACE("[ConCommandManager::ExecuteCommandCallbacks][{}]: {}",
mode == Pre ? "Pre" : "Post", name);
ConCommandInfo* pInfo = m_cmd_lookup[std::string(name)];
ConCommandInfo* p_info = m_cmd_lookup[cmd];
if (p_info == nullptr) {
if (slot.Get() == 0 && !globals::engine->IsDedicatedServer())
return false;
HookResult result = HookResult::Continue;
for (ConCommandInfo* cmdInfo : m_cmd_list) {
if ((cmdInfo != nullptr) && strcasecmp(cmdInfo->command->GetName(), cmd) == 0) {
p_info = cmdInfo;
auto globalCallback = mode == HookMode::Pre ? m_global_cmd.callback_pre : m_global_cmd.callback_post;
if (globalCallback->GetFunctionCount() > 0) {
globalCallback->ScriptContext().Reset();
globalCallback->ScriptContext().Push(ctx.GetPlayerSlot().Get());
globalCallback->ScriptContext().Push(&args);
for (auto fnMethodToCall : globalCallback->GetFunctions()) {
if (!fnMethodToCall)
continue;
fnMethodToCall(&globalCallback->ScriptContextStruct());
auto hookResult = globalCallback->ScriptContext().GetResult<HookResult>();
if (hookResult >= HookResult::Stop) {
if (mode == HookMode::Pre) {
return HookResult::Stop;
}
result = hookResult;
break;
}
if (hookResult >= HookResult::Handled) {
result = hookResult;
}
}
}
if (!p_info) {
return false;
if (!pInfo) {
return result;
}
int realClient = slot.Get();
auto pCallback = mode == HookMode::Pre ? pInfo->callback_pre : pInfo->callback_post;
bool result = false;
if (p_info->callback_pre) {
p_info->callback_pre->ScriptContext().Reset();
p_info->callback_pre->ScriptContext().SetArgument(0, realClient);
p_info->callback_pre->ScriptContext().SetArgument(1, args);
p_info->callback_pre->Execute(false);
pCallback->Reset();
pCallback->ScriptContext().Push(ctx.GetPlayerSlot().Get());
pCallback->ScriptContext().Push(&args);
result = p_info->callback_pre->ScriptContext().GetResult<bool>();
for (auto fnMethodToCall : pCallback->GetFunctions()) {
if (!fnMethodToCall)
continue;
fnMethodToCall(&pCallback->ScriptContextStruct());
auto thisResult = pCallback->ScriptContext().GetResult<HookResult>();
if (thisResult >= HookResult::Handled) {
return result;
} else if (thisResult > result) {
result = thisResult;
}
}
return result;
}
bool ConCommandManager::InternalDispatch_Post(CPlayerSlot slot, const CCommand* args)
void ConCommandManager::Hook_DispatchConCommand(ConCommandHandle cmd, const CCommandContext& ctx,
const CCommand& args)
{
const char* cmd = args->Arg(0);
const char* name = args.Arg(0);
ConCommandInfo* p_info = m_cmd_lookup[cmd];
if (p_info == nullptr) {
if (slot.Get() == 0 && !globals::engine->IsDedicatedServer())
return false;
CSSHARP_CORE_TRACE("[ConCommandManager::Hook_DispatchConCommand]: {}", name);
for (ConCommandInfo* cmdInfo : m_cmd_list) {
if ((cmdInfo != nullptr) && strcasecmp(cmdInfo->command->GetName(), cmd) == 0) {
p_info = cmdInfo;
continue;
}
}
auto result = ExecuteCommandCallbacks(name, ctx, args, HookMode::Pre);
if (result >= HookResult::Handled) {
RETURN_META(MRES_SUPERCEDE);
}
int realClient = slot.Get();
bool result = false;
if (p_info->callback_post) {
p_info->callback_post->ScriptContext().Reset();
p_info->callback_post->ScriptContext().SetArgument(0, realClient);
p_info->callback_post->ScriptContext().SetArgument(1, args);
p_info->callback_post->Execute(false);
result = p_info->callback_post->ScriptContext().GetResult<bool>();
}
return result;
}
bool ConCommandManager::DispatchClientCommand(CPlayerSlot slot, const char* cmd,
const CCommand* args)
void ConCommandManager::Hook_DispatchConCommand_Post(ConCommandHandle cmd,
const CCommandContext& ctx,
const CCommand& args)
{
ConCommandInfo* p_info = m_cmd_lookup[cmd];
if (p_info == nullptr) {
auto found =
std::find_if(m_cmd_list.begin(), m_cmd_list.end(), [&](const ConCommandInfo* info) {
return V_strcasecmp(info->command->GetName(), cmd) == 0;
});
if (found == m_cmd_list.end()) {
return false;
}
const char* name = args.Arg(0);
p_info = *found;
auto result = ExecuteCommandCallbacks(name, ctx, args, HookMode::Post);
if (result >= HookResult::Handled) {
RETURN_META(MRES_SUPERCEDE);
}
if (p_info->server_only)
return false;
bool result = false;
if (p_info->callback_pre) {
p_info->callback_pre->ScriptContext().Reset();
p_info->callback_pre->ScriptContext().Push(slot.Get());
p_info->callback_pre->ScriptContext().Push(args);
p_info->callback_pre->Execute();
result = true;
}
if (result) {
if (p_info->callback_post) {
p_info->callback_post->ScriptContext().Reset();
p_info->callback_post->ScriptContext().Push(slot.Get());
p_info->callback_post->ScriptContext().Push(args);
p_info->callback_post->Execute();
result = true;
}
}
return result;
}
bool ConCommandManager::IsValidValveCommand(const char* name) {
ConCommandHandle pCmd = globals::cvars->FindCommand(name);
return pCmd.IsValid();
}
} // namespace counterstrikesharp

View File

@@ -40,6 +40,16 @@
#include <string>
#include "playerslot.h"
struct CaseInsensitiveComparator {
bool operator()(const std::string& lhs, const std::string& rhs) const {
return std::lexicographical_compare(
lhs.begin(), lhs.end(),
rhs.begin(), rhs.end(),
[](char a, char b) { return std::tolower(a) < std::tolower(b); }
);
}
};
namespace counterstrikesharp {
class ScriptCallback;
@@ -47,7 +57,9 @@ class ConCommandInfo {
friend class ConCommandManager;
public:
ConCommandInfo() {}
ConCommandInfo();
ConCommandInfo(bool bNoCallbacks);
~ConCommandInfo();
public:
void HookChange(CallbackT callback, bool post);
@@ -64,39 +76,27 @@ private:
class ConCommandManager : public GlobalClass {
friend class ConCommandInfo;
friend void CommandCallback(const CCommand& command);
friend void CommandCallback_Post(const CCommand& command);
public:
ConCommandManager();
~ConCommandManager();
void OnAllInitialized() override;
void OnShutdown() override;
ConCommandInfo* AddOrFindCommand(const char* name,
const char* description,
bool server_only,
int flags);
bool DispatchClientCommand(CPlayerSlot slot, const char* cmd, const CCommand* args);
bool InternalDispatch(CPlayerSlot slot, const CCommand* args);
int GetCommandClient();
bool InternalDispatch_Post(CPlayerSlot slot, const CCommand* args);
public:
ConCommandInfo* AddCommand(
const char* name, const char* description, bool server_only, int flags, CallbackT callback);
bool RemoveCommand(const char* name, CallbackT callback);
ConCommandInfo* FindCommand(const char* name);
void AddCommandListener(const char* name, CallbackT callback, HookMode mode);
void RemoveCommandListener(const char* name, CallbackT callback, HookMode mode);
bool IsValidValveCommand(const char* name);
bool AddValveCommand(const char* name, const char* description, bool server_only, int flags);
bool RemoveValveCommand(const char* name);
void Hook_DispatchConCommand(ConCommandHandle cmd, const CCommandContext& ctx, const CCommand& args);
void Hook_DispatchConCommand_Post(ConCommandHandle cmd, const CCommandContext& ctx, const CCommand& args);
HookResult ExecuteCommandCallbacks(const char* name, const CCommandContext& ctx,
const CCommand& args, HookMode mode);
private:
void SetCommandClient(int client);
private:
int last_command_client;
std::vector<ConCommandInfo*> m_cmd_list;
std::map<std::string, ConCommandInfo*> m_cmd_lookup;
std::map<std::string, ConCommandInfo*, CaseInsensitiveComparator> m_cmd_lookup;
ConCommandInfo m_global_cmd = ConCommandInfo(true);
};
} // namespace counterstrikesharp

View File

@@ -30,7 +30,7 @@
*/
#include "core/managers/player_manager.h"
#include "core/managers/client_command_manager.h"
#include "core/managers/con_command_manager.h"
#include <public/eiface.h>
#include <public/inetchannelinfo.h>
@@ -287,8 +287,10 @@ void PlayerManager::OnClientCommand(CPlayerSlot slot, const CCommand& args) cons
const char* cmd = args.Arg(0);
bool response = globals::clientCommandManager.DispatchClientCommand(slot, cmd, &args);
if (response) {
auto result = globals::conCommandManager.ExecuteCommandCallbacks(
cmd, CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), args, HookMode::Pre);
if (result >= HookResult::Handled) {
RETURN_META(MRES_SUPERCEDE);
}
}

View File

@@ -16,7 +16,6 @@
#include <eiface.h>
#include "core/managers/client_command_manager.h"
#include "scripting/autonative.h"
#include "scripting/callback_manager.h"
#include "core/managers/con_command_manager.h"
@@ -26,7 +25,7 @@
namespace counterstrikesharp {
static ConCommandInfo* AddCommand(ScriptContext& script_context)
static void AddCommand(ScriptContext& script_context)
{
auto name = script_context.GetArgument<const char*>(0);
auto description = script_context.GetArgument<const char*>(1);
@@ -37,7 +36,8 @@ static ConCommandInfo* AddCommand(ScriptContext& script_context)
CSSHARP_CORE_TRACE("Adding command {}, {}, {}, {}, {}", name, description, server_only, flags,
(void*)callback);
return globals::conCommandManager.AddCommand(name, description, server_only, flags, callback);
globals::conCommandManager.AddValveCommand(name, description, server_only, flags);
globals::conCommandManager.AddCommandListener(name, callback, HookMode::Pre);
}
static void RemoveCommand(ScriptContext& script_context)
@@ -45,7 +45,8 @@ static void RemoveCommand(ScriptContext& script_context)
auto name = script_context.GetArgument<const char*>(0);
auto callback = script_context.GetArgument<CallbackT>(1);
globals::conCommandManager.RemoveCommand(name, callback);
globals::conCommandManager.RemoveCommandListener(name, callback, HookMode::Pre);
globals::conCommandManager.RemoveValveCommand(name);
}
static void AddCommandListener(ScriptContext& script_context)
@@ -54,7 +55,7 @@ static void AddCommandListener(ScriptContext& script_context)
auto callback = script_context.GetArgument<CallbackT>(1);
auto post = script_context.GetArgument<bool>(2);
globals::clientCommandManager.AddCommandListener(name, callback, post);
globals::conCommandManager.AddCommandListener(name, callback, post ? HookMode::Post : HookMode::Pre);
}
static void RemoveCommandListener(ScriptContext& script_context)
@@ -63,7 +64,7 @@ static void RemoveCommandListener(ScriptContext& script_context)
auto callback = script_context.GetArgument<CallbackT>(1);
auto post = script_context.GetArgument<bool>(2);
globals::clientCommandManager.RemoveCommandListener(name, callback, post);
globals::conCommandManager.RemoveCommandListener(name, callback, post ? HookMode::Post : HookMode::Pre);
}
static int CommandGetArgCount(ScriptContext& script_context)

View File

@@ -1,4 +1,4 @@
ADD_COMMAND: name:string,description:string,serverOnly:bool,flags:int,callback:callback -> pointer
ADD_COMMAND: name:string,description:string,serverOnly:bool,flags:int,callback:callback -> void
REMOVE_COMMAND: name:string,callback:callback -> void
ADD_COMMAND_LISTENER: cmd:string, callback:callback, post:bool -> void
REMOVE_COMMAND_LISTENER: cmd:string, callback:callback, post:bool -> void

View File

@@ -40,6 +40,11 @@ enum HookResult {
Stop = 4,
};
enum HookMode {
Pre = 0,
Post = 1,
};
inline uint32_t hash_string(const char *string) {
unsigned long result = 5381;