2018-12-28 12:50:34 +01:00
|
|
|
#include <std_include.hpp>
|
2022-03-23 13:48:13 +00:00
|
|
|
#include <loader/module_loader.hpp>
|
2022-10-06 23:15:25 +01:00
|
|
|
#include "game/game.hpp"
|
|
|
|
|
2022-03-23 13:48:13 +00:00
|
|
|
#include <utils/string.hpp>
|
|
|
|
#include <utils/hook.hpp>
|
2022-03-10 19:50:36 +00:00
|
|
|
|
2022-03-23 13:48:13 +00:00
|
|
|
#include "command.hpp"
|
2022-10-06 23:15:25 +01:00
|
|
|
|
2018-12-28 12:50:34 +01:00
|
|
|
|
2022-12-09 14:50:13 +01:00
|
|
|
static utils::memory::allocator allocator;
|
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
std::unordered_map<std::string, std::function<void(const command::params&)>> command::handlers;
|
2022-12-19 13:13:13 +01:00
|
|
|
std::unordered_map<std::string, std::function<void(game::native::gentity_s*, const command::params_sv&)>> command::handlers_sv;
|
|
|
|
std::unordered_map<std::string, std::function<void(game::native::sp::gentity_s*, const command::params_sv&)>> command::handlers_sp_sv;
|
2018-12-28 12:50:34 +01:00
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
command::params::params()
|
|
|
|
: nesting_(game::native::cmd_args->nesting)
|
2018-12-28 12:50:34 +01:00
|
|
|
{
|
2022-03-10 19:50:36 +00:00
|
|
|
assert(game::native::cmd_args->nesting < game::native::CMD_MAX_NESTING);
|
|
|
|
}
|
|
|
|
|
|
|
|
int command::params::size() const
|
|
|
|
{
|
|
|
|
return game::native::cmd_args->argc[this->nesting_];
|
|
|
|
}
|
2018-12-28 12:50:34 +01:00
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
const char* command::params::get(const int index) const
|
|
|
|
{
|
|
|
|
if (index >= this->size())
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
}
|
2018-12-28 12:50:34 +01:00
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
return game::native::cmd_args->argv[this->nesting_][index];
|
2018-12-28 12:50:34 +01:00
|
|
|
}
|
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
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
|
2018-12-28 12:50:34 +01:00
|
|
|
{
|
2022-03-10 19:50:36 +00:00
|
|
|
std::string result;
|
|
|
|
|
|
|
|
for (auto i = index; i < this->size(); i++)
|
2019-01-05 11:44:45 +01:00
|
|
|
{
|
2022-03-10 19:50:36 +00:00
|
|
|
if (i > index) result.append(" ");
|
|
|
|
result.append(this->get(i));
|
2019-01-05 11:44:45 +01:00
|
|
|
}
|
2022-03-10 19:50:36 +00:00
|
|
|
|
|
|
|
return result;
|
2018-12-28 12:50:34 +01:00
|
|
|
}
|
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
void command::add_raw(const char* name, void (*callback)())
|
2018-12-28 12:50:34 +01:00
|
|
|
{
|
2022-12-09 14:50:13 +01:00
|
|
|
game::native::Cmd_AddCommand(name, callback, allocator.allocate<game::native::cmd_function_t>());
|
2022-03-10 19:50:36 +00:00
|
|
|
}
|
|
|
|
|
2022-12-19 13:13:13 +01:00
|
|
|
void command::add(const char* name, const std::function<void(const params&)>& callback)
|
2022-03-10 19:50:36 +00:00
|
|
|
{
|
|
|
|
const auto command = utils::string::to_lower(name);
|
2018-12-28 12:50:34 +01:00
|
|
|
|
2022-08-11 23:04:29 +02:00
|
|
|
if (!handlers.contains(command))
|
2022-03-10 19:50:36 +00:00
|
|
|
{
|
|
|
|
add_raw(name, main_handler);
|
|
|
|
}
|
2019-11-29 22:42:38 +01:00
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
handlers[command] = callback;
|
|
|
|
}
|
2018-12-28 12:50:34 +01:00
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
void command::add(const char* name, const std::function<void()>& callback)
|
|
|
|
{
|
2022-08-11 23:04:29 +02:00
|
|
|
add(name, [callback]([[maybe_unused]] const params& params)
|
2022-03-10 19:50:36 +00:00
|
|
|
{
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
}
|
2018-12-28 12:50:34 +01:00
|
|
|
|
2022-12-19 13:13:13 +01:00
|
|
|
void command::add_sv(const char* name, std::function<void(game::native::gentity_s*, const params_sv&)> callback)
|
2022-03-10 19:50:36 +00:00
|
|
|
{
|
2022-03-10 21:20:13 +00:00
|
|
|
// Since the game console is not usable there is no point in calling add_raw
|
2022-03-10 19:50:36 +00:00
|
|
|
const auto command = utils::string::to_lower(name);
|
|
|
|
|
2022-08-11 23:04:29 +02:00
|
|
|
if (!handlers_sv.contains(command))
|
2022-03-10 19:50:36 +00:00
|
|
|
{
|
|
|
|
handlers_sv[command] = callback;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-11 14:30:00 +00:00
|
|
|
void command::add_sp_sv(const char* name, std::function<void(game::native::sp::gentity_s*, const params_sv&)> callback)
|
|
|
|
{
|
|
|
|
const auto command = utils::string::to_lower(name);
|
|
|
|
|
2022-08-11 23:04:29 +02:00
|
|
|
if (!handlers_sp_sv.contains(command))
|
2022-03-11 14:30:00 +00:00
|
|
|
{
|
|
|
|
handlers_sp_sv[command] = callback;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 23:54:41 +00:00
|
|
|
void command::execute(std::string command, bool sync)
|
|
|
|
{
|
|
|
|
command += "\n";
|
|
|
|
|
|
|
|
if (sync)
|
|
|
|
{
|
|
|
|
game::native::Cmd_ExecuteSingleCommand(game::native::LOCAL_CLIENT_0, 0, command.data());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game::native::Cbuf_AddText(game::native::LOCAL_CLIENT_0, command.data());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
void command::main_handler()
|
|
|
|
{
|
2022-12-19 13:13:13 +01:00
|
|
|
const params params;
|
2022-03-10 19:50:36 +00:00
|
|
|
|
|
|
|
const auto command = utils::string::to_lower(params[0]);
|
|
|
|
|
2022-12-09 14:50:13 +01:00
|
|
|
if (const auto itr = handlers.find(command); itr != handlers.end())
|
2022-03-10 19:50:36 +00:00
|
|
|
{
|
2022-10-14 22:48:09 +01:00
|
|
|
itr->second(params);
|
2022-03-10 19:50:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void command::client_command_stub(int client_num)
|
|
|
|
{
|
|
|
|
const auto entity = &game::native::g_entities[client_num];
|
|
|
|
|
2022-12-09 14:50:13 +01:00
|
|
|
if (!entity->client)
|
2022-03-10 19:50:36 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-19 13:13:13 +01:00
|
|
|
const params_sv params;
|
2022-03-10 19:50:36 +00:00
|
|
|
|
|
|
|
const auto command = utils::string::to_lower(params[0]);
|
|
|
|
|
2022-12-09 14:50:13 +01:00
|
|
|
if (const auto itr = handlers_sv.find(command); itr != handlers_sv.end())
|
2022-03-10 19:50:36 +00:00
|
|
|
{
|
2022-10-14 22:48:09 +01:00
|
|
|
itr->second(entity, params);
|
2022-03-10 21:20:13 +00:00
|
|
|
return;
|
2022-03-10 19:50:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
game::native::ClientCommand(client_num);
|
|
|
|
}
|
|
|
|
|
2022-03-11 14:30:00 +00:00
|
|
|
void command::client_command_sp(int client_num, const char* s)
|
|
|
|
{
|
|
|
|
auto* entity = &game::native::sp::g_entities[client_num];
|
|
|
|
|
2022-08-11 23:04:29 +02:00
|
|
|
assert(entity->client); // On sp it should only be an assertion
|
2022-03-11 14:30:00 +00:00
|
|
|
|
2022-12-19 13:13:13 +01:00
|
|
|
const params_sv params;
|
2022-03-11 14:30:00 +00:00
|
|
|
|
|
|
|
const auto command = utils::string::to_lower(params[0]);
|
|
|
|
|
2022-12-09 14:50:13 +01:00
|
|
|
if (const auto itr = handlers_sp_sv.find(command); itr != handlers_sp_sv.end())
|
2022-03-11 14:30:00 +00:00
|
|
|
{
|
2022-10-14 22:48:09 +01:00
|
|
|
itr->second(entity, params);
|
2022-03-11 14:30:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void command::client_command_sp_stub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
pushad
|
|
|
|
|
|
|
|
push [esp + 0x20 + 0x8]
|
|
|
|
push [esp + 0x20 + 0x8]
|
2022-12-09 14:50:13 +01:00
|
|
|
call client_command_sp
|
2022-03-11 14:30:00 +00:00
|
|
|
add esp, 0x8
|
|
|
|
|
|
|
|
popad
|
|
|
|
|
|
|
|
// Code our hook skipped
|
|
|
|
mov eax, [esp + 0x8]
|
|
|
|
sub esp, 0x400
|
|
|
|
|
|
|
|
push 0x44BB5A // ClientCommand
|
|
|
|
retn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Between ufo/noclip functions and their mp counterpart is that I reversed the 'CG' type
|
|
|
|
void command::add_sp_commands()
|
|
|
|
{
|
2022-05-21 21:39:22 +02:00
|
|
|
add("noclip", []
|
2022-03-11 14:30:00 +00:00
|
|
|
{
|
2022-05-13 10:59:11 +01:00
|
|
|
if (!game::native::sp::IsServerRunning())
|
2022-03-14 23:54:41 +00:00
|
|
|
return;
|
|
|
|
|
2022-03-11 14:30:00 +00:00
|
|
|
const auto* ent = &game::native::sp::g_entities[0];
|
|
|
|
if (ent->health < 1)
|
|
|
|
return;
|
|
|
|
|
2022-03-11 17:34:59 +00:00
|
|
|
assert(ent->s.eType == game::native::ET_PLAYER);
|
|
|
|
|
2022-03-11 14:30:00 +00:00
|
|
|
ent->client->flags ^= 1;
|
|
|
|
|
|
|
|
const auto* msg = (ent->client->flags & 1) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF";
|
|
|
|
printf("%s\n", game::native::SEH_LocalizeTextMessage(msg, "noclip print", game::native::LOCMSG_SAFE));
|
|
|
|
});
|
|
|
|
|
2022-05-21 21:39:22 +02:00
|
|
|
add("ufo", []
|
2022-03-11 14:30:00 +00:00
|
|
|
{
|
2022-05-13 10:59:11 +01:00
|
|
|
if (!game::native::sp::IsServerRunning())
|
2022-03-14 23:54:41 +00:00
|
|
|
return;
|
|
|
|
|
2022-03-11 14:30:00 +00:00
|
|
|
const auto* ent = &game::native::sp::g_entities[0];
|
|
|
|
if (ent->health < 1)
|
|
|
|
return;
|
|
|
|
|
2022-03-11 17:34:59 +00:00
|
|
|
assert(ent->s.eType == game::native::ET_PLAYER);
|
|
|
|
|
2022-03-11 14:30:00 +00:00
|
|
|
ent->client->flags ^= 2;
|
|
|
|
|
|
|
|
const auto* msg = (ent->client->flags & 2) ? "GAME_UFOON" : "GAME_UFOOFF";
|
|
|
|
printf("%s\n", game::native::SEH_LocalizeTextMessage(msg, "ufo print", game::native::LOCMSG_SAFE));
|
|
|
|
});
|
|
|
|
|
2022-05-21 21:39:22 +02:00
|
|
|
add("god", []
|
2022-03-11 14:30:00 +00:00
|
|
|
{
|
2022-05-13 10:59:11 +01:00
|
|
|
if (!game::native::sp::IsServerRunning())
|
2022-03-14 23:54:41 +00:00
|
|
|
return;
|
|
|
|
|
2022-03-11 14:30:00 +00:00
|
|
|
auto* ent = &game::native::sp::g_entities[0];
|
|
|
|
if (ent->health < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
assert(ent->s.eType == game::native::ET_PLAYER);
|
|
|
|
|
|
|
|
ent->flags ^= game::native::FL_GODMODE;
|
|
|
|
|
|
|
|
const auto* msg = (ent->flags & game::native::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF";
|
|
|
|
printf("%s\n", game::native::SEH_LocalizeTextMessage(msg, "god print", game::native::LOCMSG_SAFE));
|
|
|
|
});
|
|
|
|
|
2022-05-21 21:39:22 +02:00
|
|
|
add("demigod", []
|
2022-03-11 14:30:00 +00:00
|
|
|
{
|
2022-05-13 10:59:11 +01:00
|
|
|
if (!game::native::sp::IsServerRunning())
|
2022-03-14 23:54:41 +00:00
|
|
|
return;
|
|
|
|
|
2022-03-11 14:30:00 +00:00
|
|
|
auto* ent = &game::native::sp::g_entities[0];
|
|
|
|
if (ent->health < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
assert(ent->s.eType == game::native::ET_PLAYER);
|
|
|
|
|
|
|
|
ent->flags ^= game::native::FL_DEMI_GODMODE;
|
|
|
|
|
|
|
|
const auto* msg = (ent->flags & game::native::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF";
|
|
|
|
printf("%s\n", game::native::SEH_LocalizeTextMessage(msg, "demigod print", game::native::LOCMSG_SAFE));
|
|
|
|
});
|
|
|
|
|
2022-05-21 21:39:22 +02:00
|
|
|
add("notarget", []
|
2022-03-11 14:30:00 +00:00
|
|
|
{
|
2022-05-13 10:59:11 +01:00
|
|
|
if (!game::native::sp::IsServerRunning())
|
2022-03-14 23:54:41 +00:00
|
|
|
return;
|
|
|
|
|
2022-03-11 14:30:00 +00:00
|
|
|
auto* ent = &game::native::sp::g_entities[0];
|
|
|
|
if (ent->health < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
assert(ent->s.eType == game::native::ET_PLAYER);
|
|
|
|
|
|
|
|
ent->flags ^= game::native::FL_NOTARGET;
|
|
|
|
|
|
|
|
const auto* msg = (ent->flags & game::native::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF";
|
|
|
|
printf("%s\n", game::native::SEH_LocalizeTextMessage(msg, "notarget print", game::native::LOCMSG_SAFE));
|
|
|
|
});
|
2022-08-11 23:04:29 +02:00
|
|
|
|
|
|
|
add("setviewpos", [](const params& params)
|
|
|
|
{
|
|
|
|
if (!game::native::sp::IsServerRunning())
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto* ent = &game::native::sp::g_entities[0];
|
|
|
|
if (ent->health < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
assert(ent->s.eType == game::native::ET_PLAYER);
|
|
|
|
assert(ent->client);
|
|
|
|
|
2022-08-11 23:13:04 +02:00
|
|
|
game::native::vec3_t origin, angles{0.f, 0.f, 0.f};
|
2022-08-11 23:04:29 +02:00
|
|
|
|
|
|
|
if (params.size() < 4 || params.size() > 6)
|
|
|
|
{
|
|
|
|
printf("setviewpos x y z [yaw] [pitch]\n");
|
|
|
|
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() == 6)
|
|
|
|
{
|
|
|
|
angles[0] = std::strtof(params.get(5), nullptr); // Pitch
|
|
|
|
}
|
|
|
|
|
|
|
|
game::native::sp::TeleportPlayer(ent, origin, angles);
|
|
|
|
});
|
2022-03-11 14:30:00 +00:00
|
|
|
}
|
|
|
|
|
2022-03-10 19:50:36 +00:00
|
|
|
void command::post_load()
|
|
|
|
{
|
2022-03-15 15:41:51 +00:00
|
|
|
add("quit", game::native::Com_Quit_f);
|
|
|
|
|
|
|
|
if (game::is_mp())
|
2022-03-10 19:50:36 +00:00
|
|
|
{
|
2022-12-09 14:50:13 +01:00
|
|
|
utils::hook(0x57192A, &client_command_stub, HOOK_CALL).install()->quick(); // SV_ExecuteClientCommand
|
2022-03-11 14:30:00 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-12-09 14:50:13 +01:00
|
|
|
utils::hook(0x44BB50, &client_command_sp_stub, HOOK_JUMP).install()->quick();
|
2022-03-11 14:30:00 +00:00
|
|
|
utils::hook::nop(0x44BB55, 5); // Nop skipped instructions
|
|
|
|
add_sp_commands();
|
|
|
|
}
|
2022-03-10 19:50:36 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 12:50:34 +01:00
|
|
|
REGISTER_MODULE(command);
|