Add chat callback (#265)

This commit is contained in:
Edo 2022-06-14 20:43:19 +02:00 committed by GitHub
parent 0a852c6431
commit 144ba7efd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 181 additions and 32 deletions

View File

@ -13,22 +13,33 @@ namespace Components
std::mutex Chat::AccessMutex; std::mutex Chat::AccessMutex;
std::unordered_set<std::uint64_t> Chat::MuteList; std::unordered_set<std::uint64_t> Chat::MuteList;
const char* Chat::EvaluateSay(char* text, Game::gentity_t* player) bool Chat::CanAddCallback = true;
std::vector<Scripting::Function> Chat::SayCallbacks;
const char* Chat::EvaluateSay(char* text, Game::gentity_t* player, int mode)
{ {
Chat::SendChat = true; SendChat = true;
const auto _0 = gsl::finally([]
{
CanAddCallback = true;
});
// Prevent callbacks from adding a new callback (would make the vector iterator invalid)
CanAddCallback = false;
if (text[1] == '/') if (text[1] == '/')
{ {
Chat::SendChat = false; SendChat = false;
text[1] = text[0]; text[1] = text[0];
++text; ++text;
} }
std::unique_lock<std::mutex> lock(Chat::AccessMutex); std::unique_lock lock(AccessMutex);
if (Chat::MuteList.contains(Game::svs_clients[player->s.number].steamID)) if (MuteList.contains(Game::svs_clients[player->s.number].steamID))
{ {
lock.unlock(); lock.unlock();
Chat::SendChat = false; SendChat = false;
Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE, Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE,
Utils::String::VA("%c \"You are muted\"", 0x65)); Utils::String::VA("%c \"You are muted\"", 0x65));
} }
@ -39,9 +50,17 @@ namespace Components
lock.unlock(); lock.unlock();
} }
if (Chat::sv_disableChat.get<bool>()) for (const auto& callback : SayCallbacks)
{ {
Chat::SendChat = false; if (!ChatCallback(player, callback.getPos(), (text + 1), mode))
{
SendChat = false;
}
}
if (sv_disableChat.get<bool>())
{
SendChat = false;
Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE, Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE,
Utils::String::VA("%c \"Chat is disabled\"", 0x65)); Utils::String::VA("%c \"Chat is disabled\"", 0x65));
} }
@ -59,21 +78,22 @@ namespace Components
{ {
__asm __asm
{ {
mov eax, [esp + 100h + 10h] mov eax, [esp + 0x100 + 0x10]
push eax push eax
pushad pushad
push [esp + 100h + 28h] push [esp + 0x100 + 0x30] // mode
push eax push [esp + 0x100 + 0x2C] // player
call Chat::EvaluateSay push eax // text
add esp, 8h call EvaluateSay
add esp, 0xC
mov [esp + 20h], eax mov [esp + 0x20], eax
popad popad
pop eax pop eax
mov [esp + 100h + 10h], eax mov [esp + 0x100 + 0x10], eax
jmp PlayerName::CleanStrStub jmp PlayerName::CleanStrStub
} }
@ -87,7 +107,7 @@ namespace Components
push eax push eax
xor eax, eax xor eax, eax
mov al, Chat::SendChat mov al, SendChat
test al, al test al, al
jnz return jnz return
@ -224,11 +244,11 @@ namespace Components
void Chat::MuteClient(const Game::client_t* client) void Chat::MuteClient(const Game::client_t* client)
{ {
std::unique_lock<std::mutex> lock(Chat::AccessMutex); std::unique_lock lock(AccessMutex);
if (!Chat::MuteList.contains(client->steamID)) if (!MuteList.contains(client->steamID))
{ {
Chat::MuteList.insert(client->steamID); MuteList.insert(client->steamID);
lock.unlock(); lock.unlock();
Logger::Print("{} was muted\n", client->name); Logger::Print("{} was muted\n", client->name);
@ -245,7 +265,7 @@ namespace Components
void Chat::UnmuteClient(const Game::client_t* client) void Chat::UnmuteClient(const Game::client_t* client)
{ {
Chat::UnmuteInternal(client->steamID); UnmuteInternal(client->steamID);
Logger::Print("{} was unmuted\n", client->name); Logger::Print("{} was unmuted\n", client->name);
Game::SV_GameSendServerCommand(client->gentity->s.number, Game::SV_CMD_CAN_IGNORE, Game::SV_GameSendServerCommand(client->gentity->s.number, Game::SV_CMD_CAN_IGNORE,
@ -254,12 +274,12 @@ namespace Components
void Chat::UnmuteInternal(const std::uint64_t id, bool everyone) void Chat::UnmuteInternal(const std::uint64_t id, bool everyone)
{ {
std::unique_lock<std::mutex> lock(Chat::AccessMutex); std::unique_lock lock(AccessMutex);
if (everyone) if (everyone)
Chat::MuteList.clear(); MuteList.clear();
else else
Chat::MuteList.erase(id); MuteList.erase(id);
} }
void Chat::AddChatCommands() void Chat::AddChatCommands()
@ -282,7 +302,7 @@ namespace Components
const auto* client = Game::SV_GetPlayerByNum(); const auto* client = Game::SV_GetPlayerByNum();
if (client != nullptr) if (client != nullptr)
{ {
Chat::MuteClient(client); MuteClient(client);
} }
}); });
@ -305,27 +325,91 @@ namespace Components
if (client != nullptr) if (client != nullptr)
{ {
Chat::UnmuteClient(client); UnmuteClient(client);
return; return;
} }
if (std::strcmp(params->get(1), "all") == 0) if (std::strcmp(params->get(1), "all") == 0)
{ {
Logger::Print("All players were unmuted\n"); Logger::Print("All players were unmuted\n");
Chat::UnmuteInternal(0, true); UnmuteInternal(0, true);
} }
else else
{ {
const auto steamId = std::strtoull(params->get(1), nullptr, 16); const auto steamId = std::strtoull(params->get(1), nullptr, 16);
Chat::UnmuteInternal(steamId); UnmuteInternal(steamId);
} }
}); });
} }
int Chat::GetCallbackReturn()
{
if (Game::scrVmPub->inparamcount == 0)
{
// Nothing. Let's not mute the player
return 1;
}
Game::Scr_ClearOutParams();
Game::scrVmPub->outparamcount = Game::scrVmPub->inparamcount;
Game::scrVmPub->inparamcount = 0;
const auto* result = &Game::scrVmPub->top[1 - Game::scrVmPub->outparamcount];
if (result->type != Game::scrParamType_t::VAR_INTEGER)
{
// Garbage was returned
return 1;
}
return result->u.intValue;
}
int Chat::ChatCallback(Game::gentity_s* self, const char* codePos, const char* message, int mode)
{
const auto entityId = Game::Scr_GetEntityId(self->s.number, 0);
Game::Scr_AddInt(mode);
Game::Scr_AddString(message);
Game::VariableValue value;
value.type = Game::scrParamType_t::VAR_OBJECT;
value.u.uintValue = entityId;
Game::AddRefToValue(value.type, value.u);
const auto localId = Game::AllocThread(entityId);
const auto result = Game::VM_Execute_0(localId, codePos, 2);
Game::RemoveRefToObject(result);
return GetCallbackReturn();
}
void Chat::AddScriptFunctions()
{
Script::AddFunction("OnPlayerSay", [] // gsc: OnPlayerSay(<function>)
{
if (Game::Scr_GetNumParam() != 1)
{
Game::Scr_Error("^1OnPlayerSay: Needs one function pointer!\n");
return;
}
if (!CanAddCallback)
{
Game::Scr_Error("^1OnPlayerSay: Cannot add a callback in this context");
return;
}
const auto* func = Script::GetCodePosForParam(0);
SayCallbacks.emplace_back(func);
});
}
Chat::Chat() Chat::Chat()
{ {
cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message"); cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
Chat::sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::dvar_flag::DVAR_NONE, "Disable chat messages from clients"); sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::dvar_flag::DVAR_NONE, "Disable chat messages from clients");
Scheduler::Once(Chat::AddChatCommands, Scheduler::Pipeline::MAIN); Scheduler::Once(Chat::AddChatCommands, Scheduler::Pipeline::MAIN);
// Intercept chat sending // Intercept chat sending
@ -335,5 +419,13 @@ namespace Components
// Change logic that does word splitting with new lines for chat messages to support fonticons // Change logic that does word splitting with new lines for chat messages to support fonticons
Utils::Hook(0x592E10, CG_AddToTeamChat_Stub, HOOK_JUMP).install()->quick(); Utils::Hook(0x592E10, CG_AddToTeamChat_Stub, HOOK_JUMP).install()->quick();
AddScriptFunctions();
// Avoid duplicates
Events::OnVMShutdown([]
{
SayCallbacks.clear();
});
} }
} }

View File

@ -21,7 +21,10 @@ namespace Components
static std::mutex AccessMutex; static std::mutex AccessMutex;
static std::unordered_set<std::uint64_t> MuteList; static std::unordered_set<std::uint64_t> MuteList;
static const char* EvaluateSay(char* text, Game::gentity_t* player); static bool CanAddCallback; // ClientCommand & GSC thread are the same
static std::vector<Scripting::Function> SayCallbacks;
static const char* EvaluateSay(char* text, Game::gentity_t* player, int mode);
static void PreSayStub(); static void PreSayStub();
static void PostSayStub(); static void PostSayStub();
@ -34,5 +37,9 @@ namespace Components
static void UnmuteClient(const Game::client_t* client); static void UnmuteClient(const Game::client_t* client);
static void UnmuteInternal(const std::uint64_t id, bool everyone = false); static void UnmuteInternal(const std::uint64_t id, bool everyone = false);
static void AddChatCommands(); static void AddChatCommands();
static int GetCallbackReturn();
static int ChatCallback(Game::gentity_s* self, const char* codePos, const char* message, int mode);
static void AddScriptFunctions();
}; };
} }

View File

@ -121,7 +121,7 @@ namespace Components
void MapRotation::AddMapRotationCommands() void MapRotation::AddMapRotationCommands()
{ {
Command::Add("AddMap", [](Command::Params* params) Command::Add("addMap", [](Command::Params* params)
{ {
if (params->size() < 2) if (params->size() < 2)
{ {
@ -132,7 +132,7 @@ namespace Components
DedicatedRotation.addEntry("map", params->get(1)); DedicatedRotation.addEntry("map", params->get(1));
}); });
Command::Add("AddGametype", [](Command::Params* params) Command::Add("addGametype", [](Command::Params* params)
{ {
if (params->size() < 2) if (params->size() < 2)
{ {

View File

@ -15,6 +15,8 @@ namespace Components
static Game::client_t* GetClient(const Game::gentity_t* gentity); static Game::client_t* GetClient(const Game::gentity_t* gentity);
static const char* GetCodePosForParam(int index);
private: private:
static std::string ScriptName; static std::string ScriptName;
static std::vector<int> ScriptHandles; static std::vector<int> ScriptHandles;
@ -54,7 +56,6 @@ namespace Components
static unsigned int SetExpFogStub(); static unsigned int SetExpFogStub();
static const char* GetCodePosForParam(int index);
static void GetReplacedPos(const char* pos); static void GetReplacedPos(const char* pos);
static void SetReplacedPos(const char* what, const char* with); static void SetReplacedPos(const char* what, const char* with);
static void VMExecuteInternalStub(); static void VMExecuteInternalStub();

View File

@ -24,6 +24,9 @@ namespace Game
AddRefToObject_t AddRefToObject = AddRefToObject_t(0x61C360); AddRefToObject_t AddRefToObject = AddRefToObject_t(0x61C360);
AllocObject_t AllocObject = AllocObject_t(0x434320); AllocObject_t AllocObject = AllocObject_t(0x434320);
AddRefToValue_t AddRefToValue = AddRefToValue_t(0x482740);
AllocThread_t AllocThread = AllocThread_t(0x4F78C0);
VM_Execute_0_t VM_Execute_0 = VM_Execute_0_t(0x6222A0);
AngleVectors_t AngleVectors = AngleVectors_t(0x4691A0); AngleVectors_t AngleVectors = AngleVectors_t(0x4691A0);
@ -282,6 +285,7 @@ namespace Game
Scr_GetInt_t Scr_GetInt = Scr_GetInt_t(0x4F31D0); Scr_GetInt_t Scr_GetInt = Scr_GetInt_t(0x4F31D0);
Scr_GetObject_t Scr_GetObject = Scr_GetObject_t(0x462100); Scr_GetObject_t Scr_GetObject = Scr_GetObject_t(0x462100);
Scr_GetNumParam_t Scr_GetNumParam = Scr_GetNumParam_t(0x4B0E90); Scr_GetNumParam_t Scr_GetNumParam = Scr_GetNumParam_t(0x4B0E90);
Scr_GetEntityId_t Scr_GetEntityId = Scr_GetEntityId_t(0x4165E0);
Scr_ExecThread_t Scr_ExecThread = Scr_ExecThread_t(0x4AD0B0); Scr_ExecThread_t Scr_ExecThread = Scr_ExecThread_t(0x4AD0B0);
Scr_FreeThread_t Scr_FreeThread = Scr_FreeThread_t(0x4BD320); Scr_FreeThread_t Scr_FreeThread = Scr_FreeThread_t(0x4BD320);

View File

@ -28,6 +28,15 @@ namespace Game
typedef unsigned int(__cdecl * AllocObject_t)(); typedef unsigned int(__cdecl * AllocObject_t)();
extern AllocObject_t AllocObject; extern AllocObject_t AllocObject;
typedef void(__cdecl * AddRefToValue_t)(int type, VariableUnion u);
extern AddRefToValue_t AddRefToValue;
typedef unsigned int(__cdecl * AllocThread_t)(unsigned int self);
extern AllocThread_t AllocThread;
typedef unsigned int(__cdecl * VM_Execute_0_t)(unsigned int localId, const char* pos, unsigned int paramcount);
extern VM_Execute_0_t VM_Execute_0;
typedef void(__cdecl * AngleVectors_t)(float *angles, float *forward, float *right, float *up); typedef void(__cdecl * AngleVectors_t)(float *angles, float *forward, float *right, float *up);
extern AngleVectors_t AngleVectors; extern AngleVectors_t AngleVectors;
@ -735,12 +744,18 @@ namespace Game
typedef unsigned int(__cdecl * Scr_GetNumParam_t)(); typedef unsigned int(__cdecl * Scr_GetNumParam_t)();
extern Scr_GetNumParam_t Scr_GetNumParam; extern Scr_GetNumParam_t Scr_GetNumParam;
typedef unsigned int(__cdecl * Scr_GetEntityId_t)(int entnum, unsigned int classnum);
extern Scr_GetEntityId_t Scr_GetEntityId;
typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char* filename, const char* name); typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char* filename, const char* name);
extern Scr_GetFunctionHandle_t Scr_GetFunctionHandle; extern Scr_GetFunctionHandle_t Scr_GetFunctionHandle;
typedef int(__cdecl * Scr_ExecThread_t)(int, int); typedef int(__cdecl * Scr_ExecThread_t)(int, int);
extern Scr_ExecThread_t Scr_ExecThread; extern Scr_ExecThread_t Scr_ExecThread;
typedef int(__cdecl * Scr_ExecEntThread_t)(gentity_s* ent, int handle, unsigned int paramcount);
extern Scr_ExecEntThread_t Scr_ExecEntThread;
typedef int(__cdecl * Scr_FreeThread_t)(int); typedef int(__cdecl * Scr_FreeThread_t)(int);
extern Scr_FreeThread_t Scr_FreeThread; extern Scr_FreeThread_t Scr_FreeThread;

View File

@ -0,0 +1,14 @@
#include <STDInclude.hpp>
namespace Scripting
{
Function::Function(const char* pos)
: pos_(pos)
{
}
const char* Function::getPos() const
{
return this->pos_;
}
}

View File

@ -0,0 +1,15 @@
#pragma once
namespace Scripting
{
class Function
{
public:
Function(const char* pos);
[[nodiscard]] const char* getPos() const;
private:
const char* pos_;
};
}

View File

@ -138,6 +138,7 @@ using namespace std::literals;
#include "Game/Structs.hpp" #include "Game/Structs.hpp"
#include "Game/Functions.hpp" #include "Game/Functions.hpp"
#include <Game/Scripting/Function.hpp>
#include "Utils/Stream.hpp" // Breaks order on purpose #include "Utils/Stream.hpp" // Breaks order on purpose