Prepare dedicated server support

This commit is contained in:
Maurice Heumann 2023-01-01 21:46:36 +01:00
parent f4a4a95674
commit 3a6f973473
26 changed files with 128 additions and 102 deletions

View File

@ -696,9 +696,8 @@ namespace arxan
return zw_terminate_process_hook.invoke<NTSTATUS>(process_handle, exit_status);
}
class component final : public component_interface
struct component final : component_interface
{
public:
void post_load() override
{
auto* dll_characteristics = &utils::nt::library().get_optional_header()->DllCharacteristics;

View File

@ -107,9 +107,8 @@ namespace auth
return guid;
}
class component final : public component_interface
struct component final : client_component_interface
{
public:
void post_unpack() override
{
// Patch steam id bit check

View File

@ -41,9 +41,8 @@ namespace bots
}
}
class component final : public component_interface
struct component final : client_component_interface
{
public:
void post_unpack() override
{
utils::hook::jump(0x141653B70_g, get_bot_name);

View File

@ -27,9 +27,8 @@ namespace branding
}
}
class component final : public component_interface
struct component final : client_component_interface
{
public:
void post_unpack() override
{
scheduler::loop(draw_branding, scheduler::renderer);

View File

@ -30,9 +30,8 @@ namespace colors
utils::hook::copy(g_color_table + index * 4, color_float, sizeof(color_float));
}
class component final : public component_interface
struct component final : client_component_interface
{
public:
void post_unpack() override
{
patch_color<1>(255, 49, 49); // 1 - Red

View File

@ -46,9 +46,8 @@ namespace command
}
}
class component final : public component_interface
struct component final : client_component_interface
{
public:
void post_unpack() override
{
// Disable whitelist

View File

@ -193,9 +193,8 @@ namespace console
}
}
class component final : public component_interface
struct component final : client_component_interface
{
public:
void post_unpack() override
{
utils::hook::set<uint8_t>(0x14133D2FE_g, 0xEB); // Always enable ingame console

View File

@ -9,9 +9,8 @@ namespace dedicated
{
}
class component final : public component_interface
struct component final : server_component_interface
{
public:
void post_unpack() override
{
}

View File

@ -441,9 +441,8 @@ namespace demonware
}
}
class component final : public component_interface
struct component final : component_interface
{
public:
component()
{
udp_servers.create<stun_server>("stun.us.demonware.net");
@ -477,6 +476,11 @@ namespace demonware
{
server_thread = utils::thread::create_named_thread("Demonware", server_main);
if (game::is_server())
{
return;
}
utils::hook::set<uint8_t>(0x14293E829_g, 0x0); // CURLOPT_SSL_VERIFYPEER
utils::hook::set<uint8_t>(0x15F3CCFED_g, 0xAF); // CURLOPT_SSL_VERIFYHOST
utils::hook::set<uint8_t>(0x1430B9810_g, 0x0); // HTTPS -> HTTP
@ -489,7 +493,7 @@ namespace demonware
utils::hook::call(0x141EC458C_g, get_ffotd_name); // Return unlocalized ffotd name
utils::hook::set<uint64_t>(0x141F04550_g, 0xC300000001B8); // Kill LPC_File_SafeWrite
utils::hook::set<uint64_t>(0x141F03180_g, 0xC300000001B8); // Kill LPC_DeleteStale
utils::hook::set<uint8_t>(0x141E0AAAB_g, 0xEB); // Release un-handled reportReward spamming loop
}

View File

@ -32,7 +32,7 @@ namespace discord
}
}
class component final : public component_interface
class component final : public client_component_interface
{
public:
void post_load() override

View File

@ -193,9 +193,8 @@ namespace exception
}
}
class component final : public component_interface
struct component final : component_interface
{
public:
component()
{
main_thread_id = GetCurrentThreadId();

View File

@ -29,9 +29,8 @@ namespace icon
}
}
class component final : public component_interface
struct component final : component_interface
{
public:
void post_load() override
{
load_icon_a_hook.create(LoadIconA, load_icon_a_stub);

View File

@ -14,7 +14,8 @@ namespace loot
utils::hook::detour liveinventory_getitemquantity_hook;
utils::hook::detour liveinventory_areextraslotspurchased_hook;
int loot_getitemquantity_stub(const game::ControllerIndex_t /*controller_index*/, const game::eModes mode, const int /*item_id*/)
int loot_getitemquantity_stub(const game::ControllerIndex_t /*controller_index*/, const game::eModes mode,
const int /*item_id*/)
{
if (mode == game::eModes::MODE_ZOMBIES)
{
@ -27,7 +28,8 @@ namespace loot
int liveinventory_getitemquantity_stub(const game::ControllerIndex_t controller_index, const int item_id)
{
// Item id's for extra CaC slots, CWL camo's and paid specialist outfits
if (item_id == 99003 || item_id >= 99018 && item_id <= 99021 || item_id == 99025 || item_id >= 90047 && item_id <= 90064)
if (item_id == 99003 || item_id >= 99018 && item_id <= 99021 || item_id == 99025 || item_id >= 90047 &&
item_id <= 90064)
{
return 1;
}
@ -47,9 +49,8 @@ namespace loot
}
};
class component final : public component_interface
struct component final : client_component_interface
{
public:
void post_unpack() override
{
scheduler::once(set_dvars_on_startup, scheduler::pipeline::main);

View File

@ -210,9 +210,8 @@ namespace network
return 2;
}
class component final : public component_interface
struct component final : client_component_interface
{
public:
void post_unpack() override
{
//utils::hook::jump(0x14143CAB0_g, ret2); // patch dwGetConnectionStatus

View File

@ -217,9 +217,8 @@ namespace party
network::send(host, "getInfo", challenge);
}
class component final : public component_interface
struct component final : client_component_interface
{
public:
void post_unpack() override
{
utils::hook::jump(0x141E19B60_g, 0x141E19BB6_g); // patch steam id validity check

View File

@ -149,9 +149,8 @@ namespace scheduler
}, type, delay);
}
class component final : public component_interface
struct component final : component_interface
{
public:
void post_load() override
{
thread = utils::thread::create_named_thread("Async Scheduler", []()
@ -166,9 +165,12 @@ namespace scheduler
void post_unpack() override
{
r_end_frame_hook.create(0x142273560_g, r_end_frame_stub); // some func called before R_EndFrame, maybe SND_EndFrame?
g_run_frame_hook.create(0x14065C360_g, server_frame_stub); // GlassSv_Update
main_frame_hook.create(0x1420F9860_g, main_frame_stub); // Com_Frame_Try_Block_Function
if (!game::is_server())
{
r_end_frame_hook.create(0x142273560_g, r_end_frame_stub); // some func called before R_EndFrame, maybe SND_EndFrame?
g_run_frame_hook.create(0x14065C360_g, server_frame_stub); // GlassSv_Update
main_frame_hook.create(0x1420F9860_g, main_frame_stub); // Com_Frame_Try_Block_Function
}
}
void pre_destroy() override

View File

@ -165,9 +165,8 @@ namespace splash
}
}
class component final : public component_interface
struct component final : client_component_interface
{
public:
component()
{
enable_dpi_awareness();

View File

@ -53,7 +53,7 @@ namespace steam_proxy
{
std::string name = utils::string::va("CLIENTENGINE_INTERFACE_VERSION%03i", i);
auto* const temp_client_engine = steam_client_module
.invoke<void*>("CreateInterface", name.data(), nullptr);
.invoke<void*>("CreateInterface", name.data(), nullptr);
if (temp_client_engine) return temp_client_engine;
}
@ -76,7 +76,7 @@ namespace steam_proxy
steam_pipe = steam_client_module.invoke<void*>("Steam_CreateSteamPipe");
global_user = steam_client_module.invoke<void*>(
"Steam_ConnectToGlobalUser", steam_pipe);
"Steam_ConnectToGlobalUser", steam_pipe);
client_user = client_engine.invoke<void*>(8, global_user, steam_pipe);
client_utils = client_engine.invoke<void*>(14, steam_pipe);
@ -205,9 +205,8 @@ namespace steam_proxy
}
}
class component final : public component_interface
struct component final : component_interface
{
public:
void post_load() override
{
load_client();
@ -312,7 +311,7 @@ namespace steam_proxy
}
void access_subscribed_items(
const std::function<void(const subscribed_item_map&)>& callback)
const std::function<void(const subscribed_item_map&)>& callback)
{
subscribed_items.access(callback);
}

View File

@ -5,18 +5,33 @@
namespace game
{
namespace
{
const utils::nt::library& get_host_library()
{
static auto host = []
{
utils::nt::library host{};
if (!host || host == utils::nt::library::get_by_address(get_base))
{
throw std::runtime_error("Invalid host application");
}
return host;
}();
return host;
}
}
size_t get_base()
{
static auto base = []
{
const utils::nt::library host{};
if (!host || host == utils::nt::library::get_by_address(get_base))
{
throw std::runtime_error("Invalid host application");
}
return reinterpret_cast<size_t>(host.get_ptr());
}();
static auto base = reinterpret_cast<size_t>(get_host_library().get_ptr());
return base;
}
bool is_server()
{
static auto server = get_host_library().get_optional_header()->CheckSum == 0x14C28B4;
return server;
}
}

View File

@ -5,6 +5,7 @@
namespace game
{
size_t get_base();
bool is_server();
inline size_t relocate(const size_t val)
{

View File

@ -10,9 +10,17 @@ enum class component_priority
arxan,
};
class component_interface
enum class component_type
{
public:
client,
server,
any,
};
struct component_interface
{
static constexpr component_type type = component_type::any;
virtual ~component_interface() = default;
virtual void post_load()
@ -32,3 +40,13 @@ public:
return component_priority::min;
}
};
struct client_component_interface : component_interface
{
static constexpr component_type type = component_type::client;
};
struct server_component_interface : component_interface
{
static constexpr component_type type = component_type::server;
};

View File

@ -23,9 +23,9 @@ namespace component_loader
return *components;
}
std::vector<registration_functor>& get_registration_functors()
std::vector<std::pair<registration_functor, component_type>>& get_registration_functors()
{
static std::vector<registration_functor> functors;
static std::vector<std::pair<registration_functor, component_type>> functors;
return functors;
}
@ -42,25 +42,28 @@ namespace component_loader
}
}
void register_component(registration_functor functor)
void register_component(registration_functor functor, component_type type)
{
if (!get_components().empty())
{
throw std::runtime_error("Registration is too late");
}
get_registration_functors().push_back(std::move(functor));
get_registration_functors().emplace_back(std::move(functor), type);
}
bool activate()
bool activate(bool server)
{
static auto res = []
static auto res = [server]
{
try
{
for (auto& functor : get_registration_functors())
{
activate_component(functor());
if (functor.second == component_type::any || server == (functor.second == component_type::server))
{
activate_component(functor.first());
}
}
}
catch (premature_shutdown_trigger&)

View File

@ -5,9 +5,9 @@ namespace component_loader
{
using registration_functor = std::function<std::unique_ptr<component_interface>()>;
void register_component(registration_functor functor);
void register_component(registration_functor functor, component_type type);
bool activate();
bool activate(bool server);
bool post_load();
void post_unpack();
void pre_destroy();
@ -33,7 +33,7 @@ namespace component_loader
register_component([]
{
return std::make_unique<T>();
});
}, T::type);
}
};
};

View File

@ -100,7 +100,7 @@ namespace loader
auto* rel_info = offset_pointer<uint16_t*>(relocation, sizeof(IMAGE_BASE_RELOCATION));
const auto* rel_info_end = offset_pointer<uint16_t*>(
rel_info, relocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION));
rel_info, relocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION));
for (; rel_info < rel_info_end; ++rel_info)
{

View File

@ -28,7 +28,8 @@ namespace
const auto game_entry = game.get_iat_entry("steam_api64.dll", func);
if (!game_entry)
{
throw std::runtime_error("Import '" + func + "' not found!");
//throw std::runtime_error("Import '" + func + "' not found!");
return {nullptr, nullptr};
}
const auto original_import = game_entry;
@ -183,12 +184,16 @@ namespace
{
remove_crash_file();
if (!component_loader::activate())
const auto client_binary = "BlackOps3.exe"s;
const auto server_binary = "BlackOps3_UnrankedDedicatedServer.exe"s;
const auto has_server = utils::io::file_exists(server_binary);
if (!component_loader::activate(has_server))
{
return 1;
}
entry_point = load_process("BlackOps3.exe");
entry_point = load_process(has_server ? server_binary : client_binary);
if (!entry_point)
{
throw std::runtime_error("Unable to load binary into memory");

View File

@ -3,51 +3,42 @@
#include "nt.hpp"
#include <shellapi.h>
#include <unordered_set>
#include "finally.hpp"
namespace utils::flags
{
void parse_flags(std::vector<std::string>& flags)
std::unordered_set<std::string> parse_flags()
{
int num_args;
int num_args{};
auto* const argv = CommandLineToArgvW(GetCommandLineW(), &num_args);
flags.clear();
if (argv)
const auto _ = finally([&argv]
{
for (auto i = 0; i < num_args; ++i)
if (argv)
{
std::wstring wide_flag(argv[i]);
if (wide_flag[0] == L'-')
{
wide_flag.erase(wide_flag.begin());
flags.emplace_back(string::convert(wide_flag));
}
LocalFree(argv);
}
});
LocalFree(argv);
std::unordered_set<std::string> flags{};
for (auto i = 0; i < num_args && argv; ++i)
{
std::wstring wide_flag(argv[i]);
if (wide_flag[0] == L'-')
{
wide_flag.erase(wide_flag.begin());
flags.emplace(string::to_lower(string::convert(wide_flag)));
}
}
return flags;
}
bool has_flag(const std::string& flag)
{
static auto parsed = false;
static std::vector<std::string> enabled_flags;
if (!parsed)
{
parse_flags(enabled_flags);
parsed = true;
}
for (const auto& entry : enabled_flags)
{
if (string::to_lower(entry) == string::to_lower(flag))
{
return true;
}
}
return false;
static const auto enabled_flags = parse_flags();
return enabled_flags.contains(string::to_lower(flag));
}
}