2021-09-06 18:40:37 -04:00
|
|
|
#include <std_include.hpp>
|
2021-04-23 14:57:21 -04:00
|
|
|
#include "loader/component_loader.hpp"
|
2021-04-19 18:56:11 -04:00
|
|
|
|
|
|
|
#include "game/game.hpp"
|
2021-04-23 14:57:21 -04:00
|
|
|
#include "game/dvars.hpp"
|
|
|
|
|
2022-01-03 15:05:46 -05:00
|
|
|
#include "game/scripting/execution.hpp"
|
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
#include "command.hpp"
|
2022-01-03 17:32:40 -05:00
|
|
|
#include "scheduler.hpp"
|
2021-04-23 14:57:21 -04:00
|
|
|
#include "game_console.hpp"
|
|
|
|
#include "chat.hpp"
|
2022-01-02 17:51:38 -05:00
|
|
|
#include "fastfiles.hpp"
|
2021-04-19 18:56:11 -04:00
|
|
|
|
|
|
|
#include <utils/hook.hpp>
|
|
|
|
#include <utils/string.hpp>
|
|
|
|
#include <utils/memory.hpp>
|
2022-01-27 18:27:53 -05:00
|
|
|
#include <utils/io.hpp>
|
2021-04-19 18:56:11 -04:00
|
|
|
|
|
|
|
namespace command
|
|
|
|
{
|
|
|
|
namespace
|
|
|
|
{
|
2021-04-22 21:46:11 -04:00
|
|
|
utils::hook::detour dvar_command_hook;
|
|
|
|
|
2021-04-19 18:56:11 -04:00
|
|
|
std::unordered_map<std::string, std::function<void(params&)>> handlers;
|
|
|
|
|
|
|
|
void main_handler()
|
|
|
|
{
|
|
|
|
params params = {};
|
|
|
|
|
|
|
|
const auto command = utils::string::to_lower(params[0]);
|
|
|
|
if (handlers.find(command) != handlers.end())
|
|
|
|
{
|
|
|
|
handlers[command](params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-22 21:46:11 -04:00
|
|
|
game::dvar_t* dvar_command_stub()
|
|
|
|
{
|
|
|
|
const params args;
|
|
|
|
|
|
|
|
if (args.size() <= 0)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto dvar = game::Dvar_FindVar(args[0]);
|
|
|
|
|
|
|
|
if (dvar)
|
|
|
|
{
|
|
|
|
if (args.size() == 1)
|
|
|
|
{
|
|
|
|
const auto current = game::Dvar_ValueToString(dvar, nullptr, &dvar->current);
|
|
|
|
const auto reset = game::Dvar_ValueToString(dvar, nullptr, &dvar->reset);
|
|
|
|
|
2021-12-21 09:43:51 -05:00
|
|
|
game_console::print(game_console::con_type_info, "\"%s\" is: \"%s\" default: \"%s\" hash: 0x%08lX",
|
2021-04-22 21:46:11 -04:00
|
|
|
args[0], current, reset, dvar->name);
|
|
|
|
|
|
|
|
game_console::print(game_console::con_type_info, " %s\n",
|
|
|
|
dvars::dvar_get_domain(dvar->type, dvar->domain).data());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char command[0x1000] = { 0 };
|
|
|
|
game::Dvar_GetCombinedString(command, 1);
|
|
|
|
game::Dvar_SetCommand(dvar->name, "", command);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dvar;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2021-04-19 18:56:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
params::params()
|
|
|
|
: nesting_(game::cmd_args->nesting)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int params::size() const
|
|
|
|
{
|
|
|
|
return game::cmd_args->argc[this->nesting_];
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* params::get(const int index) const
|
|
|
|
{
|
|
|
|
if (index >= this->size())
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return game::cmd_args->argv[this->nesting_][index];
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_raw(const char* name, void (*callback)())
|
|
|
|
{
|
|
|
|
game::Cmd_AddCommandInternal(name, callback, utils::memory::get_allocator()->allocate<game::cmd_function_s>());
|
|
|
|
}
|
|
|
|
|
|
|
|
void add(const char* name, const std::function<void(const 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 add(const char* name, const std::function<void()>& callback)
|
|
|
|
{
|
|
|
|
add(name, [callback](const params&)
|
|
|
|
{
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void execute(std::string command, const bool sync)
|
|
|
|
{
|
|
|
|
command += "\n";
|
|
|
|
|
|
|
|
if (sync)
|
|
|
|
{
|
|
|
|
game::Cmd_ExecuteSingleCommand(0, 0, command.data());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game::Cbuf_AddText(0, command.data());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
class component final : public component_interface
|
2021-04-19 18:56:11 -04:00
|
|
|
{
|
2021-04-23 14:57:21 -04:00
|
|
|
public:
|
|
|
|
void post_unpack() override
|
2021-04-23 10:45:33 -04:00
|
|
|
{
|
2022-03-18 17:02:44 -04:00
|
|
|
utils::hook::jump(0x1405A74F0, dvar_command_stub, true);
|
2021-04-23 10:45:33 -04:00
|
|
|
|
2021-12-27 22:25:14 -05:00
|
|
|
add("quit", game::Quit);
|
2021-05-09 02:37:02 -04:00
|
|
|
|
2021-04-25 14:18:28 -04:00
|
|
|
add("startmap", [](const params& params)
|
|
|
|
{
|
|
|
|
const auto map = params.get(1);
|
|
|
|
|
2022-03-18 17:02:44 -04:00
|
|
|
const auto exists = utils::hook::invoke<bool>(0x140412B50, map, 0);
|
2021-04-25 14:18:28 -04:00
|
|
|
|
|
|
|
if (!exists)
|
|
|
|
{
|
|
|
|
game_console::print(game_console::con_type_error, "map '%s' not found\n", map);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// SV_SpawnServer
|
2022-03-18 17:02:44 -04:00
|
|
|
utils::hook::invoke<void>(0x1406B3AA0, map, 0, 0, 0, 0);
|
2021-04-25 14:18:28 -04:00
|
|
|
});
|
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
add("say", [](const params& params)
|
2021-04-19 18:56:11 -04:00
|
|
|
{
|
2021-04-23 14:57:21 -04:00
|
|
|
chat::print(params.join(1));
|
|
|
|
});
|
2021-04-19 18:56:11 -04:00
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
add("listassetpool", [](const params& params)
|
2021-04-19 18:56:11 -04:00
|
|
|
{
|
2021-04-23 14:57:21 -04:00
|
|
|
if (params.size() < 2)
|
2021-04-19 18:56:11 -04:00
|
|
|
{
|
2021-04-23 14:57:21 -04:00
|
|
|
game_console::print(game_console::con_type_info, "listassetpool <poolnumber>: list all the assets in the specified pool\n");
|
2021-04-19 18:56:11 -04:00
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
|
|
|
|
{
|
|
|
|
game_console::print(game_console::con_type_info, "%d %s\n", i, game::g_assetNames[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2021-04-19 18:56:11 -04:00
|
|
|
{
|
2021-04-23 14:57:21 -04:00
|
|
|
const auto type = static_cast<game::XAssetType>(atoi(params.get(1)));
|
|
|
|
|
|
|
|
if (type < 0 || type >= game::XAssetType::ASSET_TYPE_COUNT)
|
|
|
|
{
|
|
|
|
game_console::print(game_console::con_type_info, "Invalid pool passed must be between [%d, %d]\n", 0, game::XAssetType::ASSET_TYPE_COUNT - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
game_console::print(game_console::con_type_info, "Listing assets in pool %s\n", game::g_assetNames[type]);
|
|
|
|
|
2022-01-02 17:51:38 -05:00
|
|
|
fastfiles::enum_assets(type, [type](const game::XAssetHeader header)
|
2021-04-23 14:57:21 -04:00
|
|
|
{
|
2021-04-24 10:22:26 -04:00
|
|
|
const auto asset = game::XAsset{type, header};
|
2021-04-23 14:57:21 -04:00
|
|
|
const auto* const asset_name = game::DB_GetXAssetName(&asset);
|
|
|
|
//const auto entry = game::DB_FindXAssetEntry(type, asset_name);
|
|
|
|
//TODO: display which zone the asset is from
|
|
|
|
game_console::print(game_console::con_type_info, "%s\n", asset_name);
|
|
|
|
}, true);
|
2021-04-19 18:56:11 -04:00
|
|
|
}
|
2021-04-23 14:57:21 -04:00
|
|
|
});
|
2021-04-19 18:56:11 -04:00
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
add("commandDump", []()
|
2021-04-19 18:56:11 -04:00
|
|
|
{
|
2021-04-23 14:57:21 -04:00
|
|
|
printf("======== Start command dump =========\n");
|
2021-04-19 18:56:11 -04:00
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
game::cmd_function_s* cmd = (*game::cmd_functions);
|
2021-04-19 18:56:11 -04:00
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
while (cmd)
|
|
|
|
{
|
|
|
|
if (cmd->name)
|
|
|
|
{
|
|
|
|
game_console::print(game_console::con_type_info, "%s\n", cmd->name);
|
|
|
|
}
|
2021-04-19 18:56:11 -04:00
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
cmd = cmd->next;
|
|
|
|
}
|
2021-04-19 18:56:11 -04:00
|
|
|
|
2021-04-23 14:57:21 -04:00
|
|
|
printf("======== End command dump =========\n");
|
|
|
|
});
|
2021-05-09 02:37:02 -04:00
|
|
|
|
|
|
|
add("god", []()
|
|
|
|
{
|
|
|
|
if (!game::SV_Loaded())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-23 11:10:58 -04:00
|
|
|
game::g_entities[0].flags ^= game::FL_GODMODE;
|
2021-05-09 02:37:02 -04:00
|
|
|
game::CG_GameMessage(0, utils::string::va("godmode %s",
|
2021-08-23 11:10:58 -04:00
|
|
|
game::g_entities[0].flags & game::FL_GODMODE
|
2021-05-09 02:37:02 -04:00
|
|
|
? "^2on"
|
|
|
|
: "^1off"));
|
|
|
|
});
|
|
|
|
|
|
|
|
add("demigod", []()
|
|
|
|
{
|
|
|
|
if (!game::SV_Loaded())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-23 11:10:58 -04:00
|
|
|
game::g_entities[0].flags ^= game::FL_DEMI_GODMODE;
|
2021-05-09 02:37:02 -04:00
|
|
|
game::CG_GameMessage(0, utils::string::va("demigod mode %s",
|
2021-08-23 11:10:58 -04:00
|
|
|
game::g_entities[0].flags & game::FL_DEMI_GODMODE
|
|
|
|
? "^2on"
|
|
|
|
: "^1off"));
|
|
|
|
});
|
|
|
|
|
|
|
|
add("notarget", []()
|
|
|
|
{
|
|
|
|
if (!game::SV_Loaded())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
game::g_entities[0].flags ^= game::FL_NOTARGET;
|
|
|
|
game::CG_GameMessage(0, utils::string::va("notarget %s",
|
|
|
|
game::g_entities[0].flags & game::FL_NOTARGET
|
2021-05-09 02:37:02 -04:00
|
|
|
? "^2on"
|
|
|
|
: "^1off"));
|
|
|
|
});
|
|
|
|
|
|
|
|
add("noclip", []()
|
|
|
|
{
|
|
|
|
if (!game::SV_Loaded())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
game::g_entities[0].client->flags ^= 1;
|
|
|
|
game::CG_GameMessage(0, utils::string::va("noclip %s",
|
|
|
|
game::g_entities[0].client->flags & 1
|
|
|
|
? "^2on"
|
|
|
|
: "^1off"));
|
|
|
|
});
|
|
|
|
|
|
|
|
add("ufo", []()
|
|
|
|
{
|
|
|
|
if (!game::SV_Loaded())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
game::g_entities[0].client->flags ^= 2;
|
|
|
|
game::CG_GameMessage(
|
|
|
|
0, utils::string::va("ufo %s", game::g_entities[0].client->flags & 2 ? "^2on" : "^1off"));
|
|
|
|
});
|
2021-07-29 22:13:43 -04:00
|
|
|
|
|
|
|
add("give", [](const params& params)
|
|
|
|
{
|
|
|
|
if (!game::SV_Loaded())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params.size() < 2)
|
|
|
|
{
|
|
|
|
game::CG_GameMessage(0, "You did not specify a weapon name");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-03 15:05:46 -05:00
|
|
|
try
|
|
|
|
{
|
|
|
|
const auto arg = params.get(1);
|
|
|
|
const scripting::entity player = scripting::call("getentbynum", {0}).as<scripting::entity>();
|
|
|
|
auto ps = game::g_entities[0].client;
|
|
|
|
|
|
|
|
if (arg == "ammo"s)
|
|
|
|
{
|
|
|
|
const auto weapon = player.call("getcurrentweapon").as<std::string>();
|
|
|
|
player.call("givemaxammo", {weapon});
|
|
|
|
}
|
|
|
|
else if (arg == "allammo"s)
|
|
|
|
{
|
|
|
|
const auto weapons = player.call("getweaponslist").as<scripting::array>();
|
|
|
|
for (auto i = 0; i < weapons.size(); i++)
|
|
|
|
{
|
|
|
|
player.call("givemaxammo", {weapons[i]});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (arg == "health"s)
|
|
|
|
{
|
2022-03-13 14:35:38 -04:00
|
|
|
if (params.size() > 2)
|
2022-01-03 15:05:46 -05:00
|
|
|
{
|
|
|
|
const auto amount = atoi(params.get(2));
|
|
|
|
const auto health = player.get("health").as<int>();
|
|
|
|
player.set("health", {health + amount});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto amount = game::Dvar_FindVar("g_player_maxhealth")->current.integer;
|
|
|
|
player.set("health", {amount});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (arg == "all"s)
|
|
|
|
{
|
|
|
|
const auto type = game::XAssetType::ASSET_TYPE_WEAPON;
|
|
|
|
fastfiles::enum_assets(type, [&player, type](const game::XAssetHeader header)
|
|
|
|
{
|
|
|
|
const auto asset = game::XAsset{type, header};
|
|
|
|
const auto* const asset_name = game::DB_GetXAssetName(&asset);
|
|
|
|
|
|
|
|
player.call("giveweapon", {asset_name});
|
|
|
|
}, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto wp = game::G_GetWeaponForName(arg);
|
|
|
|
if (wp)
|
|
|
|
{
|
|
|
|
if (game::G_GivePlayerWeapon(ps, wp, 0, 0, 0, 0))
|
|
|
|
{
|
|
|
|
game::G_InitializeAmmo(ps, wp, 0);
|
|
|
|
game::G_SelectWeapon(0, wp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game::CG_GameMessage(0, "Weapon does not exist");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
add("dropweapon", [](const params& params)
|
|
|
|
{
|
|
|
|
if (!game::SV_Loaded())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
const scripting::entity player = scripting::call("getentbynum", {0}).as<scripting::entity>();
|
|
|
|
const auto weapon = player.call("getcurrentweapon");
|
|
|
|
player.call("dropitem", {weapon});
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
add("take", [](const params& params)
|
|
|
|
{
|
|
|
|
if (!game::SV_Loaded())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params.size() < 2)
|
|
|
|
{
|
|
|
|
game::CG_GameMessage(0, "You did not specify a weapon name");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto weapon = params.get(1);
|
|
|
|
|
|
|
|
try
|
2021-07-29 22:13:43 -04:00
|
|
|
{
|
2022-01-03 15:05:46 -05:00
|
|
|
const scripting::entity player = scripting::call("getentbynum", {0}).as<scripting::entity>();
|
|
|
|
if (weapon == "all"s)
|
|
|
|
{
|
|
|
|
player.call("takeallweapons");
|
|
|
|
}
|
|
|
|
else
|
2021-07-29 22:13:43 -04:00
|
|
|
{
|
2022-01-03 15:05:46 -05:00
|
|
|
player.call("takeweapon", {weapon});
|
2021-07-29 22:13:43 -04:00
|
|
|
}
|
|
|
|
}
|
2022-01-03 15:05:46 -05:00
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
2021-07-29 22:13:43 -04:00
|
|
|
});
|
2022-01-03 17:32:40 -05:00
|
|
|
|
|
|
|
add("kill", [](const params& params)
|
|
|
|
{
|
|
|
|
if (!game::SV_Loaded())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scheduler::once([]()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
const scripting::entity player = scripting::call("getentbynum", {0}).as<scripting::entity>();
|
|
|
|
player.call("kill");
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}, scheduler::pipeline::server);
|
|
|
|
});
|
2021-04-23 14:57:21 -04:00
|
|
|
}
|
|
|
|
};
|
2021-04-19 18:56:11 -04:00
|
|
|
}
|
2021-04-23 14:57:21 -04:00
|
|
|
|
|
|
|
REGISTER_COMPONENT(command::component)
|