Rcon
This commit is contained in:
parent
4fe63068fa
commit
e7a0cd99cc
@ -3,7 +3,9 @@
|
|||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
|
#include "rcon.hpp"
|
||||||
|
|
||||||
#include <utils/thread.hpp>
|
#include <utils/thread.hpp>
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
@ -114,6 +116,11 @@ namespace console
|
|||||||
|
|
||||||
int dispatch_message(const int type, const std::string& message)
|
int dispatch_message(const int type, const std::string& message)
|
||||||
{
|
{
|
||||||
|
if (rcon::message_redirect(message))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard _0(print_mutex);
|
std::lock_guard _0(print_mutex);
|
||||||
|
|
||||||
clear_output();
|
clear_output();
|
||||||
|
@ -117,10 +117,13 @@ namespace logger
|
|||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
{
|
{
|
||||||
// lua stuff
|
if (!game::environment::is_dedi())
|
||||||
utils::hook::jump(SELECT_VALUE(0x106010_b, 0x27CBB0_b), print_dev); // debug
|
{
|
||||||
utils::hook::jump(SELECT_VALUE(0x107680_b, 0x27E210_b), print_error); // error
|
// lua stuff
|
||||||
utils::hook::jump(SELECT_VALUE(0x0E6E30_b, 0x1F6140_b), printf); // print
|
utils::hook::jump(SELECT_VALUE(0x106010_b, 0x27CBB0_b), print_dev); // debug
|
||||||
|
utils::hook::jump(SELECT_VALUE(0x107680_b, 0x27E210_b), print_error); // error
|
||||||
|
utils::hook::jump(SELECT_VALUE(0x0E6E30_b, 0x1F6140_b), printf); // print
|
||||||
|
}
|
||||||
|
|
||||||
com_error_hook.create(game::Com_Error, com_error_stub);
|
com_error_hook.create(game::Com_Error, com_error_stub);
|
||||||
}
|
}
|
||||||
|
232
src/client/component/rcon.cpp
Normal file
232
src/client/component/rcon.cpp
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
|
#include "command.hpp"
|
||||||
|
#include "console.hpp"
|
||||||
|
#include "network.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "rcon.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
namespace rcon
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool is_redirecting_ = false;
|
||||||
|
bool has_redirected_ = false;
|
||||||
|
game::netadr_s redirect_target_ = {};
|
||||||
|
std::recursive_mutex redirect_lock;
|
||||||
|
|
||||||
|
void setup_redirect(const game::netadr_s& target)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> $(redirect_lock);
|
||||||
|
|
||||||
|
has_redirected_ = false;
|
||||||
|
is_redirecting_ = true;
|
||||||
|
redirect_target_ = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_redirect()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> $(redirect_lock);
|
||||||
|
|
||||||
|
has_redirected_ = false;
|
||||||
|
is_redirecting_ = false;
|
||||||
|
redirect_target_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_rcon_command(const std::string& password, const std::string& data)
|
||||||
|
{
|
||||||
|
// If you are the server, don't bother with rcon and just execute the command
|
||||||
|
if (game::Dvar_FindVar("sv_running")->current.enabled)
|
||||||
|
{
|
||||||
|
game::Cbuf_AddText(0, 0, data.data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.empty())
|
||||||
|
{
|
||||||
|
console::info("You must login first to use RCON\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*game::mp::connect_state != nullptr && *game::connectionState >= game::CA_CONNECTED)
|
||||||
|
{
|
||||||
|
const auto target = (*game::mp::connect_state)->address;
|
||||||
|
const auto buffer = password + " " + data;
|
||||||
|
network::send(target, "rcon", buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console::warn("You need to be connected to a server!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string build_status_buffer()
|
||||||
|
{
|
||||||
|
const auto sv_maxclients = game::Dvar_FindVar("sv_maxclients");
|
||||||
|
const auto mapname = game::Dvar_FindVar("mapname");
|
||||||
|
|
||||||
|
std::string buffer{};
|
||||||
|
buffer.append(utils::string::va("map: %s\n", mapname->current.string));
|
||||||
|
buffer.append(
|
||||||
|
"num score bot ping guid name address qport\n");
|
||||||
|
buffer.append(
|
||||||
|
"--- ----- --- ---- -------------------------------- ---------------- --------------------- -----\n");
|
||||||
|
|
||||||
|
const auto svs_clients = *game::mp::svs_clients;
|
||||||
|
if (svs_clients == nullptr)
|
||||||
|
{
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < sv_maxclients->current.integer; i++)
|
||||||
|
{
|
||||||
|
const auto client = &svs_clients[i];
|
||||||
|
|
||||||
|
char clean_name[32] = {0};
|
||||||
|
strncpy_s(clean_name, client->name, sizeof(clean_name));
|
||||||
|
game::I_CleanStr(clean_name);
|
||||||
|
|
||||||
|
if (client->header.state >= 1)
|
||||||
|
{
|
||||||
|
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
|
||||||
|
i,
|
||||||
|
game::G_GetClientScore(i),
|
||||||
|
game::SV_BotIsBot(i) ? "Yes" : "No",
|
||||||
|
(client->header.state == 2)
|
||||||
|
? "CNCT"
|
||||||
|
: (client->header.state == 1)
|
||||||
|
? "ZMBI"
|
||||||
|
: utils::string::va("%4i", game::SV_GetClientPing(i)),
|
||||||
|
game::SV_GetGuid(i),
|
||||||
|
clean_name,
|
||||||
|
network::net_adr_to_string(client->header.remoteAddress),
|
||||||
|
client->header.remoteAddress.port)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool message_redirect(const std::string& message)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> $(redirect_lock);
|
||||||
|
|
||||||
|
if (is_redirecting_)
|
||||||
|
{
|
||||||
|
has_redirected_ = true;
|
||||||
|
network::send(redirect_target_, "print", message, '\n');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
if (game::environment::is_sp())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler::once([]()
|
||||||
|
{
|
||||||
|
dvars::register_string("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE,
|
||||||
|
"The password for remote console");
|
||||||
|
}, scheduler::pipeline::main);
|
||||||
|
|
||||||
|
command::add("status", []()
|
||||||
|
{
|
||||||
|
const auto sv_running = game::Dvar_FindVar("sv_running");
|
||||||
|
if (game::VirtualLobby_Loaded() || !sv_running || !sv_running->current.enabled)
|
||||||
|
{
|
||||||
|
console::error("Server is not running\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto status_buffer = build_status_buffer();
|
||||||
|
console::info(status_buffer.data());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!game::environment::is_dedi())
|
||||||
|
{
|
||||||
|
command::add("rcon", [&](const command::params& params)
|
||||||
|
{
|
||||||
|
static std::string rcon_password{};
|
||||||
|
|
||||||
|
if (params.size() < 2) return;
|
||||||
|
|
||||||
|
const auto operation = params.get(1);
|
||||||
|
if (operation == "login"s)
|
||||||
|
{
|
||||||
|
if (params.size() < 3) return;
|
||||||
|
|
||||||
|
rcon_password = params.get(2);
|
||||||
|
}
|
||||||
|
else if (operation == "logout"s)
|
||||||
|
{
|
||||||
|
rcon_password.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
send_rcon_command(rcon_password, params.join(1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
network::on("rcon", [](const game::netadr_s& addr, const std::string_view& data)
|
||||||
|
{
|
||||||
|
const auto message = std::string{data};
|
||||||
|
const auto pos = message.find_first_of(" ");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
|
network::send(addr, "print", "Invalid RCon request", '\n');
|
||||||
|
console::info("Invalid RCon request from %s\n", network::net_adr_to_string(addr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto password = message.substr(0, pos);
|
||||||
|
const auto command = message.substr(pos + 1);
|
||||||
|
const auto rcon_password = game::Dvar_FindVar("rcon_password");
|
||||||
|
if (command.empty() || !rcon_password || !rcon_password->current.string || !strlen(
|
||||||
|
rcon_password->current.string))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_redirect(addr);
|
||||||
|
|
||||||
|
if (password != rcon_password->current.string)
|
||||||
|
{
|
||||||
|
network::send(redirect_target_, "print", "Invalid rcon password", '\n');
|
||||||
|
console::error("Invalid rcon password\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
command::execute(command, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_redirected_)
|
||||||
|
{
|
||||||
|
network::send(redirect_target_, "print", "", '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_redirect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(rcon::component)
|
6
src/client/component/rcon.hpp
Normal file
6
src/client/component/rcon.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace rcon
|
||||||
|
{
|
||||||
|
bool message_redirect(const std::string& message);
|
||||||
|
}
|
@ -1466,6 +1466,13 @@ namespace game
|
|||||||
int num_players;
|
int num_players;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// made up
|
||||||
|
struct connect_state_t
|
||||||
|
{
|
||||||
|
char __pad0[0xC];
|
||||||
|
netadr_s address;
|
||||||
|
};
|
||||||
|
|
||||||
static_assert(offsetof(client_state_t, ping) == 0x4A50);
|
static_assert(offsetof(client_state_t, ping) == 0x4A50);
|
||||||
static_assert(offsetof(client_state_t, num_players) == 0x4A5C);
|
static_assert(offsetof(client_state_t, num_players) == 0x4A5C);
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ namespace game
|
|||||||
WEAK symbol<int(const char* fname)> generateHashValue{0x11FEA0, 0x183F80};
|
WEAK symbol<int(const char* fname)> generateHashValue{0x11FEA0, 0x183F80};
|
||||||
|
|
||||||
WEAK symbol<void()> G_Glass_Update{0x2992E0, 0x417940};
|
WEAK symbol<void()> G_Glass_Update{0x2992E0, 0x417940};
|
||||||
WEAK symbol<int(int clientNum)> G_GetClientScore{0x0, 0x0};
|
WEAK symbol<int(int clientNum)> G_GetClientScore{0x0, 0x420420};
|
||||||
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x2F20F0, 0x461180};
|
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x2F20F0, 0x461180};
|
||||||
WEAK symbol<int(playerState_s* ps, unsigned int weapon, int dualWield,
|
WEAK symbol<int(playerState_s* ps, unsigned int weapon, int dualWield,
|
||||||
int startInAltMode, int, int, int, char, ...)> G_GivePlayerWeapon{0x2F24F0, 0x461600};
|
int startInAltMode, int, int, int, char, ...)> G_GivePlayerWeapon{0x2F24F0, 0x461600};
|
||||||
@ -241,6 +241,8 @@ namespace game
|
|||||||
WEAK symbol<CmdArgs> cmd_args{0xB48FEE0, 0x2ED1E00};
|
WEAK symbol<CmdArgs> cmd_args{0xB48FEE0, 0x2ED1E00};
|
||||||
WEAK symbol<CmdArgsPrivate> cmd_argsPrivate{0, 0x3513F20};
|
WEAK symbol<CmdArgsPrivate> cmd_argsPrivate{0, 0x3513F20};
|
||||||
|
|
||||||
|
WEAK symbol<int> connectionState{0x0, 0x2EC82C8};
|
||||||
|
|
||||||
WEAK symbol<int> g_poolSize{0x0, 0x0};
|
WEAK symbol<int> g_poolSize{0x0, 0x0};
|
||||||
|
|
||||||
WEAK symbol<scrVarGlob_t> scr_VarGlob{0xBD80E00, 0xB138180};
|
WEAK symbol<scrVarGlob_t> scr_VarGlob{0xBD80E00, 0xB138180};
|
||||||
@ -274,6 +276,7 @@ namespace game
|
|||||||
WEAK symbol<bool> virtualLobby_loaded{0x0, 0x2E6EC9D};
|
WEAK symbol<bool> virtualLobby_loaded{0x0, 0x2E6EC9D};
|
||||||
|
|
||||||
WEAK symbol<client_state_t*> client_state{0x0, 0x2EC84F0};
|
WEAK symbol<client_state_t*> client_state{0x0, 0x2EC84F0};
|
||||||
|
WEAK symbol<connect_state_t*> connect_state{0x0, 0x2EC8510};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace sp
|
namespace sp
|
||||||
|
Loading…
Reference in New Issue
Block a user