fixes for iw4m, gsc, notifes and more
This commit is contained in:
parent
e7de6af00a
commit
549064fa64
60
src/client/component/chat.cpp
Normal file
60
src/client/component/chat.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
#include "client_command.hpp"
|
||||
|
||||
namespace chat
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void cmd_say_f(game::gentity_s* ent, const command::params_sv& params)
|
||||
{
|
||||
if (params.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto mode = 0;
|
||||
if (std::strcmp(params[0], "say_team") == 0)
|
||||
{
|
||||
mode = 1;
|
||||
}
|
||||
|
||||
auto p = params.join(1);
|
||||
game::Scr_AddString(game::SCRIPTINSTANCE_SERVER, p.data() + 1); // Skip special char
|
||||
game::Scr_Notify_Canon(ent, game::Scr_CanonHash(params[0]), 1);
|
||||
|
||||
game::G_Say(ent, nullptr, mode, p.data());
|
||||
}
|
||||
|
||||
void cmd_chat_f(game::gentity_s* ent, const command::params_sv& params)
|
||||
{
|
||||
auto p = params.join(1);
|
||||
|
||||
// Not a mistake! + 2 is necessary for the GSC script to receive only the actual chat text
|
||||
game::Scr_AddString(game::SCRIPTINSTANCE_SERVER, p.data() + 2);
|
||||
game::Scr_Notify_Canon(ent, game::Scr_CanonHash(params[0]), 1);
|
||||
|
||||
utils::hook::invoke<void>(0x140298E70, ent, p.data());
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public server_component
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
client_command::add("say", cmd_say_f);
|
||||
client_command::add("say_team", cmd_say_f);
|
||||
|
||||
client_command::add("chat", cmd_chat_f);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(chat::component)
|
57
src/client/component/client_command.cpp
Normal file
57
src/client/component/client_command.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
#include "client_command.hpp"
|
||||
|
||||
namespace client_command
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::unordered_map<std::string, callback> handlers;
|
||||
|
||||
void client_command_stub(const int client_num)
|
||||
{
|
||||
const auto ent = &game::g_entities[client_num];
|
||||
|
||||
if (ent->client == nullptr)
|
||||
{
|
||||
// Client is not fully in game
|
||||
return;
|
||||
}
|
||||
|
||||
const command::params_sv params;
|
||||
|
||||
const auto command = utils::string::to_lower(params.get(0));
|
||||
if (const auto got = handlers.find(command); got != handlers.end())
|
||||
{
|
||||
got->second(ent, params);
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::invoke<void>(0x140295C40, client_num);
|
||||
}
|
||||
}
|
||||
|
||||
void add(const std::string& name, const callback& cmd)
|
||||
{
|
||||
const auto command = utils::string::to_lower(name);
|
||||
handlers[command] = cmd;
|
||||
}
|
||||
|
||||
class component final : public server_component
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
utils::hook::call(0x14052F81B_g, client_command_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(client_command::component)
|
7
src/client/component/client_command.hpp
Normal file
7
src/client/component/client_command.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace client_command
|
||||
{
|
||||
using callback = std::function<void(game::gentity_s* ent, const command::params_sv& params)>;
|
||||
void add(const std::string& name, const callback& cmd);
|
||||
}
|
@ -18,6 +18,12 @@ namespace command
|
||||
return command_map;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, sv_command_param_function>& get_sv_command_map()
|
||||
{
|
||||
static std::unordered_map<std::string, sv_command_param_function> command_map{};
|
||||
return command_map;
|
||||
}
|
||||
|
||||
void execute_custom_command()
|
||||
{
|
||||
const params params{};
|
||||
@ -31,6 +37,19 @@ namespace command
|
||||
}
|
||||
}
|
||||
|
||||
void execute_custom_sv_command()
|
||||
{
|
||||
const params_sv params{};
|
||||
const auto command = utils::string::to_lower(params[0]);
|
||||
|
||||
auto& map = get_sv_command_map();
|
||||
const auto entry = map.find(command);
|
||||
if (entry != map.end())
|
||||
{
|
||||
entry->second(params);
|
||||
}
|
||||
}
|
||||
|
||||
game::CmdArgs* get_cmd_args()
|
||||
{
|
||||
return game::Sys_GetTLS()->cmdArgs;
|
||||
@ -67,6 +86,40 @@ namespace command
|
||||
return get_cmd_args()->argc[this->nesting_];
|
||||
}
|
||||
|
||||
params_sv::params_sv()
|
||||
: nesting_(game::sv_cmd_args->nesting)
|
||||
{
|
||||
assert(this->nesting_ < game::CMD_MAX_NESTING);
|
||||
}
|
||||
|
||||
int params_sv::size() const
|
||||
{
|
||||
return game::sv_cmd_args->argc[this->nesting_];
|
||||
}
|
||||
|
||||
const char* params_sv::get(const int index) const
|
||||
{
|
||||
if (index >= this->size())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return game::sv_cmd_args->argv[this->nesting_][index];
|
||||
}
|
||||
|
||||
std::string params_sv::join(const int index) const
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for (auto i = index; i < this->size(); ++i)
|
||||
{
|
||||
if (i > index) result.append(" ");
|
||||
result.append(this->get(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* params::get(const int index) const
|
||||
{
|
||||
if (index >= this->size())
|
||||
@ -118,6 +171,27 @@ namespace command
|
||||
game::Cmd_AddCommandInternal(cmd_string, execute_custom_command, cmd_function);
|
||||
cmd_function->autoComplete = 1;
|
||||
}
|
||||
|
||||
void add_sv(const std::string& command, sv_command_param_function function)
|
||||
{
|
||||
auto lower_command = utils::string::to_lower(command);
|
||||
|
||||
auto& map = get_sv_command_map();
|
||||
const auto is_registered = map.contains(lower_command);
|
||||
|
||||
map[std::move(lower_command)] = std::move(function);
|
||||
|
||||
if (is_registered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& allocator = *utils::memory::get_allocator();
|
||||
const auto* cmd_string = allocator.duplicate_string(command);
|
||||
|
||||
game::Cmd_AddCommandInternal(cmd_string, game::Cbuf_AddServerText_f, allocator.allocate<game::cmd_function_s>());
|
||||
game::Cmd_AddServerCommandInternal(cmd_string, execute_custom_sv_command, allocator.allocate<game::cmd_function_s>());
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(command::component)
|
||||
|
@ -7,11 +7,29 @@ namespace command
|
||||
public:
|
||||
params();
|
||||
|
||||
int size() const;
|
||||
const char* get(int index) const;
|
||||
std::string join(int index) const;
|
||||
[[nodiscard]] int size() const;
|
||||
[[nodiscard]] const char* get(int index) const;
|
||||
[[nodiscard]] std::string join(int index) const;
|
||||
|
||||
const char* operator[](const int index) const
|
||||
[[nodiscard]] const char* operator[](const int index) const
|
||||
{
|
||||
return this->get(index); //
|
||||
}
|
||||
|
||||
private:
|
||||
int nesting_;
|
||||
};
|
||||
|
||||
class params_sv
|
||||
{
|
||||
public:
|
||||
params_sv();
|
||||
|
||||
[[nodiscard]] int size() const;
|
||||
[[nodiscard]] const char* get(int index) const;
|
||||
[[nodiscard]] std::string join(int index) const;
|
||||
|
||||
[[nodiscard]] const char* operator[](const int index) const
|
||||
{
|
||||
return this->get(index); //
|
||||
}
|
||||
@ -22,7 +40,10 @@ namespace command
|
||||
|
||||
using command_function = std::function<void()>;
|
||||
using command_param_function = std::function<void(const params&)>;
|
||||
using sv_command_param_function = std::function<void(const params_sv&)>;
|
||||
|
||||
void add(const std::string& command, command_function function);
|
||||
void add(const std::string& command, command_param_function function);
|
||||
|
||||
void add_sv(const std::string& command, sv_command_param_function function);
|
||||
}
|
||||
|
51
src/client/component/console_command.cpp
Normal file
51
src/client/component/console_command.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
#include "console_command.hpp"
|
||||
|
||||
namespace console_command
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour console_command_hook;
|
||||
|
||||
std::unordered_map<std::string, callback> handlers;
|
||||
|
||||
int console_command_stub()
|
||||
{
|
||||
const command::params params;
|
||||
|
||||
const auto command = utils::string::to_lower(params.get(0));
|
||||
if (const auto got = handlers.find(command); got != handlers.end())
|
||||
{
|
||||
got->second(params);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return console_command_hook.invoke<int>();
|
||||
}
|
||||
}
|
||||
|
||||
void add_console(const std::string& name, const callback& cmd)
|
||||
{
|
||||
const auto command = utils::string::to_lower(name);
|
||||
handlers[command] = cmd;
|
||||
}
|
||||
|
||||
class component final : public server_component
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
console_command_hook.create(0x1402FF8C0_g, &console_command_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(console_command::component)
|
7
src/client/component/console_command.hpp
Normal file
7
src/client/component/console_command.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace console_command
|
||||
{
|
||||
using callback = std::function<void(const command::params& params)>;
|
||||
void add_console(const std::string& name, const callback& cmd);
|
||||
}
|
@ -9,6 +9,10 @@ namespace dedicated
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void sv_con_tell_f_stub(game::client_s* cl_0, game::svscmd_type type, [[maybe_unused]] const char* fmt, [[maybe_unused]] int c, char* text)
|
||||
{
|
||||
game::SV_SendServerCommand(cl_0, type, "%c \"GAME_SERVER\x15: %s\"", 79, text);
|
||||
}
|
||||
}
|
||||
|
||||
struct component final : server_component
|
||||
@ -18,6 +22,9 @@ namespace dedicated
|
||||
// Ignore "bad stats"
|
||||
utils::hook::set<uint8_t>(0x14052D523_g, 0xEB);
|
||||
utils::hook::nop(0x14052D4E4_g, 2);
|
||||
|
||||
// Fix tell command for IW4M
|
||||
utils::hook::call(0x14052A8CF_g, sv_con_tell_f_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
71
src/client/component/game_log.cpp
Normal file
71
src/client/component/game_log.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace game_log
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void g_scr_log_print()
|
||||
{
|
||||
char string[1024]{};
|
||||
std::size_t i_string_len = 0;
|
||||
|
||||
const auto i_num_parms = game::Scr_GetNumParam(game::SCRIPTINSTANCE_SERVER);
|
||||
for (std::uint32_t i = 0; i < i_num_parms; ++i)
|
||||
{
|
||||
const auto* psz_token = game::Scr_GetString(game::SCRIPTINSTANCE_SERVER, i);
|
||||
const auto i_token_len = std::strlen(psz_token);
|
||||
|
||||
i_string_len += i_token_len;
|
||||
if (i_string_len >= sizeof(string))
|
||||
{
|
||||
// Do not overflow the buffer
|
||||
break;
|
||||
}
|
||||
|
||||
strncat_s(string, psz_token, _TRUNCATE);
|
||||
}
|
||||
|
||||
game::G_LogPrintf("%s", string);
|
||||
}
|
||||
|
||||
void g_log_printf_stub(const char* fmt, ...)
|
||||
{
|
||||
char va_buffer[0x400] = { 0 };
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsprintf_s(va_buffer, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
const auto* file = "games_mp.log";
|
||||
const auto time = *game::level_time / 1000;
|
||||
|
||||
utils::io::write_file(file, utils::string::va("%3i:%i%i %s",
|
||||
time / 60,
|
||||
time % 60 / 10,
|
||||
time % 60 % 10,
|
||||
va_buffer
|
||||
), true);
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public server_component
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
// Fix format string vulnerability & make it work
|
||||
utils::hook::jump(0x1402D9300_g, g_scr_log_print);
|
||||
utils::hook::jump(0x1402A7BB0_g, g_log_printf_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(game_log::component)
|
@ -122,6 +122,11 @@ namespace network
|
||||
{
|
||||
return length + (socket_byte_missing() ? 1 : 0);
|
||||
}
|
||||
|
||||
void con_restricted_execute_buf_stub(int local_clientNum, game::ControllerIndex_t controller_index, const char* buffer)
|
||||
{
|
||||
game::Cbuf_ExecuteBuffer(local_clientNum, controller_index, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void on(const std::string& command, const callback& callback)
|
||||
@ -228,6 +233,9 @@ namespace network
|
||||
|
||||
utils::hook::set<uint8_t>(game::select(0x14224E90D, 0x1405315F9), 0xEB); // don't kick clients without dw handle
|
||||
|
||||
// Remove restrictions for rcon commands
|
||||
utils::hook::call(0x140538D5C_g, con_restricted_execute_buf_stub); // SVC_RemoteCommand
|
||||
|
||||
// TODO: Fix that
|
||||
scheduler::once(create_ip_socket, scheduler::main);
|
||||
}
|
||||
|
@ -870,6 +870,48 @@ namespace game
|
||||
JoinResult joinResult;
|
||||
};
|
||||
|
||||
typedef uint32_t ScrVarCanonicalName_t;
|
||||
|
||||
enum svscmd_type
|
||||
{
|
||||
SV_CMD_CAN_IGNORE_0 = 0x0,
|
||||
SV_CMD_RELIABLE_0 = 0x1,
|
||||
};
|
||||
|
||||
struct client_s
|
||||
{
|
||||
};
|
||||
|
||||
enum scriptInstance_t
|
||||
{
|
||||
SCRIPTINSTANCE_SERVER = 0x0,
|
||||
SCRIPTINSTANCE_CLIENT = 0x1,
|
||||
SCRIPT_INSTANCE_MAX = 0x2,
|
||||
};
|
||||
|
||||
struct gclient_s
|
||||
{
|
||||
char __pad0[0x8C];
|
||||
float velocity[3];
|
||||
char __pad1[59504];
|
||||
char flags;
|
||||
};
|
||||
|
||||
struct EntityState
|
||||
{
|
||||
int number;
|
||||
};
|
||||
|
||||
struct gentity_s
|
||||
{
|
||||
EntityState s;
|
||||
unsigned char __pad0[0x24C];
|
||||
gclient_s* client;
|
||||
unsigned char __pad1[0x2A0];
|
||||
};
|
||||
|
||||
static_assert(sizeof(gentity_s) == 0x4F8);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -8,4 +8,35 @@ namespace game
|
||||
{
|
||||
return eModes(*reinterpret_cast<uint32_t*>(game::select(0x1568EF7F4, 0x14948DB04)) << 28 >> 28);
|
||||
}
|
||||
|
||||
bool I_islower(int c)
|
||||
{
|
||||
return c >= 'a' && c <= 'z';
|
||||
}
|
||||
|
||||
bool I_isupper(int c)
|
||||
{
|
||||
return c >= 'A' && c <= 'Z';
|
||||
}
|
||||
|
||||
unsigned int Scr_CanonHash(const char* str)
|
||||
{
|
||||
#define FNV_OFFSET 0x4B9ACE2F
|
||||
#define FNV_PRIME 16777619
|
||||
|
||||
const auto* s = str;
|
||||
const int first_char = I_islower(*s) ? static_cast<unsigned char>(*s) : tolower(static_cast<unsigned char>(*str));
|
||||
|
||||
unsigned int hash = FNV_PRIME * (first_char ^ FNV_OFFSET);
|
||||
while (*s)
|
||||
{
|
||||
int acc = I_islower(*++s)
|
||||
? static_cast<unsigned char>(*s)
|
||||
: std::tolower(static_cast<unsigned char>(*s));
|
||||
|
||||
hash = FNV_PRIME * (acc ^ hash);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,10 @@ namespace game
|
||||
int numPrivateSlots, const char* mapname, const char* gametype)> CL_ConnectFromLobby
|
||||
{0x14134C570};
|
||||
|
||||
// Game
|
||||
WEAK symbol<void(gentity_s* ent, gentity_s* target, int mode, const char* chatText)> G_Say{0x0, 0x140299170};
|
||||
WEAK symbol<void(const char* fmt, ...)> G_LogPrintf{0x0, 0x1402A7BB0};
|
||||
|
||||
// Com
|
||||
WEAK symbol<void(int channel, unsigned int label, const char* fmt, ...)> Com_Printf{0x1421499C0, 0x140505630};
|
||||
WEAK symbol<void(const char* file, int line, int code, const char* fmt, ...)> Com_Error_{0x1420F8BD0};
|
||||
@ -22,9 +26,14 @@ namespace game
|
||||
};
|
||||
|
||||
WEAK symbol<void(uint32_t localClientNum, const char* text)> Cbuf_AddText{0x1420EC8B0, 0x1404F75B0};
|
||||
WEAK symbol<void(int localClientNum, ControllerIndex_t controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{0x0, 0x1404F78D0};
|
||||
WEAK symbol<void(const char* cmdName, xcommand_t function, cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{
|
||||
0x1420ED530, 0x1404F8210
|
||||
};
|
||||
WEAK symbol<void()> Cbuf_AddServerText_f{0x0, 0x1407DB4C0};
|
||||
WEAK symbol<void(const char* cmdName, xcommand_t function, cmd_function_s* allocedCmd)> Cmd_AddServerCommandInternal{
|
||||
0x0, 0x1404F8280
|
||||
};
|
||||
WEAK symbol<void(uint32_t localClientNum, ControllerIndex_t controllerIndex, const char* text,
|
||||
bool fromRemoteConsol)> Cmd_ExecuteSingleCommand{
|
||||
0x1420EDC20
|
||||
@ -64,6 +73,12 @@ namespace game
|
||||
0x1422C7F60
|
||||
};
|
||||
|
||||
// Scr
|
||||
WEAK symbol<void(scriptInstance_t inst, const char* value)> Scr_AddString{0x0, 0x14016F320};
|
||||
WEAK symbol<const char* (scriptInstance_t inst, unsigned int index)> Scr_GetString{0x0, 0x140171490};
|
||||
WEAK symbol<void(gentity_s* ent, ScrVarCanonicalName_t stringValue, unsigned int paramcount)> Scr_Notify_Canon{0x0, 0x1402F5FF0};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst)> Scr_GetNumParam{0x0, 0x140171320};
|
||||
|
||||
WEAK symbol<void(uint64_t id, bool cancelAll)> Cinematic_StopPlayback{0x1412BEA70};
|
||||
|
||||
// Rendering
|
||||
@ -72,13 +87,18 @@ namespace game
|
||||
0x141CD98D0
|
||||
};
|
||||
|
||||
// Rendering
|
||||
// SV
|
||||
WEAK symbol<void*()> SV_AddTestClient{0x1422499A0, 0x14052E3E0};
|
||||
WEAK symbol<void(client_s* cl_0, svscmd_type type, const char* fmt, ...)> SV_SendServerCommand{0x0, 0x140537F10};
|
||||
|
||||
// Variables
|
||||
|
||||
WEAK symbol<cmd_function_s> cmd_functions{0x15689FF58, 0x14946F860};
|
||||
WEAK symbol<CmdArgs> sv_cmd_args{0x15689CE30};
|
||||
WEAK symbol<CmdArgs> sv_cmd_args{0x0, 0x15689CE30};
|
||||
|
||||
WEAK symbol<gentity_s> g_entities{0x0, 0x1471031B0};
|
||||
|
||||
WEAK symbol<int> level_time{0x0, 0x1474FDC94};
|
||||
|
||||
WEAK symbol<SOCKET> ip_socket{0x157E77818, 0x14A640988};
|
||||
|
||||
@ -101,4 +121,9 @@ namespace game
|
||||
|
||||
// Re-implementations
|
||||
eModes Com_SessionMode_GetMode();
|
||||
|
||||
bool I_islower(int c);
|
||||
bool I_isupper(int c);
|
||||
|
||||
unsigned int Scr_CanonHash(const char* str);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user