mirror of
https://github.com/diamante0018/MW3ServerFreezer.git
synced 2025-06-30 16:21:52 +00:00
Refactor this (tekno gods dont sue please)
This commit is contained in:
99
src/client/component/cheats.cpp
Normal file
99
src/client/component/cheats.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "key_catcher.hpp"
|
||||
#include "command.hpp"
|
||||
|
||||
namespace cheats {
|
||||
game::dvar_t* cl_EnableCheats;
|
||||
|
||||
__declspec(naked) void draw_red_box_stub() {
|
||||
__asm {
|
||||
push eax
|
||||
mov eax, cl_EnableCheats
|
||||
cmp byte ptr [eax + 12], 1
|
||||
pop eax
|
||||
|
||||
je draw
|
||||
|
||||
test byte ptr ds:0x8FF110, 0x10
|
||||
|
||||
push 0x430568
|
||||
retn
|
||||
|
||||
draw:
|
||||
push 0x43056A
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void blind_eye_check_stub() {
|
||||
__asm {
|
||||
push eax
|
||||
mov eax, cl_EnableCheats
|
||||
cmp byte ptr [eax + 12], 1
|
||||
pop eax
|
||||
|
||||
je draw
|
||||
|
||||
test byte ptr [esi], 0x20
|
||||
jnz skip_because_blindeye
|
||||
|
||||
jmp draw
|
||||
|
||||
skip_because_blindeye:
|
||||
push 0x5AA5A2
|
||||
retn
|
||||
|
||||
draw:
|
||||
push 0x5AA529
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override {
|
||||
cl_EnableCheats = game::Dvar_RegisterBool(
|
||||
"cl_EnableCheats", false, game::DVAR_NONE, "Enable FoF wallhack");
|
||||
|
||||
utils::hook::jump(0x430561, draw_red_box_stub);
|
||||
utils::hook::nop(0x430566, 2);
|
||||
|
||||
utils::hook::jump(0x5AA524, blind_eye_check_stub);
|
||||
|
||||
add_cheat_commands();
|
||||
}
|
||||
|
||||
private:
|
||||
static void add_cheat_commands() {
|
||||
key_catcher::on_key_press(
|
||||
"Z", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
||||
game::Dvar_SetBool(cl_EnableCheats, true);
|
||||
});
|
||||
|
||||
key_catcher::on_key_press(
|
||||
"X", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
||||
game::Dvar_SetBool(cl_EnableCheats, false);
|
||||
});
|
||||
|
||||
key_catcher::on_key_press(
|
||||
"Y", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
||||
command::execute(
|
||||
utils::string::va("cmd mr %i 2 allies", *game::serverId), true);
|
||||
});
|
||||
|
||||
key_catcher::on_key_press(
|
||||
"8", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
||||
command::execute(
|
||||
utils::string::va("cmd mr %i -1 endround", *game::serverId),
|
||||
true);
|
||||
});
|
||||
}
|
||||
};
|
||||
} // namespace cheats
|
||||
|
||||
REGISTER_COMPONENT(cheats::component)
|
93
src/client/component/command.cpp
Normal file
93
src/client/component/command.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
|
||||
constexpr auto CMD_MAX_NESTING = 8;
|
||||
|
||||
namespace command {
|
||||
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.contains(command)) {
|
||||
handlers[command](params);
|
||||
}
|
||||
}
|
||||
|
||||
params::params() : nesting_(game::cmd_args->nesting) {
|
||||
assert(game::cmd_args->nesting < CMD_MAX_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_t>());
|
||||
}
|
||||
|
||||
void add(const char* name, const std::function<void(const params&)>& callback) {
|
||||
const auto command = utils::string::to_lower(name);
|
||||
|
||||
if (!handlers.contains(command)) {
|
||||
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(game::LocalClientNum_t::LOCAL_CLIENT_0, 0,
|
||||
command.data());
|
||||
} else {
|
||||
game::Cbuf_AddText(game::LocalClientNum_t::LOCAL_CLIENT_0, command.data());
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override { add_commands_generic(); }
|
||||
|
||||
private:
|
||||
static void add_commands_generic() {
|
||||
// Will cause blue screen
|
||||
add("quitMeme", utils::nt::raise_hard_exception);
|
||||
add("quit", game::Com_Quit_f);
|
||||
}
|
||||
};
|
||||
} // namespace command
|
||||
|
||||
REGISTER_COMPONENT(command::component)
|
23
src/client/component/command.hpp
Normal file
23
src/client/component/command.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
namespace command {
|
||||
class params {
|
||||
public:
|
||||
params();
|
||||
|
||||
[[nodiscard]] int size() const;
|
||||
[[nodiscard]] const char* get(int index) const;
|
||||
[[nodiscard]] std::string join(int index) const;
|
||||
|
||||
const char* operator[](const int index) const { return this->get(index); }
|
||||
|
||||
private:
|
||||
int nesting_;
|
||||
};
|
||||
|
||||
void add_raw(const char* name, void (*callback)());
|
||||
void add(const char* name, const std::function<void(const params&)>& callback);
|
||||
void add(const char* name, const std::function<void()>& callback);
|
||||
|
||||
void execute(std::string command, bool sync = false);
|
||||
} // namespace command
|
42
src/client/component/console.cpp
Normal file
42
src/client/component/console.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
namespace console {
|
||||
namespace {
|
||||
std::thread thread;
|
||||
std::thread::id async_thread_id;
|
||||
|
||||
LRESULT __stdcall sys_start_console(HWND, UINT, WPARAM, LPARAM) {
|
||||
game::Sys_ShowConsole();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void console_unlock() {
|
||||
const auto callback = SetWindowLongA(
|
||||
*game::g_wv_hWnd, GWL_WNDPROC, reinterpret_cast<LONG>(sys_start_console));
|
||||
|
||||
SendMessageA(*game::g_wv_hWnd, WM_QUIT, 0, 0);
|
||||
SetWindowLongA(*game::g_wv_hWnd, GWL_WNDPROC, callback);
|
||||
}
|
||||
|
||||
void show_console() {
|
||||
if (*game::s_wcd_hWnd) {
|
||||
ShowWindow(*game::s_wcd_hWnd, SW_SHOW);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override {
|
||||
thread = std::thread([]() {
|
||||
console_unlock();
|
||||
show_console();
|
||||
});
|
||||
|
||||
async_thread_id = thread.get_id();
|
||||
}
|
||||
};
|
||||
} // namespace console
|
||||
|
||||
REGISTER_COMPONENT(console::component)
|
18
src/client/component/dvar_patches.cpp
Normal file
18
src/client/component/dvar_patches.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace dvar_patches {
|
||||
void dvar_set_from_string_by_name_stub(const char* /*dvarName*/,
|
||||
const char* /*string*/) {}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override {
|
||||
utils::hook::call(0x59C0EF, dvar_set_from_string_by_name_stub);
|
||||
}
|
||||
};
|
||||
} // namespace dvar_patches
|
||||
|
||||
REGISTER_COMPONENT(dvar_patches::component)
|
108
src/client/component/exploit.cpp
Normal file
108
src/client/component/exploit.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "command.hpp"
|
||||
#include "key_catcher.hpp"
|
||||
|
||||
namespace exploit {
|
||||
game::dvar_t* cl_exploit;
|
||||
|
||||
/*
|
||||
* void CL_Netchan_Transmit(netchan_t* chan, unsigned char* data, int a3)
|
||||
* A brief description of data: the first few bytes contain information from
|
||||
* clientConnection_t structure Offset 0: ServerID Size : 1 Offset 1:
|
||||
* serverMessageSequence Size: 4 Offset 5: serverCommandSequence Size: 4 One
|
||||
* clean way of sending invalid data to the server is to hook the functions
|
||||
* that write the info to the packet Credit:
|
||||
* https://stackoverflow.com/questions/58981714/how-do-i-change-the-value-of-a-single-byte-in-a-uint32-t-variable
|
||||
*/
|
||||
|
||||
void write_message_sequence(game::msg_t* msg, int data) {
|
||||
if (msg->maxsize - static_cast<unsigned int>(msg->cursize) < sizeof(int)) {
|
||||
msg->overflowed = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cl_exploit->current.enabled)
|
||||
data = (data & 0xFFFFFF00) | 0xAAu;
|
||||
|
||||
auto* dest = reinterpret_cast<int*>(&msg->data[msg->cursize]);
|
||||
*dest = data;
|
||||
msg->cursize += sizeof(int);
|
||||
}
|
||||
|
||||
void write_command_sequence(game::msg_t* msg, int data) {
|
||||
if (msg->maxsize - static_cast<unsigned int>(msg->cursize) < sizeof(int)) {
|
||||
msg->overflowed = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cl_exploit->current.enabled)
|
||||
data = (data & 0x00FFFFFF) | (0x80u << 24);
|
||||
|
||||
auto* dest = reinterpret_cast<int*>(&msg->data[msg->cursize]);
|
||||
*dest = data;
|
||||
msg->cursize += sizeof(int);
|
||||
}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override {
|
||||
cl_exploit = game::Dvar_RegisterBool("cl_exploit", false, game::DVAR_NONE,
|
||||
"Enable server freezer");
|
||||
|
||||
add_exploit_commands();
|
||||
add_key_hooks();
|
||||
|
||||
utils::hook::call(0x420B76, write_message_sequence);
|
||||
utils::hook::call(0x420B86, write_command_sequence);
|
||||
}
|
||||
|
||||
private:
|
||||
static void add_key_hooks() {
|
||||
key_catcher::on_key_press(
|
||||
"O", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
||||
command::execute("exploit");
|
||||
});
|
||||
|
||||
key_catcher::on_key_press(
|
||||
"L", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
||||
command::execute("undo_exploit");
|
||||
});
|
||||
|
||||
key_catcher::on_key_press(
|
||||
"K", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
||||
command::execute("disconnect");
|
||||
});
|
||||
}
|
||||
|
||||
static void add_exploit_commands() {
|
||||
command::add("exploit", []([[maybe_unused]] const command::params& params) {
|
||||
game::Dvar_SetBool(cl_exploit, true);
|
||||
});
|
||||
|
||||
command::add("undo_exploit",
|
||||
[]([[maybe_unused]] const command::params& params) {
|
||||
game::Dvar_SetBool(cl_exploit, false);
|
||||
});
|
||||
|
||||
command::add(
|
||||
"send_command", []([[maybe_unused]] const command::params& params) {
|
||||
if (params.size() < 2)
|
||||
return;
|
||||
|
||||
if (*game::connectionState <= game::connstate_t::CA_CHALLENGING)
|
||||
return;
|
||||
|
||||
const auto cmd = std::format("queryserverinfo ;{}", params.join(1));
|
||||
game::NET_OutOfBandPrint(game::NS_SERVER,
|
||||
game::localClientConnection->serverAddress,
|
||||
cmd.data());
|
||||
});
|
||||
}
|
||||
};
|
||||
} // namespace exploit
|
||||
|
||||
REGISTER_COMPONENT(exploit::component)
|
53
src/client/component/key_catcher.cpp
Normal file
53
src/client/component/key_catcher.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "key_catcher.hpp"
|
||||
|
||||
namespace key_catcher {
|
||||
utils::hook::detour cl_key_event_hook;
|
||||
|
||||
namespace {
|
||||
std::unordered_map<std::string, key_catcher::callback>& get_key_callbacks() {
|
||||
static std::unordered_map<std::string, key_catcher::callback> key_callbacks{};
|
||||
return key_callbacks;
|
||||
}
|
||||
|
||||
void handle_key_event(game::LocalClientNum_t local_client, int key_id) {
|
||||
const auto result = VkKeyScanA(static_cast<CHAR>(key_id));
|
||||
const auto vk_key = LOBYTE(result);
|
||||
const auto& callbacks = get_key_callbacks();
|
||||
|
||||
for (auto const& [key, value] : callbacks) {
|
||||
const auto game_vk_key = game::Key_StringToKeynum(key.data());
|
||||
if (static_cast<BYTE>(game_vk_key) == vk_key) {
|
||||
value(local_client);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void on_key_press(const std::string& command, const callback& callback) {
|
||||
get_key_callbacks()[command] = callback;
|
||||
}
|
||||
|
||||
void cl_key_event_stub(game::LocalClientNum_t local_client, int key_id,
|
||||
int a3) {
|
||||
handle_key_event(local_client, key_id);
|
||||
|
||||
cl_key_event_hook.invoke<void>(local_client, key_id, a3);
|
||||
}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override {
|
||||
cl_key_event_hook.create(0x4CD840, &cl_key_event_stub);
|
||||
}
|
||||
|
||||
void pre_destroy() override { cl_key_event_hook.clear(); }
|
||||
};
|
||||
} // namespace key_catcher
|
||||
|
||||
REGISTER_COMPONENT(key_catcher::component)
|
7
src/client/component/key_catcher.hpp
Normal file
7
src/client/component/key_catcher.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace key_catcher {
|
||||
using callback = std::function<void(game::LocalClientNum_t& local_client)>;
|
||||
|
||||
void on_key_press(const std::string& command, const callback& callback);
|
||||
} // namespace key_catcher
|
69
src/client/component/network.cpp
Normal file
69
src/client/component/network.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "network.hpp"
|
||||
#include "command.hpp"
|
||||
|
||||
namespace network {
|
||||
namespace {
|
||||
std::unordered_map<std::string, network::callback>& get_callbacks() {
|
||||
static std::unordered_map<std::string, network::callback> network_callbacks{};
|
||||
return network_callbacks;
|
||||
}
|
||||
|
||||
bool handle_command(game::netadr_s* address, const char* command,
|
||||
game::msg_t* msg) {
|
||||
const auto cmd_string = utils::string::to_lower(command);
|
||||
auto& callbacks = get_callbacks();
|
||||
|
||||
const auto handler = callbacks.find(cmd_string);
|
||||
const auto offset = cmd_string.size() + 5;
|
||||
|
||||
if (static_cast<unsigned int>(msg->cursize) < offset ||
|
||||
handler == callbacks.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string_view data(reinterpret_cast<char*>(msg->data) + offset,
|
||||
msg->cursize - offset);
|
||||
|
||||
handler->second(*address, data);
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int packet_interception_handler(game::netadr_s* from, const char* command,
|
||||
game::msg_t* message) {
|
||||
if (!handle_command(from, command, message)) {
|
||||
return utils::hook::invoke<int>(0x525730, from, command, message);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void on_packet(const std::string& command, const callback& callback) {
|
||||
get_callbacks()[utils::string::to_lower(command)] = callback;
|
||||
}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override {
|
||||
add_network_commands();
|
||||
|
||||
utils::hook::call(0x5B27E1, packet_interception_handler);
|
||||
}
|
||||
|
||||
private:
|
||||
static void add_network_commands() {
|
||||
on_packet("naughty_reply",
|
||||
[](const game::netadr_s&, const std::string_view&) {
|
||||
command::execute("quitMeme");
|
||||
});
|
||||
}
|
||||
};
|
||||
} // namespace network
|
||||
|
||||
REGISTER_COMPONENT(network::component)
|
8
src/client/component/network.hpp
Normal file
8
src/client/component/network.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace network {
|
||||
using callback =
|
||||
std::function<void(const game::netadr_s&, const std::string_view&)>;
|
||||
|
||||
void on_packet(const std::string& command, const callback& callback);
|
||||
} // namespace network
|
69
src/client/component/remove_hooks.cpp
Normal file
69
src/client/component/remove_hooks.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace remove_hooks {
|
||||
namespace {
|
||||
int msg_read_bits_compress_check_sv(const char* from, char* to, int size) {
|
||||
static char buffer[0x8000];
|
||||
|
||||
if (size > 0x800) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = game::MSG_ReadBitsCompress(from, buffer, size);
|
||||
|
||||
if (size > 0x800) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::memcpy(to, buffer, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
int msg_read_bits_compress_check_cl(const char* from, char* to, int size) {
|
||||
static char buffer[0x100000];
|
||||
|
||||
if (size > 0x20000) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = game::MSG_ReadBitsCompress(from, buffer, size);
|
||||
|
||||
if (size > 0x20000) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::memcpy(to, buffer, size);
|
||||
return size;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_start() override { remove_tekno_hooks(); }
|
||||
|
||||
void post_unpack() override {
|
||||
utils::hook::call(0x4E3D42, msg_read_bits_compress_check_sv);
|
||||
utils::hook::call(0x4A9F56, msg_read_bits_compress_check_cl);
|
||||
}
|
||||
|
||||
private:
|
||||
static void remove_tekno_hooks() {
|
||||
utils::hook::set<BYTE>(0x4E3D42, 0xE8);
|
||||
utils::hook::set<BYTE>(0x4E3D43, 0xA9);
|
||||
utils::hook::set<BYTE>(0x4E3D44, 0x25);
|
||||
utils::hook::set<BYTE>(0x4E3D45, 0xFE);
|
||||
utils::hook::set<BYTE>(0x4E3D46, 0xFF);
|
||||
|
||||
utils::hook::set<BYTE>(0x6EA960, 0x55);
|
||||
utils::hook::set<BYTE>(0x6EA961, 0x8B);
|
||||
utils::hook::set<BYTE>(0x6EA962, 0xEC);
|
||||
utils::hook::set<BYTE>(0x6EA963, 0x81);
|
||||
utils::hook::set<BYTE>(0x6EA964, 0xEC);
|
||||
}
|
||||
};
|
||||
} // namespace remove_hooks
|
||||
|
||||
REGISTER_COMPONENT(remove_hooks::component)
|
155
src/client/component/scheduler.cpp
Normal file
155
src/client/component/scheduler.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
#include <std_include.hpp>
|
||||
#include "../loader/component_loader.hpp"
|
||||
|
||||
#include <utils/concurrency.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/thread.hpp>
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace scheduler {
|
||||
namespace {
|
||||
struct task {
|
||||
std::function<bool()> handler{};
|
||||
std::chrono::milliseconds interval{};
|
||||
std::chrono::high_resolution_clock::time_point last_call{};
|
||||
};
|
||||
|
||||
using task_list = std::vector<task>;
|
||||
|
||||
class task_pipeline {
|
||||
public:
|
||||
void add(task&& task) {
|
||||
new_callbacks_.access([&task, this](task_list& tasks) {
|
||||
tasks.emplace_back(std::move(task));
|
||||
});
|
||||
}
|
||||
|
||||
void clear() {
|
||||
callbacks_.access([&](task_list& tasks) {
|
||||
this->merge_callbacks();
|
||||
tasks.clear();
|
||||
});
|
||||
}
|
||||
|
||||
void execute() {
|
||||
callbacks_.access([&](task_list& tasks) {
|
||||
this->merge_callbacks();
|
||||
|
||||
for (auto i = tasks.begin(); i != tasks.end();) {
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const auto diff = now - i->last_call;
|
||||
|
||||
if (diff < i->interval) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->last_call = now;
|
||||
|
||||
const auto res = i->handler();
|
||||
if (res == cond_end) {
|
||||
i = tasks.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
utils::concurrency::container<task_list> new_callbacks_;
|
||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||
|
||||
void merge_callbacks() {
|
||||
callbacks_.access([&](task_list& tasks) {
|
||||
new_callbacks_.access([&](task_list& new_tasks) {
|
||||
tasks.insert(tasks.end(),
|
||||
std::move_iterator<task_list::iterator>(new_tasks.begin()),
|
||||
std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||
new_tasks = {};
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
volatile bool kill = false;
|
||||
std::thread thread;
|
||||
task_pipeline pipelines[pipeline::count];
|
||||
|
||||
void execute(const pipeline type) {
|
||||
assert(type >= 0 && type < pipeline::count);
|
||||
pipelines[type].execute();
|
||||
}
|
||||
|
||||
void cl_frame_stub(game::LocalClientNum_t localClientNum) {
|
||||
utils::hook::invoke<void>(0x41C9B0, localClientNum);
|
||||
execute(pipeline::client);
|
||||
}
|
||||
|
||||
void main_frame_stub() {
|
||||
utils::hook::invoke<void>(0x4E46A0);
|
||||
execute(pipeline::main);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void clear_tasks(const pipeline type) { return pipelines[type].clear(); }
|
||||
|
||||
void schedule(const std::function<bool()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay) {
|
||||
assert(type >= 0 && type < pipeline::count);
|
||||
|
||||
task task;
|
||||
task.handler = callback;
|
||||
task.interval = delay;
|
||||
task.last_call = std::chrono::high_resolution_clock::now();
|
||||
|
||||
pipelines[type].add(std::move(task));
|
||||
}
|
||||
|
||||
void loop(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay) {
|
||||
schedule(
|
||||
[callback]() {
|
||||
callback();
|
||||
return cond_continue;
|
||||
},
|
||||
type, delay);
|
||||
}
|
||||
|
||||
void once(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay) {
|
||||
schedule(
|
||||
[callback]() {
|
||||
callback();
|
||||
return cond_end;
|
||||
},
|
||||
type, delay);
|
||||
}
|
||||
|
||||
unsigned int thread_id;
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_unpack() override {
|
||||
thread = utils::thread::create_named_thread("Async Scheduler", []() {
|
||||
while (!kill) {
|
||||
execute(pipeline::async);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
});
|
||||
|
||||
utils::hook::call(0x4E4A0D, cl_frame_stub);
|
||||
utils::hook::call(0x543B0E, main_frame_stub);
|
||||
}
|
||||
|
||||
void pre_destroy() override {
|
||||
kill = true;
|
||||
if (thread.joinable()) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace scheduler
|
||||
|
||||
REGISTER_COMPONENT(scheduler::component)
|
25
src/client/component/scheduler.hpp
Normal file
25
src/client/component/scheduler.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
namespace scheduler {
|
||||
enum pipeline {
|
||||
client,
|
||||
async,
|
||||
main,
|
||||
count,
|
||||
};
|
||||
|
||||
static const bool cond_continue = false;
|
||||
static const bool cond_end = true;
|
||||
|
||||
void clear_tasks(const pipeline type);
|
||||
|
||||
void schedule(const std::function<bool()>& callback,
|
||||
pipeline type = pipeline::client,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void loop(const std::function<void()>& callback,
|
||||
pipeline type = pipeline::client,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void once(const std::function<void()>& callback,
|
||||
pipeline type = pipeline::client,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
} // namespace scheduler
|
18
src/client/dllmain.cpp
Normal file
18
src/client/dllmain.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "std_include.hpp"
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call,
|
||||
LPVOID /*lpReserved*/
|
||||
) {
|
||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
|
||||
std::srand(uint32_t(time(nullptr)));
|
||||
component_loader::post_start();
|
||||
component_loader::post_unpack();
|
||||
}
|
||||
|
||||
else if (ul_reason_for_call == DLL_PROCESS_DETACH) {
|
||||
component_loader::pre_destroy();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
3
src/client/game/game.cpp
Normal file
3
src/client/game/game.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
namespace game {}
|
19
src/client/game/game.hpp
Normal file
19
src/client/game/game.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
namespace game {
|
||||
template <typename T> class symbol {
|
||||
public:
|
||||
symbol(const size_t dedi) : dedi_(reinterpret_cast<T*>(dedi)) {}
|
||||
|
||||
T* get() const { return dedi_; }
|
||||
|
||||
operator T*() const { return this->get(); }
|
||||
|
||||
T* operator->() const { return this->get(); }
|
||||
|
||||
private:
|
||||
T* dedi_;
|
||||
};
|
||||
} // namespace game
|
||||
|
||||
#include "symbols.hpp"
|
311
src/client/game/structs.hpp
Normal file
311
src/client/game/structs.hpp
Normal file
@ -0,0 +1,311 @@
|
||||
#pragma once
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324)
|
||||
|
||||
namespace game {
|
||||
typedef float vec_t;
|
||||
typedef vec_t vec2_t[2];
|
||||
typedef vec_t vec3_t[3];
|
||||
typedef vec_t vec4_t[4];
|
||||
|
||||
struct cmd_function_t {
|
||||
cmd_function_t* next;
|
||||
const char* name;
|
||||
const char* autoCompleteDir;
|
||||
const char* autoCompleteExt;
|
||||
void(__cdecl* function)();
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct CmdArgs {
|
||||
int nesting;
|
||||
int localClientNum[8];
|
||||
int controllerIndex[8];
|
||||
int argc[8];
|
||||
const char** argv[8];
|
||||
};
|
||||
|
||||
struct kbutton_t {
|
||||
int down[2];
|
||||
unsigned int downtime;
|
||||
unsigned int msec;
|
||||
bool active;
|
||||
bool wasPressed;
|
||||
};
|
||||
|
||||
static_assert(sizeof(kbutton_t) == 20);
|
||||
|
||||
typedef enum {
|
||||
NS_CLIENT1 = 0,
|
||||
NS_CLIENT2 = 1,
|
||||
NS_CLIENT3 = 2,
|
||||
NS_CLIENT4 = 3,
|
||||
NS_MAXCLIENTS = 4,
|
||||
NS_SERVER = 4,
|
||||
NS_PACKET = 5,
|
||||
NS_INVALID_NETSRC = 6
|
||||
} netsrc_t;
|
||||
|
||||
enum netadrtype_t {
|
||||
NA_BOT = 0x0,
|
||||
NA_BAD = 0x1,
|
||||
NA_LOOPBACK = 0x2,
|
||||
NA_BROADCAST = 0x3,
|
||||
NA_IP = 0x4
|
||||
};
|
||||
|
||||
struct netadr_s {
|
||||
netadrtype_t type;
|
||||
unsigned char ip[4];
|
||||
unsigned __int16 port;
|
||||
unsigned char ipx[10];
|
||||
unsigned int addrHandleIndex;
|
||||
};
|
||||
|
||||
static_assert(sizeof(netadr_s) == 24);
|
||||
|
||||
typedef enum {
|
||||
ERR_FATAL = 0x0,
|
||||
ERR_DROP = 0x1,
|
||||
ERR_SERVERDISCONNECT = 0x2,
|
||||
ERR_DISCONNECT = 0x3,
|
||||
ERR_SCRIPT = 0x4,
|
||||
ERR_SCRIPT_DROP = 0x5,
|
||||
ERR_LOCALIZATION = 0x6,
|
||||
ERR_MAPLOADERRORSUMMARY = 0x7
|
||||
} errorParm_t;
|
||||
|
||||
enum class 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
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
CA_DISCONNECTED = 0,
|
||||
CA_CINEMATIC = 1,
|
||||
CA_LOGO = 2,
|
||||
CA_CONNECTING = 3,
|
||||
CA_CHALLENGING = 4,
|
||||
CA_CONNECTED = 5,
|
||||
CA_SENDINGSTATS = 6,
|
||||
CA_REQUESTING_MATCH_RULES = 7,
|
||||
CA_LOADING = 8,
|
||||
CA_PRIMED = 9,
|
||||
CA_ACTIVE = 10
|
||||
} connstate_t;
|
||||
|
||||
struct msg_t {
|
||||
int overflowed;
|
||||
int readOnly;
|
||||
unsigned char* data;
|
||||
unsigned char* splitData;
|
||||
int maxsize;
|
||||
int cursize;
|
||||
int splitSize;
|
||||
int readcount;
|
||||
int bit;
|
||||
int lastEntityRef;
|
||||
};
|
||||
|
||||
struct netProfilePacket_t {
|
||||
int iTime;
|
||||
int iSize;
|
||||
int bFragment;
|
||||
};
|
||||
|
||||
struct netProfileStream_t {
|
||||
netProfilePacket_t packets[60];
|
||||
int iCurrPacket;
|
||||
int iBytesPerSeconds;
|
||||
int iLastBPSCalcTime;
|
||||
int iCountedPackets;
|
||||
int iCountedFragments;
|
||||
int iFragmentPercentage;
|
||||
int iLargestPacket;
|
||||
int iSmallestPacket;
|
||||
};
|
||||
|
||||
struct netProfileInfo_t {
|
||||
netProfileStream_t send;
|
||||
netProfileStream_t recieve;
|
||||
};
|
||||
|
||||
static_assert(sizeof(netProfileInfo_t) == 0x5E0);
|
||||
|
||||
struct netchan_t {
|
||||
int outgoingSequence;
|
||||
netsrc_t sock;
|
||||
int dropped;
|
||||
int incomingSequence;
|
||||
netadr_s remoteAddress;
|
||||
int qport;
|
||||
int fragmentSequence;
|
||||
int fragmentLength;
|
||||
unsigned char* fragmentBuffer;
|
||||
int fragmentBufferSize;
|
||||
int unsentFragments;
|
||||
int unsentFragmentStart;
|
||||
int unsentLength;
|
||||
unsigned char* unsentBuffer;
|
||||
int unsentBufferSize;
|
||||
netProfileInfo_t prof;
|
||||
};
|
||||
|
||||
static_assert(sizeof(netchan_t) == 0x630);
|
||||
|
||||
enum dvar_flags : std::uint16_t {
|
||||
DVAR_NONE = 0x0,
|
||||
DVAR_ARCHIVE = 0x1,
|
||||
DVAR_CHEAT = 0x4,
|
||||
DVAR_CODINFO = 0x8,
|
||||
DVAR_SCRIPTINFO = 0x10,
|
||||
DVAR_SERVERINFO = 0x400,
|
||||
DVAR_WRITEPROTECTED = 0x800,
|
||||
DVAR_READONLY = 0x2000,
|
||||
};
|
||||
|
||||
union DvarValue {
|
||||
bool enabled;
|
||||
int integer;
|
||||
unsigned int unsignedInt;
|
||||
float value;
|
||||
float vector[4];
|
||||
const char* string;
|
||||
char color[4];
|
||||
};
|
||||
|
||||
struct enum_limit {
|
||||
int stringCount;
|
||||
const char** strings;
|
||||
};
|
||||
|
||||
struct int_limit {
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
struct float_limit {
|
||||
float min;
|
||||
float max;
|
||||
};
|
||||
|
||||
union DvarLimits {
|
||||
enum_limit enumeration;
|
||||
int_limit integer;
|
||||
float_limit value;
|
||||
float_limit vector;
|
||||
};
|
||||
|
||||
struct dvar_t {
|
||||
const char* name;
|
||||
unsigned int flags;
|
||||
char type;
|
||||
bool modified;
|
||||
DvarValue current;
|
||||
DvarValue latched;
|
||||
DvarValue reset;
|
||||
DvarLimits domain;
|
||||
bool(__cdecl* domainFunc)(dvar_t*, DvarValue);
|
||||
dvar_t* hashNext;
|
||||
};
|
||||
|
||||
struct usercmd_s {
|
||||
int serverTime;
|
||||
int buttons;
|
||||
int angles[3];
|
||||
unsigned int weapon;
|
||||
unsigned int offHand;
|
||||
char forwardmove;
|
||||
char rightmove;
|
||||
unsigned __int16 airburstMarkDistance;
|
||||
unsigned __int16 meleeChargeEnt;
|
||||
unsigned char meleeChargeDist;
|
||||
char selectedLoc[2];
|
||||
char selectedLocAngle;
|
||||
char remoteControlAngles[2];
|
||||
int remoteControlMove;
|
||||
};
|
||||
|
||||
enum LocSelInputState {
|
||||
LOC_SEL_INPUT_NONE = 0,
|
||||
LOC_SEL_INPUT_CONFIRM = 1,
|
||||
LOC_SEL_INPUT_CANCEL = 2
|
||||
};
|
||||
|
||||
struct field_t {
|
||||
int cursor;
|
||||
int scroll;
|
||||
int drawWidth;
|
||||
int widthInPixels;
|
||||
float charHeight;
|
||||
int fixedSize;
|
||||
char buffer[256];
|
||||
};
|
||||
|
||||
struct KeyState {
|
||||
int down;
|
||||
int repeats;
|
||||
int binding;
|
||||
const char* bindingCheat;
|
||||
};
|
||||
|
||||
static_assert(sizeof(field_t) == 280);
|
||||
|
||||
struct PlayerKeyState {
|
||||
field_t chatField;
|
||||
int chat_team;
|
||||
int overstrikeMode;
|
||||
int anyKeyDown;
|
||||
KeyState keys[256];
|
||||
LocSelInputState locSelInputState;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PlayerKeyState) == 4392);
|
||||
|
||||
enum clientState_t {
|
||||
CS_FREE = 0,
|
||||
CS_ZOMBIE = 1,
|
||||
CS_RECONNECTING = 2,
|
||||
CS_CONNECTED = 3,
|
||||
CS_PRIMED = 4,
|
||||
CS_ACTIVE = 5
|
||||
};
|
||||
|
||||
struct clientConnection_t {
|
||||
int qport; // 0
|
||||
int clientNum; // 4
|
||||
int lastPacketSentTime; // 8
|
||||
int lastPacketTime; // 12
|
||||
netadr_s serverAddress; // 16
|
||||
int connectTime; // 40
|
||||
int connectPacketCount; // 44
|
||||
char serverMessage[256]; // 48
|
||||
int challenge; // 304
|
||||
int checksumFeed; // 308
|
||||
int reliableSequence; // 312
|
||||
int reliableAcknowledge; // 316
|
||||
char reliableCommands[128][1024]; // 320
|
||||
int serverMessageSequence; // 131392
|
||||
int serverCommandSequence; // 131396
|
||||
int lastExecutedServerCommand; // 131400
|
||||
char serverCommands[128][1024]; // 131404
|
||||
bool isServerRestarting; // 262476
|
||||
char clientDemo[16592]; // 262480
|
||||
netchan_t netchan; // 279072
|
||||
char netchanOutgoingBuffer[2048]; // 280656
|
||||
char netchanIncomingBuffer[65536]; // 282704
|
||||
netProfileInfo_t OOBProf; // 348240
|
||||
short statPacketsToSend; // 349744
|
||||
int statPacketSendTime[10]; // From here it might be wrong
|
||||
int currentGamestatePacket;
|
||||
};
|
||||
|
||||
struct clientStatic_t {};
|
||||
} // namespace game
|
||||
|
||||
#pragma warning(pop)
|
80
src/client/game/symbols.hpp
Normal file
80
src/client/game/symbols.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#define WEAK __declspec(selectany)
|
||||
|
||||
namespace game {
|
||||
// Functions
|
||||
WEAK symbol<void()> Sys_ShowConsole{0x515CD0};
|
||||
WEAK symbol<void(HINSTANCE__*)> Sys_CreateConsole{0x51B770};
|
||||
WEAK symbol<void(const char* fmt, ...)> Sys_Error{0x434000};
|
||||
WEAK symbol<void(const char* text)> Conbuf_AppendText{0x4F7300};
|
||||
WEAK symbol<void(errorParm_t, const char* fmt, ...)> Com_Error{0x4A6660};
|
||||
WEAK symbol<const char*(int index)> ConcatArgs{0x539060};
|
||||
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_AddText{0x4C1030};
|
||||
WEAK symbol<void(LocalClientNum_t, const char* text)> Cbuf_InsertText{0x429920};
|
||||
WEAK symbol<void(const char* cmdName, void(), cmd_function_t* cmd)>
|
||||
Cmd_AddCommandInternal{0x537E70};
|
||||
WEAK symbol<void(LocalClientNum_t, int, const char* text)>
|
||||
Cmd_ExecuteSingleCommand{0x4EB8F0};
|
||||
WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{0x4EAF30};
|
||||
WEAK symbol<const char*(int index)> Cmd_Argv{0x5580E0};
|
||||
|
||||
WEAK symbol<dvar_t*(const char*)> Dvar_FindVar{0x4EBB50};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, int value, int min, int max,
|
||||
unsigned __int16 flags, const char* desc)>
|
||||
Dvar_RegisterInt{0x50C760};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned __int16 flags,
|
||||
const char* description)>
|
||||
Dvar_RegisterBool{0x4A3300};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char* value,
|
||||
unsigned __int16 flags, const char* description)>
|
||||
Dvar_RegisterString{0x4157E0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float value, float min, float max,
|
||||
unsigned __int16 flags, const char* description)>
|
||||
Dvar_RegisterFloat{0x4A5CF0};
|
||||
WEAK symbol<void(dvar_t* var, bool value)> Dvar_SetBool{0x46DD70};
|
||||
WEAK symbol<void(const char* dvarName, bool value)> Dvar_SetBoolByName{
|
||||
0x48C7D0};
|
||||
WEAK symbol<const char*(int, int)> Dvar_InfoString{0x4028C0};
|
||||
|
||||
WEAK symbol<int(const char* cmd)> Key_GetBindingForCmd{0x47D300};
|
||||
WEAK symbol<int(const char* keyAsText)> Key_StringToKeynum{
|
||||
0x50A710}; // Virtual-Key Code
|
||||
WEAK symbol<void(LocalClientNum_t, int, int)> Key_SetBinding{0x50B770};
|
||||
|
||||
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{
|
||||
0x4F6B00};
|
||||
|
||||
WEAK symbol<bool(netsrc_t, netadr_s dest, const char* message)>
|
||||
NET_OutOfBandPrint{0x496230};
|
||||
WEAK symbol<bool(netsrc_t, netadr_s dest, unsigned char* data, int size)>
|
||||
NET_OutOfBandData{0x4639C0};
|
||||
WEAK symbol<void(netadr_s*, sockaddr*)> NetadrToSockadr{0x48B460};
|
||||
WEAK symbol<int(const char* serverName, netadr_s serverRemote)> NET_StringToAdr{
|
||||
0x4E09A0};
|
||||
WEAK symbol<SOCKET> query_socket{0x5A861EC};
|
||||
WEAK symbol<void()> Com_Quit_f{0x556060};
|
||||
|
||||
WEAK symbol<void(const msg_t*, unsigned char*, int)> MSG_Init{0x40E030};
|
||||
WEAK symbol<void(const msg_t*, const char*)> MSG_WriteString{0x42A560};
|
||||
WEAK symbol<void(const msg_t*, unsigned __int64)> MSG_WriteInt64{0x4906B0};
|
||||
WEAK symbol<void(const msg_t*, int)> MSG_WriteShort{0x4ACD80};
|
||||
WEAK symbol<void(const msg_t*, const void*, int)> MSG_WriteData{0x4F8C20};
|
||||
WEAK symbol<void(int, const char*)> CL_AddReliableCommand{0x4EE3A0};
|
||||
|
||||
WEAK symbol<int(const char* from, char* to, int size)> MSG_ReadBitsCompress{
|
||||
0x4C62F0};
|
||||
|
||||
WEAK symbol<unsigned __int64()> LiveSteam_GetUid{0x4A4050};
|
||||
WEAK symbol<int(unsigned __int64, const void*, unsigned int)>
|
||||
LiveSteam_Client_ConnectToSteamServer{0x4D6980};
|
||||
|
||||
// Variables
|
||||
WEAK symbol<CmdArgs> cmd_args{0x1C96850};
|
||||
WEAK symbol<PlayerKeyState> playerKeys{0xB3A38C};
|
||||
WEAK symbol<clientConnection_t> localClientConnection{0xB3D360};
|
||||
WEAK symbol<HWND> g_wv_hWnd{0x5A86AF0};
|
||||
WEAK symbol<HWND> s_wcd_hWnd{0x5A86330};
|
||||
WEAK symbol<int> serverId{0xFF5058};
|
||||
WEAK symbol<connstate_t> connectionState{0x1060214};
|
||||
} // namespace game
|
21
src/client/loader/component_interface.hpp
Normal file
21
src/client/loader/component_interface.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
class component_interface {
|
||||
public:
|
||||
virtual ~component_interface() {}
|
||||
|
||||
virtual void post_start() {}
|
||||
|
||||
virtual void post_load() {}
|
||||
|
||||
virtual void pre_destroy() {}
|
||||
|
||||
virtual void post_unpack() {}
|
||||
|
||||
virtual void* load_import([[maybe_unused]] const std::string& library,
|
||||
[[maybe_unused]] const std::string& function) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool is_supported() { return true; }
|
||||
};
|
111
src/client/loader/component_loader.cpp
Normal file
111
src/client/loader/component_loader.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
#include <std_include.hpp>
|
||||
#include "component_loader.hpp"
|
||||
|
||||
void component_loader::register_component(
|
||||
std::unique_ptr<component_interface>&& component_) {
|
||||
get_components().push_back(std::move(component_));
|
||||
}
|
||||
|
||||
bool component_loader::post_start() {
|
||||
static auto handled = false;
|
||||
if (handled)
|
||||
return true;
|
||||
handled = true;
|
||||
|
||||
try {
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->post_start();
|
||||
}
|
||||
} catch (premature_shutdown_trigger&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool component_loader::post_load() {
|
||||
static auto handled = false;
|
||||
if (handled)
|
||||
return true;
|
||||
handled = true;
|
||||
|
||||
clean();
|
||||
|
||||
try {
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->post_load();
|
||||
}
|
||||
} catch (premature_shutdown_trigger&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void component_loader::post_unpack() {
|
||||
static auto handled = false;
|
||||
if (handled)
|
||||
return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->post_unpack();
|
||||
}
|
||||
}
|
||||
|
||||
void component_loader::pre_destroy() {
|
||||
static auto handled = false;
|
||||
if (handled)
|
||||
return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& component_ : get_components()) {
|
||||
component_->pre_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void component_loader::clean() {
|
||||
auto& components = get_components();
|
||||
for (auto i = components.begin(); i != components.end();) {
|
||||
if (!(*i)->is_supported()) {
|
||||
(*i)->pre_destroy();
|
||||
i = components.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* component_loader::load_import(const std::string& library,
|
||||
const std::string& function) {
|
||||
void* function_ptr = nullptr;
|
||||
|
||||
for (const auto& component_ : get_components()) {
|
||||
auto* const component_function_ptr =
|
||||
component_->load_import(library, function);
|
||||
if (component_function_ptr) {
|
||||
function_ptr = component_function_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return function_ptr;
|
||||
}
|
||||
|
||||
void component_loader::trigger_premature_shutdown() {
|
||||
throw premature_shutdown_trigger();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<component_interface>>&
|
||||
component_loader::get_components() {
|
||||
using component_vector = std::vector<std::unique_ptr<component_interface>>;
|
||||
using component_vector_container =
|
||||
std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
|
||||
|
||||
static component_vector_container components(
|
||||
new component_vector, [](component_vector* component_vector) {
|
||||
pre_destroy();
|
||||
delete component_vector;
|
||||
});
|
||||
|
||||
return *components;
|
||||
}
|
51
src/client/loader/component_loader.hpp
Normal file
51
src/client/loader/component_loader.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "component_interface.hpp"
|
||||
|
||||
class component_loader final {
|
||||
public:
|
||||
class premature_shutdown_trigger final : public std::exception {
|
||||
[[nodiscard]] const char* what() const noexcept override {
|
||||
return "Premature shutdown requested";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class installer final {
|
||||
static_assert(std::is_base_of<component_interface, T>::value,
|
||||
"component has invalid base class");
|
||||
|
||||
public:
|
||||
installer() { register_component(std::make_unique<T>()); }
|
||||
};
|
||||
|
||||
template <typename T> static T* get() {
|
||||
for (const auto& component_ : get_components()) {
|
||||
if (typeid(*component_.get()) == typeid(T)) {
|
||||
return reinterpret_cast<T*>(component_.get());
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
register_component(std::unique_ptr<component_interface>&& component);
|
||||
|
||||
static bool post_start();
|
||||
static bool post_load();
|
||||
static void post_unpack();
|
||||
static void pre_destroy();
|
||||
static void clean();
|
||||
|
||||
static void* load_import(const std::string& library,
|
||||
const std::string& function);
|
||||
|
||||
static void trigger_premature_shutdown();
|
||||
|
||||
private:
|
||||
static std::vector<std::unique_ptr<component_interface>>& get_components();
|
||||
};
|
||||
|
||||
#define REGISTER_COMPONENT(name) \
|
||||
namespace { \
|
||||
static component_loader::installer<name> __component; \
|
||||
}
|
1
src/client/std_include.cpp
Normal file
1
src/client/std_include.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "std_include.hpp"
|
24
src/client/std_include.hpp
Normal file
24
src/client/std_include.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#define DLL_EXPORT extern "C" __declspec(dllexport)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
// clang-format off
|
||||
#include "game/structs.hpp"
|
||||
#include "game/game.hpp"
|
||||
// clang-format on
|
Reference in New Issue
Block a user