Refactor command & add client commands
This commit is contained in:
parent
800795b3a4
commit
7b3ba9d3d4
@ -7,6 +7,8 @@ namespace game
|
||||
{
|
||||
Cmd_AddCommand_t Cmd_AddCommand;
|
||||
|
||||
Cmd_RemoveCommand_t Cmd_RemoveCommand;
|
||||
|
||||
Com_Error_t Com_Error;
|
||||
|
||||
DB_LoadXAssets_t DB_LoadXAssets;
|
||||
@ -45,6 +47,10 @@ namespace game
|
||||
|
||||
SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString;
|
||||
|
||||
SV_GameSendServerCommand_t SV_GameSendServerCommand;
|
||||
|
||||
SV_SendServerCommand_t SV_SendServerCommand;
|
||||
|
||||
XUIDToString_t XUIDToString;
|
||||
|
||||
decltype(longjmp)* _longjmp;
|
||||
@ -70,6 +76,8 @@ namespace game
|
||||
|
||||
int* svs_clientCount;
|
||||
|
||||
gentity_s* g_entities;
|
||||
|
||||
namespace mp
|
||||
{
|
||||
client_t* svs_clients;
|
||||
@ -386,7 +394,7 @@ namespace game
|
||||
call func
|
||||
|
||||
popad
|
||||
ret
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,6 +451,30 @@ namespace game
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void client_command_dedi(int clientNum)
|
||||
{
|
||||
static DWORD func = 0x47EA40;
|
||||
|
||||
__asm
|
||||
{
|
||||
mov edi, clientNum
|
||||
call func
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
void ClientCommand(int clientNum)
|
||||
{
|
||||
if (is_dedi())
|
||||
{
|
||||
client_command_dedi(clientNum);
|
||||
}
|
||||
else if (is_mp())
|
||||
{
|
||||
reinterpret_cast<void(*)(int)>(0x502CB0)(clientNum);
|
||||
}
|
||||
}
|
||||
|
||||
int GetProtocolVersion()
|
||||
{
|
||||
return 0x507C;
|
||||
@ -452,6 +484,61 @@ namespace game
|
||||
{
|
||||
addr->type = type;
|
||||
}
|
||||
|
||||
__declspec(naked) void cbuf_add_text_dedi(LocalClientNum_t localClientNum, const char* text)
|
||||
{
|
||||
static DWORD func = 0x4CB5D0;
|
||||
|
||||
__asm
|
||||
{
|
||||
mov eax, text
|
||||
push localClientNum
|
||||
call func
|
||||
add esp, 4h
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
void Cbuf_AddText(LocalClientNum_t localClientNum, const char* text)
|
||||
{
|
||||
if (is_dedi())
|
||||
{
|
||||
cbuf_add_text_dedi(localClientNum, text);
|
||||
}
|
||||
else
|
||||
{
|
||||
reinterpret_cast<void(*)(LocalClientNum_t, const char*)>
|
||||
(SELECT_VALUE(0x457C90, 0x545680, 0x0))(localClientNum, text);
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void teleport_player_dedi(gentity_s* player, float* origin, float* angles)
|
||||
{
|
||||
static DWORD func = 0x48B840;
|
||||
|
||||
__asm
|
||||
{
|
||||
mov eax, player
|
||||
mov ecx, origin
|
||||
push angles
|
||||
call func
|
||||
add esp, 4h
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
void TeleportPlayer(gentity_s* player, float* origin, float* angles)
|
||||
{
|
||||
if (is_dedi())
|
||||
{
|
||||
teleport_player_dedi(player, origin, angles);
|
||||
}
|
||||
else if (is_mp())
|
||||
{
|
||||
reinterpret_cast<void(*)(gentity_s*, float*, float*)>
|
||||
(0x50D840)(player, origin, angles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
launcher::mode mode = launcher::mode::none;
|
||||
@ -487,6 +574,8 @@ namespace game
|
||||
|
||||
native::Cmd_AddCommand = native::Cmd_AddCommand_t(SELECT_VALUE(0x558820, 0x545DF0, 0));
|
||||
|
||||
native::Cmd_RemoveCommand = native::Cmd_RemoveCommand_t(SELECT_VALUE(0x443A30, 0x545E20, 0x4CC060));
|
||||
|
||||
native::Com_Error = native::Com_Error_t(SELECT_VALUE(0x425540, 0x555450, 0x4D93F0));
|
||||
|
||||
native::DB_LoadXAssets = native::DB_LoadXAssets_t(SELECT_VALUE(0x48A8E0, 0x4CD020, 0x44F770));
|
||||
@ -528,6 +617,10 @@ namespace game
|
||||
|
||||
native::SV_Cmd_EndTokenizedString = native::SV_Cmd_EndTokenizedString_t(SELECT_VALUE(0x0, 0x545D70, 0x0));
|
||||
|
||||
native::SV_GameSendServerCommand = native::SV_GameSendServerCommand_t(SELECT_VALUE(0x0, 0x573220, 0x0));
|
||||
|
||||
native::SV_SendServerCommand = native::SV_SendServerCommand_t(SELECT_VALUE(0x0, 0x575DE0, 0x4FD5A0));
|
||||
|
||||
native::XUIDToString = native::XUIDToString_t(SELECT_VALUE(0x4FAA30, 0x55CC20, 0x0));
|
||||
|
||||
native::_longjmp = reinterpret_cast<decltype(longjmp)*>(SELECT_VALUE(0x73AC20, 0x7363BC, 0x655558));
|
||||
@ -558,5 +651,7 @@ namespace game
|
||||
|
||||
native::mp::svs_clients = reinterpret_cast<native::mp::client_t*>(0x4B5CF90);
|
||||
native::dedi::svs_clients = reinterpret_cast<native::dedi::client_t*>(0x4A12E90);
|
||||
|
||||
native::g_entities = reinterpret_cast<native::gentity_s*>(SELECT_VALUE(0, 0x1A66E28, 0x191B900));
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ namespace game
|
||||
typedef void (*Cmd_AddCommand_t)(const char* cmdName, void (*function)(), cmd_function_t* allocedCmd);
|
||||
extern Cmd_AddCommand_t Cmd_AddCommand;
|
||||
|
||||
typedef void (*Cmd_RemoveCommand_t)(const char* cmdName);
|
||||
extern Cmd_RemoveCommand_t Cmd_RemoveCommand;
|
||||
|
||||
typedef void (*Com_Error_t)(int code, const char* fmt, ...);
|
||||
extern Com_Error_t Com_Error;
|
||||
|
||||
@ -71,11 +74,18 @@ namespace game
|
||||
typedef void (*SV_Cmd_EndTokenizedString_t)();
|
||||
extern SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString;
|
||||
|
||||
typedef void (*SV_GameSendServerCommand_t)(int clientNum, svscmd_type type, const char* text);
|
||||
extern SV_GameSendServerCommand_t SV_GameSendServerCommand;
|
||||
|
||||
typedef void (*SV_SendServerCommand_t)(dedi::client_t* cl, svscmd_type type, const char* fmt, ...);
|
||||
extern SV_SendServerCommand_t SV_SendServerCommand;
|
||||
|
||||
typedef void (*XUIDToString_t)(const unsigned __int64* xuid, char* str);
|
||||
extern XUIDToString_t XUIDToString;
|
||||
|
||||
extern decltype(longjmp)* _longjmp;
|
||||
|
||||
constexpr auto CMD_MAX_NESTING = 8;
|
||||
extern CmdArgs* sv_cmd_args;
|
||||
extern CmdArgs* cmd_args;
|
||||
|
||||
@ -97,6 +107,8 @@ namespace game
|
||||
|
||||
extern int* svs_clientCount;
|
||||
|
||||
extern gentity_s* g_entities;
|
||||
|
||||
namespace mp
|
||||
{
|
||||
extern client_t* svs_clients;
|
||||
@ -135,9 +147,15 @@ namespace game
|
||||
void SV_DropClient(mp::client_t* drop, const char* reason, bool tellThem);
|
||||
void SV_DropAllBots();
|
||||
|
||||
void ClientCommand(int clientNum);
|
||||
|
||||
int GetProtocolVersion();
|
||||
|
||||
void NetAdr_SetType(netadr_s* addr, netadrtype_t type);
|
||||
|
||||
void Cbuf_AddText(LocalClientNum_t localClientNum, const char* text);
|
||||
|
||||
void TeleportPlayer(gentity_s* player, float* origin, float* angles);
|
||||
}
|
||||
|
||||
bool is_mp();
|
||||
|
@ -4,6 +4,11 @@ namespace game
|
||||
{
|
||||
namespace native
|
||||
{
|
||||
typedef float vec_t;
|
||||
typedef vec_t vec2_t[2];
|
||||
typedef vec_t vec3_t[3];
|
||||
typedef vec_t vec4_t[4];
|
||||
|
||||
enum bdLobbyErrorCode : uint32_t
|
||||
{
|
||||
BD_NO_ERROR = 0x0,
|
||||
@ -369,6 +374,23 @@ namespace game
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
enum svscmd_type
|
||||
{
|
||||
SV_CMD_CAN_IGNORE,
|
||||
SV_CMD_RELIABLE,
|
||||
};
|
||||
|
||||
enum LocalClientNum_t
|
||||
{
|
||||
LOCAL_CLIENT_0 = 0,
|
||||
LOCAL_CLIENT_1 = 1,
|
||||
LOCAL_CLIENT_2 = 2,
|
||||
LOCAL_CLIENT_3 = 3,
|
||||
LOCAL_CLIENT_LAST = 3,
|
||||
LOCAL_CLIENT_COUNT = 4,
|
||||
LOCAL_CLIENT_INVALID = -1,
|
||||
};
|
||||
|
||||
struct cmd_function_t
|
||||
{
|
||||
cmd_function_t* next;
|
||||
@ -594,6 +616,28 @@ namespace game
|
||||
|
||||
static_assert(sizeof(gclient_s) == 0x3980);
|
||||
|
||||
enum entityFlag
|
||||
{
|
||||
FL_GODMODE = 0x1,
|
||||
FL_DEMI_GODMODE = 0x2,
|
||||
FL_NOTARGET = 0x4,
|
||||
FL_NO_KNOCKBACK = 0x8,
|
||||
FL_NO_RADIUS_DAMAGE = 0x10,
|
||||
FL_SUPPORTS_LINKTO = 0x1000,
|
||||
FL_NO_AUTO_ANIM_UPDATE = 0x2000,
|
||||
FL_GRENADE_TOUCH_DAMAGE = 0x4000,
|
||||
FL_STABLE_MISSILES = 0x20000,
|
||||
FL_REPEAT_ANIM_UPDATE = 0x40000,
|
||||
FL_VEHICLE_TARGET = 0x80000,
|
||||
FL_GROUND_ENT = 0x100000,
|
||||
FL_CURSOR_HINT = 0x200000,
|
||||
FL_MISSILE_ATTRACTOR = 0x800000,
|
||||
FL_WEAPON_BEING_GRABBED = 0x1000000,
|
||||
FL_DELETE = 0x2000000,
|
||||
FL_BOUNCE = 0x4000000,
|
||||
FL_MOVER_SLIDE = 0x8000000
|
||||
};
|
||||
|
||||
struct entityState_s
|
||||
{
|
||||
int number;
|
||||
|
142
src/module/client_command.cpp
Normal file
142
src/module/client_command.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/module_loader.hpp"
|
||||
#include "command.hpp"
|
||||
|
||||
#include "game/structs.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "utils/string.hpp"
|
||||
|
||||
class client_command final : public module
|
||||
{
|
||||
public:
|
||||
void post_load() override
|
||||
{
|
||||
if (game::is_mp())
|
||||
{
|
||||
add_mp_client_commands();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void send_msg_to_client(int client_num, game::native::svscmd_type type, const char* string)
|
||||
{
|
||||
if (game::is_dedi())
|
||||
{
|
||||
game::native::SV_SendServerCommand(&game::native::dedi::svs_clients[client_num],
|
||||
type, string);
|
||||
}
|
||||
else if (game::is_mp())
|
||||
{
|
||||
game::native::SV_GameSendServerCommand(client_num, type, string);
|
||||
}
|
||||
}
|
||||
|
||||
// I know this is supposed to check sv_cheats but it's not even a registered dvar so why bother
|
||||
static bool cheats_ok(game::native::gentity_s* ent)
|
||||
{
|
||||
if (ent->health < 1)
|
||||
{
|
||||
send_msg_to_client(ent->s.number, game::native::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void add_mp_client_commands()
|
||||
{
|
||||
command::add_sv("noclip", [](game::native::gentity_s* ent, [[maybe_unused]] const command::params_sv& params)
|
||||
{
|
||||
if (!cheats_ok(ent))
|
||||
return;
|
||||
|
||||
ent->client->flags ^= 1;
|
||||
|
||||
send_msg_to_client(ent->s.number, game::native::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"%s\"", 0x65, (ent->client->flags & 1) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
||||
});
|
||||
|
||||
command::add_sv("ufo", [](game::native::gentity_s* ent, [[maybe_unused]] const command::params_sv& params)
|
||||
{
|
||||
if (!cheats_ok(ent))
|
||||
return;
|
||||
|
||||
ent->client->flags ^= 2;
|
||||
|
||||
send_msg_to_client(ent->s.number, game::native::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"%s\"", 0x65, (ent->client->flags & 2) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
||||
});
|
||||
|
||||
command::add_sv("god", [](game::native::gentity_s* ent, [[maybe_unused]] const command::params_sv& params)
|
||||
{
|
||||
if (!cheats_ok(ent))
|
||||
return;
|
||||
|
||||
ent->flags = game::native::FL_GODMODE;
|
||||
|
||||
send_msg_to_client(ent->s.number, game::native::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"%s\"", 0x65, (ent->flags & game::native::FL_GODMODE)
|
||||
? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
||||
});
|
||||
|
||||
command::add_sv("demigod", [](game::native::gentity_s* ent, [[maybe_unused]] const command::params_sv& params)
|
||||
{
|
||||
if (!cheats_ok(ent))
|
||||
return;
|
||||
|
||||
ent->flags = game::native::FL_DEMI_GODMODE;
|
||||
|
||||
send_msg_to_client(ent->s.number, game::native::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"%s\"", 0x65, (ent->flags & game::native::FL_DEMI_GODMODE)
|
||||
? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
|
||||
});
|
||||
|
||||
command::add_sv("notarget", [](game::native::gentity_s* ent, [[maybe_unused]] const command::params_sv& params)
|
||||
{
|
||||
if (!cheats_ok(ent))
|
||||
return;
|
||||
|
||||
ent->flags = game::native::FL_NOTARGET;
|
||||
|
||||
send_msg_to_client(ent->s.number, game::native::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"%s\"", 0x65, (ent->flags & game::native::FL_NOTARGET)
|
||||
? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
|
||||
});
|
||||
|
||||
command::add_sv("setviewpos", [](game::native::gentity_s* ent, [[maybe_unused]] const command::params_sv& params)
|
||||
{
|
||||
if (!cheats_ok(ent))
|
||||
return;
|
||||
|
||||
game::native::vec3_t origin, angles{0.f, 0.f, 0.f};
|
||||
|
||||
if (params.size() < 4 || params.size() > 6)
|
||||
{
|
||||
send_msg_to_client(ent->s.number, game::native::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\"", 0x65));
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < 3; i++)
|
||||
{
|
||||
origin[i] = std::strtof(params.get(i + 1), nullptr);
|
||||
}
|
||||
|
||||
if (params.size() > 4)
|
||||
{
|
||||
angles[1] = std::strtof(params.get(4), nullptr); // Yaw
|
||||
}
|
||||
|
||||
if (params.size() == 6u)
|
||||
{
|
||||
angles[0] = std::strtof(params.get(5), nullptr); // Pitch
|
||||
}
|
||||
|
||||
game::native::TeleportPlayer(ent, origin, angles);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_MODULE(client_command);
|
@ -1,54 +1,188 @@
|
||||
#include <std_include.hpp>
|
||||
#include "command.hpp"
|
||||
|
||||
#include "utils/string.hpp"
|
||||
#include "game/structs.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "utils/hook.hpp"
|
||||
|
||||
utils::memory::allocator command::allocator_;
|
||||
std::mutex command::mutex_;
|
||||
std::unordered_map<std::string, std::function<void(const std::vector<std::string>&)>> command::callbacks_;
|
||||
std::unordered_map<std::string, std::function<void(const command::params&)>> command::handlers;
|
||||
std::unordered_map<std::string, std::function<void(game::native::gentity_s*, command::params_sv&)>> command::handlers_sv;
|
||||
|
||||
void command::add(const std::string& name, const std::function<void(const std::vector<std::string>&)>& callback)
|
||||
command::params::params()
|
||||
: nesting_(game::native::cmd_args->nesting)
|
||||
{
|
||||
std::lock_guard _(mutex_);
|
||||
callbacks_[utils::string::to_lower(name)] = callback;
|
||||
assert(game::native::cmd_args->nesting < game::native::CMD_MAX_NESTING);
|
||||
}
|
||||
|
||||
const auto cmd_name = allocator_.duplicate_string(name);
|
||||
const auto cmd_function = allocator_.allocate<game::native::cmd_function_t>();
|
||||
int command::params::size() const
|
||||
{
|
||||
return game::native::cmd_args->argc[this->nesting_];
|
||||
}
|
||||
|
||||
game::native::Cmd_AddCommand(cmd_name, dispatcher, cmd_function);
|
||||
const char* command::params::get(const int index) const
|
||||
{
|
||||
if (index >= this->size())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return game::native::cmd_args->argv[this->nesting_][index];
|
||||
}
|
||||
|
||||
std::string command::params::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;
|
||||
}
|
||||
|
||||
command::params_sv::params_sv()
|
||||
: nesting_(game::native::sv_cmd_args->nesting)
|
||||
{
|
||||
assert(game::native::sv_cmd_args->nesting < game::native::CMD_MAX_NESTING);
|
||||
}
|
||||
|
||||
int command::params_sv::size() const
|
||||
{
|
||||
return game::native::sv_cmd_args->argc[this->nesting_];
|
||||
}
|
||||
|
||||
const char* command::params_sv::get(const int index) const
|
||||
{
|
||||
if (index >= this->size())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return game::native::sv_cmd_args->argv[this->nesting_][index];
|
||||
}
|
||||
|
||||
std::string command::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;
|
||||
}
|
||||
|
||||
void command::add_raw(const char* name, void (*callback)())
|
||||
{
|
||||
game::native::Cmd_AddCommand(name, callback, command::allocator_.allocate<game::native::cmd_function_t>());
|
||||
}
|
||||
|
||||
void command::add(const char* name, const std::function<void(const command::params&)>& callback)
|
||||
{
|
||||
const auto command = utils::string::to_lower(name);
|
||||
|
||||
if (handlers.find(command) == handlers.end())
|
||||
{
|
||||
add_raw(name, main_handler);
|
||||
}
|
||||
|
||||
handlers[command] = callback;
|
||||
}
|
||||
|
||||
void command::add(const char* name, const std::function<void()>& callback)
|
||||
{
|
||||
add(name, [callback](const command::params&)
|
||||
{
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
void command::add_sv(const char* name, std::function<void(game::native::gentity_s*, const command::params_sv&)> callback)
|
||||
{
|
||||
// Since the console is not usable there is no point in calling add_raw
|
||||
const auto command = utils::string::to_lower(name);
|
||||
|
||||
if (handlers_sv.find(command) == handlers_sv.end())
|
||||
{
|
||||
handlers_sv[command] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
void command::main_handler()
|
||||
{
|
||||
params params;
|
||||
|
||||
const auto command = utils::string::to_lower(params[0]);
|
||||
const auto got = handlers.find(command);
|
||||
|
||||
if (got != handlers.end())
|
||||
{
|
||||
got->second(params);
|
||||
}
|
||||
}
|
||||
|
||||
void command::client_command_stub(int client_num)
|
||||
{
|
||||
const auto entity = &game::native::g_entities[client_num];
|
||||
|
||||
if (entity->client == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
params_sv params;
|
||||
|
||||
const auto command = utils::string::to_lower(params[0]);
|
||||
const auto got = handlers_sv.find(command);
|
||||
|
||||
if (got != handlers_sv.end())
|
||||
{
|
||||
got->second(entity, params);
|
||||
}
|
||||
|
||||
game::native::ClientCommand(client_num);
|
||||
}
|
||||
|
||||
__declspec(naked) void command::client_command_dedi_stub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
push edi
|
||||
call client_command_stub
|
||||
add esp, 4h
|
||||
|
||||
popad
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
void command::post_load()
|
||||
{
|
||||
if (game::is_mp())
|
||||
{
|
||||
utils::hook(0x57192A, &command::client_command_stub, HOOK_CALL).install()->quick(); // SV_ExecuteClientCommand
|
||||
}
|
||||
else if (game::is_dedi())
|
||||
{
|
||||
utils::hook(0x4F96B5, &command::client_command_dedi_stub, HOOK_CALL).install()->quick(); // SV_ExecuteClientCommand
|
||||
}
|
||||
}
|
||||
|
||||
void command::pre_destroy()
|
||||
{
|
||||
std::lock_guard _(mutex_);
|
||||
if (!callbacks_.empty())
|
||||
for (const auto& [key, val] : command::handlers)
|
||||
{
|
||||
callbacks_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void command::dispatcher()
|
||||
{
|
||||
const auto cmd_index = game::native::cmd_args->nesting;
|
||||
const auto arg_count = game::native::cmd_args->argc[cmd_index];
|
||||
|
||||
if (arg_count < 1) return;
|
||||
|
||||
const auto command = utils::string::to_lower(game::native::cmd_args->argv[cmd_index][0]);
|
||||
const auto handler = callbacks_.find(command);
|
||||
if (handler == callbacks_.end()) return;
|
||||
|
||||
std::vector<std::string> arguments;
|
||||
arguments.reserve(arg_count);
|
||||
|
||||
for (auto i = 0; i < game::native::cmd_args->argc[cmd_index]; ++i)
|
||||
{
|
||||
arguments.emplace_back(game::native::cmd_args->argv[cmd_index][i]);
|
||||
handlers.erase(key);
|
||||
game::native::Cmd_RemoveCommand(key.data());
|
||||
}
|
||||
|
||||
handler->second(arguments);
|
||||
command::allocator_.clear();
|
||||
}
|
||||
|
||||
REGISTER_MODULE(command);
|
||||
|
@ -1,18 +1,68 @@
|
||||
#pragma once
|
||||
#include "loader/module_loader.hpp"
|
||||
|
||||
#include "game/structs.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "utils/memory.hpp"
|
||||
|
||||
class command final : public module
|
||||
{
|
||||
public:
|
||||
static void add(const std::string& name, const std::function<void(const std::vector<std::string>&)>& callback);
|
||||
class params
|
||||
{
|
||||
public:
|
||||
params();
|
||||
|
||||
int size() const;
|
||||
const char* get(int index) const;
|
||||
std::string join(int index) const;
|
||||
|
||||
const char* operator[](const int index) const
|
||||
{
|
||||
return this->get(index);
|
||||
}
|
||||
|
||||
private:
|
||||
int nesting_;
|
||||
};
|
||||
|
||||
class params_sv
|
||||
{
|
||||
public:
|
||||
params_sv();
|
||||
|
||||
int size() const;
|
||||
const char* get(int index) const;
|
||||
std::string join(int index) const;
|
||||
|
||||
const char* operator[](const int index) const
|
||||
{
|
||||
return this->get(index);
|
||||
}
|
||||
|
||||
private:
|
||||
int nesting_;
|
||||
};
|
||||
|
||||
static void add(const char* name, const std::function<void(const params&)>& callback);
|
||||
static void add(const char* name, const std::function<void()>& callback);
|
||||
|
||||
static void add_sv(const char* name, std::function<void(game::native::gentity_s*, const params_sv&)> callback);
|
||||
|
||||
void post_load() override;
|
||||
void pre_destroy() override;
|
||||
|
||||
private:
|
||||
static utils::memory::allocator allocator_;
|
||||
static std::mutex mutex_;
|
||||
static std::unordered_map<std::string, std::function<void(const std::vector<std::string>&)>> callbacks_;
|
||||
|
||||
static void dispatcher();
|
||||
static std::unordered_map<std::string, std::function<void(const params&)>> handlers;
|
||||
static std::unordered_map<std::string, std::function<void(game::native::gentity_s*, params_sv&)>> handlers_sv;
|
||||
|
||||
static void main_handler();
|
||||
|
||||
static void client_command_stub(int client_num);
|
||||
static void client_command_dedi_stub();
|
||||
|
||||
static void add_raw(const char* name, void (*callback)());
|
||||
};
|
||||
|
@ -16,7 +16,7 @@ public:
|
||||
if (game::is_mp())
|
||||
{
|
||||
// Set dvar limit
|
||||
static const auto cg_fov_limit = 90.0f;
|
||||
static const auto cg_fov_limit = 120.0f;
|
||||
utils::hook::set(0x455148, &cg_fov_limit);
|
||||
|
||||
// Prevent value change via internal scripts
|
||||
|
@ -158,7 +158,7 @@ void test_clients::post_load()
|
||||
if (game::is_mp()) this->patch_mp();
|
||||
else return; // No sp/dedi bots for now :(
|
||||
|
||||
command::add("spawnBot", []([[maybe_unused]] const std::vector<std::string>& params)
|
||||
command::add("spawnBot", []()
|
||||
{
|
||||
// Because I am unable to expand the scheduler at the moment
|
||||
// we only get one bot at the time
|
||||
|
Loading…
Reference in New Issue
Block a user