Implement rcon support

This fixes #337
This commit is contained in:
Maurice Heumann 2023-03-14 19:12:50 +01:00
parent 91a8b5bf94
commit 85794fd992
7 changed files with 153 additions and 3 deletions

View File

@ -81,6 +81,24 @@ namespace command
assert(this->nesting_ < game::CMD_MAX_NESTING);
}
params::params(const std::string& text)
: needs_end_(true)
{
auto* cmd_args = get_cmd_args();
game::Cmd_TokenizeStringKernel(0, game::CONTROLLER_INDEX_FIRST, text.data(),
512 - cmd_args->totalUsedArgvPool, false, cmd_args);
this->nesting_ = cmd_args->nesting;
}
params::~params()
{
if (this->needs_end_)
{
game::Cmd_EndTokenizedString();
}
}
int params::size() const
{
return get_cmd_args()->argc[this->nesting_];
@ -189,8 +207,10 @@ namespace command
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>());
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>());
}
}

View File

@ -6,6 +6,14 @@ namespace command
{
public:
params();
params(const std::string& text);
~params();
params(params&&) = delete;
params(const params&) = delete;
params& operator=(params&&) = delete;
params& operator=(const params&) = delete;
[[nodiscard]] int size() const;
[[nodiscard]] const char* get(int index) const;
@ -17,6 +25,7 @@ namespace command
}
private:
bool needs_end_{false};
int nesting_;
};

View File

@ -27,6 +27,7 @@ namespace console
utils::image::object logo;
std::atomic_bool started{false};
std::atomic_bool terminate_runner{false};
utils::concurrency::container<std::function<void(const std::string& message)>> interceptor{};
utils::concurrency::container<std::queue<std::string>> message_queue{};
void print_message(const char* message)
@ -43,6 +44,14 @@ namespace console
void queue_message(const char* message)
{
interceptor.access([message](const std::function<void(const std::string&)>& callback)
{
if (callback)
{
callback(message);
}
});
message_queue.access([message](std::queue<std::string>& queue)
{
queue.push(message);
@ -202,6 +211,19 @@ namespace console
}
}
void set_interceptor(std::function<void(const std::string& message)> callback)
{
interceptor.access([&callback](std::function<void(const std::string&)>& c)
{
c = std::move(callback);
});
}
void remove_interceptor()
{
set_interceptor({});
}
struct component final : generic_component
{
void post_unpack() override

View File

@ -3,4 +3,24 @@
namespace console
{
void set_title(const std::string& title);
void set_interceptor(std::function<void(const std::string& message)> callback);
void remove_interceptor();
struct scoped_interceptor
{
scoped_interceptor(std::function<void(const std::string& message)> callback)
{
set_interceptor(std::move(callback));
}
~scoped_interceptor()
{
remove_interceptor();
}
scoped_interceptor(scoped_interceptor&&) = delete;
scoped_interceptor(const scoped_interceptor&) = delete;
scoped_interceptor& operator=(scoped_interceptor&&) = delete;
scoped_interceptor& operator=(const scoped_interceptor&) = delete;
};
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <game/game.hpp>
namespace network
{
using data_view = std::basic_string_view<uint8_t>;

View File

@ -0,0 +1,74 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "network.hpp"
#include "console.hpp"
#include "command.hpp"
#include "scheduler.hpp"
#include <utils/finally.hpp>
#include <utils/string.hpp>
#include <game/utils.hpp>
namespace rcon
{
namespace
{
std::optional<std::string> get_and_validate_rcon_command(const std::string& data)
{
const command::params params{reinterpret_cast<const char*>(data.data())};
if (params.size() <= 1)
{
return {};
}
if (params[0] != game::get_dvar_string("rcon_password"))
{
return {};
}
return params.join(1);
}
void rcon_executer(const game::netadr_t& target, const std::string& data)
{
const auto command = get_and_validate_rcon_command(data);
if (!command)
{
return;
}
std::string console_buffer{};
console::scoped_interceptor _([&console_buffer](const std::string& text)
{
console_buffer += text;
});
game::Cmd_ExecuteSingleCommand(0, game::CONTROLLER_INDEX_FIRST, command->data(), true);
network::send(target, "print", console_buffer);
}
void rcon_handler(const game::netadr_t& target, const network::data_view& data)
{
auto str_data = std::string(reinterpret_cast<const char*>(data.data()), data.size());
scheduler::once([target, s = std::move(str_data)]
{
rcon_executer(target, s);
}, scheduler::main);
}
}
struct component final : server_component
{
void post_unpack() override
{
network::on("rcon", rcon_handler);
}
};
}
REGISTER_COMPONENT(rcon::component)

View File

@ -50,8 +50,11 @@ namespace game
};
WEAK symbol<void(int localClientNum, ControllerIndex_t controllerIndex, const char* text,
bool fromRemoteConsole)> Cmd_ExecuteSingleCommand{
0x1420ED380
0x1420ED380, 0x1404F8890
};
WEAK symbol<void(int localClientNum, ControllerIndex_t localControllerIndex, const char* text_in, int max_tokens,
bool evalExpressions, CmdArgs* args)> Cmd_TokenizeStringKernel{0x1420EED60, 0x1404FA300};
WEAK symbol<void()> Cmd_EndTokenizedString{0x1420ECED0, 0x1404F8420};
WEAK symbol<void(char* text, int maxSize)> Con_GetTextCopy{0x14133A7D0, 0x140182C40};
// DB