Compare commits

..

12 Commits

Author SHA1 Message Date
Caball
d424dc2c45 Added workaround to enforce max fps limit.
Without this workaround my fps occasionally exceeds 1500 or even 2000 fps.
2024-12-31 00:23:57 +01:00
Caball
d26a5fe43f Added a few changes.
Added missing includes to the list of standard headers.
Added an assertion to the asmjit assemble function to get an early warning if asmjit fails silently.
2024-12-31 00:17:57 +01:00
Caball
7954966c60 Updated function symbols and references.
Replaced all occurrences of 0x0 with 0 for consistency.
2024-12-31 00:12:38 +01:00
Caball
3553cd49ff Updated game structs, variable symbols and references. 2024-12-31 00:01:31 +01:00
Caball
2516a48fc1 Added fallback logic for current path / working directory. 2024-12-30 23:35:17 +01:00
Caball
1db89c64c6 Added functions to parse the command line arguments. 2024-12-30 23:28:21 +01:00
Caball
51e831cbfe Added helper util functions for (de)compression. 2024-12-30 23:26:29 +01:00
Caball
50aa4097bb Fixed two bugs in the old color strip function. 2024-12-30 23:22:34 +01:00
Caball
e5a547650c Added helper function to strip colors from strings. 2024-12-30 23:21:50 +01:00
Caball
adc33983a8 Improved naming for function 'get_client_name_stub'. 2024-12-30 23:15:46 +01:00
Caball
27d1f61789 Added hook and fn stub to clean color from agent overhead name. 2024-12-30 23:13:16 +01:00
Caball
d0003da606 Changed all game struct suffixes from _s to _t. 2024-12-30 23:05:06 +01:00
77 changed files with 1272 additions and 1810 deletions

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit 3325bbd33d24d1f8f5a0f69e782c92ad5a39a68e
Subproject commit f1a494cfd2ce55fe88b5134eab985f5852667b8d

2
deps/WinToast vendored

@ -1 +1 @@
Subproject commit a78ce469b456c06103b3b30d4bd37e7bb80da30c
Subproject commit 821c4818ade1aa4da56ac753285c159ce26fd597

2
deps/asmjit vendored

@ -1 +1 @@
Subproject commit e8c8e2e48a1a38154c8e8864eb3bc61db80a1e31
Subproject commit bfa0bf690c2e90cc0844f2f012efa41b916bde7e

2
deps/gsc-tool vendored

@ -1 +1 @@
Subproject commit 2d9781ce0ce9e8551eaf040d7761fd986f33cfdc
Subproject commit b8e30e6334aa33ea731caf8d0700a9e6c7794c09

2
deps/libtomcrypt vendored

@ -1 +1 @@
Subproject commit a6b9aff7aab857fe1b491710a5c5b9e2be49cb08
Subproject commit 7e863d21429f94ed6a720e24499a12a3f852bb31

2
deps/libtommath vendored

@ -1 +1 @@
Subproject commit e823b0c34cea291bdb94d672731e1c1f08525557
Subproject commit 8314bde5e5c8e5d9331460130a9d1066e324f091

2
deps/minhook vendored

@ -1 +1 @@
Subproject commit c3fcafdc10146beb5919319d0683e44e3c30d537
Subproject commit f5485b8454544c2f034c78f8f127c1d03dea3636

2
deps/rapidjson vendored

@ -1 +1 @@
Subproject commit 24b5e7a8b27f42fa16b96fc70aade9106cf7102f
Subproject commit 6089180ecb704cb2b136777798fa1be303618975

2
deps/zlib vendored

@ -1 +1 @@
Subproject commit 5a82f71ed1dfc0bec044d9702463dbdf84ea3b71
Subproject commit 9f0f2d4f9f1f28be7e16d8bf3b4e9d4ada70aa9f

View File

@ -268,7 +268,7 @@ filter "configurations:Release"
buildoptions {"/GL"}
linkoptions {"/IGNORE:4702", "/LTCG"}
defines {"NDEBUG"}
fatalwarnings {"All"}
flags {"FatalCompileWarnings"}
filter {}
filter "configurations:Debug"

View File

@ -1,35 +0,0 @@
#include <std_include.hpp>
#include <loader/component_loader.hpp>
#include "game/game.hpp"
#include <utils/memory.hpp>
namespace asset_restrict
{
namespace
{
void reallocate_asset_pool(game::XAssetType type, const int size)
{
const auto asset_size = game::DB_GetXAssetTypeSize(type);
const auto new_size = size * game::g_poolSize[type];
const auto new_allocation_size = static_cast<std::size_t>(new_size * asset_size);
const game::XAssetHeader pool_entry = { .data = utils::memory::allocate(new_allocation_size) };
game::DB_XAssetPool[type] = pool_entry.data;
game::g_poolSize[type] = new_size;
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
reallocate_asset_pool(game::ASSET_TYPE_XANIMPARTS, 2);
reallocate_asset_pool(game::ASSET_TYPE_ATTACHMENT, 2);
}
};
}
REGISTER_COMPONENT(asset_restrict::component)

View File

@ -1,61 +0,0 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "component/console.hpp"
#include <utils/hook.hpp>
namespace weapons
{
namespace
{
void g_setup_level_weapon_def_stub()
{
game::G_SetupLevelWeaponDef();
// The count on most maps is well below 200
std::array<game::WeaponCompleteDef*, 256> weapons{};
const auto count = game::DB_GetAllXAssetOfType_FastFile(game::ASSET_TYPE_WEAPON, (void**)weapons.data(), static_cast<int>(weapons.max_size()));
std::sort(weapons.begin(), weapons.begin() + count, [](game::WeaponCompleteDef* weapon1, game::WeaponCompleteDef* weapon2)
{
assert(weapon1->szInternalName);
assert(weapon2->szInternalName);
return std::strcmp(weapon1->szInternalName, weapon2->szInternalName) < 0;
});
#ifdef _DEBUG
console::info("Found %i weapons to precache\n", count);
#endif
for (auto i = 0; i < count; ++i)
{
#ifdef _DEBUG
console::info("Precaching weapon \"%s\"\n", weapons[i]->szInternalName);
#endif
(void)game::G_GetWeaponForName(weapons[i]->szInternalName);
}
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_sp()) return;
// Kill Scr_PrecacheItem (We are going to do this from code)
utils::hook::nop(0x1403BDB10, 4);
utils::hook::set<std::uint8_t>(0x1403BDB10, 0xC3);
// Load weapons from the DB
utils::hook::call(0x14039F382, g_setup_level_weapon_def_stub);
utils::hook::call(0x1403AE31A, g_setup_level_weapon_def_stub);
}
};
}
REGISTER_COMPONENT(weapons::component)

View File

@ -8,13 +8,11 @@
#include "console.hpp"
#include "network.hpp"
#include <utils/cryptography.hpp>
#include <utils/hook.hpp>
#include <utils/info_string.hpp>
#include <utils/io.hpp>
#include <utils/properties.hpp>
#include <utils/smbios.hpp>
#include <utils/string.hpp>
#include <utils/smbios.hpp>
#include <utils/info_string.hpp>
#include <utils/cryptography.hpp>
namespace auth
{
@ -33,21 +31,13 @@ namespace auth
std::string get_hw_profile_guid()
{
auto hw_profile_path = (utils::properties::get_appdata_path() / "iw6-guid.dat").generic_string();
if (utils::io::file_exists(hw_profile_path))
{
// Migration
utils::io::remove_file(hw_profile_path);
}
HW_PROFILE_INFO info;
if (!GetCurrentHwProfileA(&info))
{
return {};
}
auto hw_profile_info = std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) };
return hw_profile_info;
return std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) };
}
std::string get_protected_data()
@ -62,7 +52,7 @@ namespace auth
return {};
}
const auto size = std::min<DWORD>(data_out.cbData, 52);
const auto size = std::min(data_out.cbData, 52ul);
std::string result{ reinterpret_cast<char*>(data_out.pbData), size };
LocalFree(data_out.pbData);
@ -86,80 +76,17 @@ namespace auth
return entropy;
}
bool load_key(utils::cryptography::ecc::key& key)
utils::cryptography::ecc::key& get_key()
{
std::string data{};
auto key_path = (utils::properties::get_appdata_path() / "iw6-private.key").generic_string();
if (!utils::io::read_file(key_path, &data))
{
return false;
}
key.deserialize(data);
if (!key.is_valid())
{
console::error("Loaded key is invalid!\n");
return false;
}
return true;
}
utils::cryptography::ecc::key generate_key()
{
auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy());
if (!key.is_valid())
{
throw std::runtime_error("Failed to generate cryptographic key!");
}
auto key_path = (utils::properties::get_appdata_path() / "iw6-private.key").generic_string();
if (!utils::io::write_file(key_path, key.serialize()))
{
console::error("Failed to write cryptographic key!\n");
}
console::info("Generated cryptographic key: %llX\n", key.get_hash());
static auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy());
return key;
}
utils::cryptography::ecc::key load_or_generate_key()
{
utils::cryptography::ecc::key key{};
if (load_key(key))
{
console::info("Loaded cryptographic key: %llX\n", key.get_hash());
return key;
}
return generate_key();
}
utils::cryptography::ecc::key get_key_internal()
{
auto key = load_or_generate_key();
auto key_path = (utils::properties::get_appdata_path() / "iw6-public.key").generic_string();
if (!utils::io::write_file(key_path, key.get_public_key()))
{
console::error("Failed to write public key!\n");
}
return key;
}
const utils::cryptography::ecc::key& get_key()
{
static auto key = get_key_internal();
return key;
}
int send_connect_data_stub(game::netsrc_t sock, game::netadr_s* adr, const char* format, const int len)
int send_connect_data_stub(game::netsrc_t sock, game::netadr_t* adr, const char* format, const int len)
{
std::string connect_string(format, len);
game::SV_Cmd_TokenizeString(connect_string.data());
const auto _0 = gsl::finally([]()
const auto _ = gsl::finally([]()
{
game::SV_Cmd_EndTokenizedString();
});
@ -182,14 +109,14 @@ namespace auth
proto::network::connect_info info;
info.set_publickey(get_key().get_public_key());
info.set_signature(utils::cryptography::ecc::sign_message(get_key(), challenge));
info.set_signature(sign_message(get_key(), challenge));
info.set_infostring(connect_string);
network::send(*adr, "connect", info.SerializeAsString());
return true;
}
void direct_connect(game::netadr_s* from, game::msg_t* msg)
void direct_connect(game::netadr_t* from, game::msg_t* msg)
{
const auto offset = sizeof("connect") + 4;
@ -223,14 +150,14 @@ namespace auth
utils::cryptography::ecc::key key;
key.set(info.publickey());
const auto xuid = std::strtoull(steam_id.data(), nullptr, 16);
const auto xuid = strtoull(steam_id.data(), nullptr, 16);
if (xuid != key.get_hash())
{
network::send(*from, "error", "XUID doesn't match the certificate!", '\n');
return;
}
if (!key.is_valid() || !utils::cryptography::ecc::verify_message(key, challenge, info.signature()))
if (!key.is_valid() || !verify_message(key, challenge, info.signature()))
{
network::send(*from, "error", "Challenge signature was invalid!", '\n');
return;
@ -290,7 +217,7 @@ namespace auth
utils::hook::call(0x1402C4F8E, send_connect_data_stub);
}
command::add("guid", []() -> void
command::add("guid", []
{
console::info("Your guid: %llX\n", steam::SteamUser()->GetSteamID().bits);
});

View File

@ -25,7 +25,9 @@ namespace binding
if (value && value < 100)
{
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
"bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
if (len < 0)
{
return bytes_used;
@ -38,7 +40,9 @@ namespace binding
value -= 100;
if (static_cast<size_t>(value) < custom_binds.size() && !custom_binds[value].empty())
{
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, custom_binds[value].data());
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
"bind %s \"%s\"\n", key_button, custom_binds[value].data());
if (len < 0)
{
return bytes_used;

View File

@ -21,7 +21,7 @@ namespace bots
bool can_add()
{
return party::get_client_count() < *game::mp::svs_clientCount;
return party::get_client_count() < game::mp::svs->clientCount;
}
void bot_team_join(const int entity_num)
@ -29,14 +29,14 @@ namespace bots
// schedule the select team call
scheduler::once([entity_num]()
{
game::SV_ExecuteClientCommand(&game::mp::svs_clients[entity_num],
game::SV_ExecuteClientCommand(&game::mp::svs->clients[entity_num],
utils::string::va("lui 68 2 %i", *game::mp::sv_serverId_value),
false);
// scheduler the select class call
scheduler::once([entity_num]()
{
game::SV_ExecuteClientCommand(&game::mp::svs_clients[entity_num],
game::SV_ExecuteClientCommand(&game::mp::svs->clients[entity_num],
utils::string::va("lui 5 %i %i", (rand() % 5) + 10,
*game::mp::sv_serverId_value), false);
}, scheduler::pipeline::server, 1s);
@ -144,7 +144,7 @@ namespace bots
{
bot_names_received = false;
game::netadr_s master{};
game::netadr_t master{};
if (server_list::get_master_server(master))
{
console::info("Getting bots...\n");
@ -175,7 +175,7 @@ namespace bots
num_bots = std::strtoul(params.get(1), nullptr, 10);
}
num_bots = std::min(num_bots, static_cast<std::size_t>(*game::mp::svs_clientCount));
num_bots = std::min(num_bots, static_cast<std::size_t>(game::mp::svs->clientCount));
console::info("Spawning %zu %s\n", num_bots, (num_bots == 1 ? "bot" : "bots"));
@ -198,9 +198,9 @@ namespace bots
parse_bot_names_from_file();
}
network::on("getbotsResponse", [](const game::netadr_s& target, const std::string& data)
network::on("getbotsResponse", [](const game::netadr_t& target, const std::string& data)
{
game::netadr_s master{};
game::netadr_t master{};
if (server_list::get_master_server(master) && !bot_names_received && target == master)
{
bot_names = utils::string::split(data, '\n');

View File

@ -41,8 +41,7 @@ namespace branding
localized_strings::override("LUA_MENU_LEGAL_COPYRIGHT", "iw6-mod: " VERSION " by AlterWare.\n");
utils::hook::call(SELECT_VALUE(0x1403BDABA, 0x140414424), dvar_set_string_stub);
ui_get_formatted_build_number_hook.create(
SELECT_VALUE(0x140415FD0, 0x1404D7C00), ui_get_formatted_build_number_stub);
ui_get_formatted_build_number_hook.create(game::LiveStorage_FetchFFotD, ui_get_formatted_build_number_stub);
scheduler::loop([]()
{

View File

@ -76,16 +76,7 @@ namespace colors
void com_clean_name_stub(const char* in, char* out, const int out_size)
{
// Check that the name is at least 3 char without colors
char name[16]{};
game::I_strncpyz(out, in, std::min<int>(out_size, sizeof(name)));
utils::string::strip(out, name, std::strlen(out) + 1);
if (std::strlen(name) < 3)
{
game::I_strncpyz(out, "UnnamedPlayer", std::min<int>(out_size, sizeof(name)));
}
strncpy_s(out, out_size, in, _TRUNCATE);
}
char* i_clean_str_stub(char* string)
@ -95,29 +86,26 @@ namespace colors
return string;
}
int cl_get_client_name_and_clan_tag_stub(const int local_client_num, const int index, char* name_buf, const int name_size, char* clan_tag_buf, int clan_tag_size)
size_t get_client_name_stub(const int local_client_num, const int index, char* name_buf,
const int max_name_size, char* clan_buf, const int max_clan_size)
{
// CL_GetClientNameAndClanTag -> CL_GetClientNameAndClanTagColorize
const auto result = utils::hook::invoke<int>(0x1402CF790, local_client_num, index, name_buf, name_size, clan_tag_buf, clan_tag_size);
// CL_GetClientNameAndClanTag (wrapper for CL_GetClientNameAndClanTagColorize)
const auto result = reinterpret_cast<size_t(*)(int, int, char*, int, char*, int)>(0x1402CF790)(
local_client_num, index, name_buf, max_name_size, clan_buf, max_clan_size);
utils::string::strip(name_buf, name_buf, name_size);
utils::string::strip(name_buf, name_buf, static_cast<size_t>(max_name_size));
return result;
}
int com_sprintf_stub(char* dest, int size, const char* fmt, const char* name)
int clean_agent_name_stub(char* out, int max_size, const char* fmt, const char* in)
{
const auto len = game::Com_sprintf(dest, size, fmt, name);
if (len < 0)
{
game::I_strncpyz(dest, "UnnamedAgent", size);
return len;
}
// format agent overhead name like [%s]
const auto length = sprintf_s(out, max_size, fmt, in);
// This should inherit the name of the owner (a player) which already passed the length check in Com_CleanName
utils::string::strip(dest, dest, len + 1);
utils::string::strip(in, out, std::min(static_cast<size_t>(length), static_cast<size_t>(max_size)));
return len;
return length;
}
void rb_lookup_color_stub(const char index, DWORD* color)
@ -160,15 +148,13 @@ namespace colors
if (!game::environment::is_sp())
{
// allows colored name in-game
utils::hook::call(0x1403881CF, com_clean_name_stub);
utils::hook::call(0x140388224, com_clean_name_stub);
utils::hook::jump(0x1404F5FC0, com_clean_name_stub);
// don't apply colors to overhead names
utils::hook::call(0x14025CE79, cl_get_client_name_and_clan_tag_stub);
utils::hook::call(0x14025CE79, get_client_name_stub);
// don't apply colors to overhead names of agents (like dogs or juggernauts)
// hook Com_sprintf in CL_GetAgentName
utils::hook::call(0x1402CF760, com_sprintf_stub);
utils::hook::call(0x1402CF760, clean_agent_name_stub);
// patch I_CleanStr
utils::hook::jump(0x1404F63C0, i_clean_str_stub);

View File

@ -2,7 +2,6 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp"
#include "console.hpp"
@ -195,7 +194,7 @@ namespace command
void add_raw(const char* name, void (*callback)())
{
game::Cmd_AddCommandInternal(name, callback, utils::memory::get_allocator()->allocate<game::cmd_function_s>());
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)
@ -233,10 +232,7 @@ namespace command
void execute(std::string command, const bool sync)
{
if (!command.ends_with('\n'))
{
command += "\n";
}
command += "\n";
if (sync)
{
@ -322,7 +318,7 @@ namespace command
}
console::info("================================ COMMAND DUMP =====================================\n");
game::cmd_function_s* cmd = *game::cmd_functions;
game::cmd_function_t* cmd = *game::cmd_functions;
auto i = 0;
while (cmd)
{
@ -460,7 +456,10 @@ namespace command
}
game::sp::g_entities[0].flags ^= game::FL_GODMODE;
game::CG_GameMessage(0, utils::string::va("godmode %s", game::sp::g_entities[0].flags & game::FL_GODMODE ? "^2on" : "^1off"));
game::CG_GameMessage(0, utils::string::va("godmode %s",
game::sp::g_entities[0].flags & game::FL_GODMODE
? "^2on"
: "^1off"));
});
add("notarget", []
@ -471,7 +470,10 @@ namespace command
}
game::sp::g_entities[0].flags ^= game::FL_NOTARGET;
game::CG_GameMessage(0, utils::string::va("notarget %s", game::sp::g_entities[0].flags & game::FL_NOTARGET ? "^2on" : "^1off"));
game::CG_GameMessage(0, utils::string::va("notarget %s",
game::sp::g_entities[0].flags & game::FL_NOTARGET
? "^2on"
: "^1off"));
});
add("noclip", []
@ -482,7 +484,10 @@ namespace command
}
game::sp::g_entities[0].client->flags ^= 1;
game::CG_GameMessage(0, utils::string::va("noclip %s", game::sp::g_entities[0].client->flags & 1 ? "^2on" : "^1off"));
game::CG_GameMessage(0, utils::string::va("noclip %s",
game::sp::g_entities[0].client->flags & 1
? "^2on"
: "^1off"));
});
add("ufo", []
@ -493,7 +498,10 @@ namespace command
}
game::sp::g_entities[0].client->flags ^= 2;
game::CG_GameMessage(0, utils::string::va("ufo %s", game::sp::g_entities[0].client->flags & 2 ? "^2on" : "^1off"));
game::CG_GameMessage(0, utils::string::va("ufo %s",
game::sp::g_entities[0].client->flags & 2
? "^2on"
: "^1off"));
});
add("give", [](const params& params)
@ -545,65 +553,78 @@ namespace command
{
if (!dvars::sv_cheats->current.enabled)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return;
}
game::mp::g_entities[client_num].flags ^= game::FL_GODMODE;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, utils::string::va("f \"godmode %s\"", game::mp::g_entities[client_num].flags & game::FL_GODMODE ? "^2on" : "^1off"));
game::SV_GameSendServerCommand(client_num, 1,
utils::string::va("f \"godmode %s\"",
game::mp::g_entities[client_num].flags & game::FL_GODMODE
? "^2on"
: "^1off"));
});
add_sv("notarget", [](const int client_num, const params_sv&)
{
if (!dvars::sv_cheats->current.enabled)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return;
}
game::mp::g_entities[client_num].flags ^= game::FL_NOTARGET;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
utils::string::va("f \"notarget %s\"", game::mp::g_entities[client_num].flags & game::FL_NOTARGET ? "^2on" : "^1off"));
game::SV_GameSendServerCommand(client_num, 1,
utils::string::va("f \"notarget %s\"",
game::mp::g_entities[client_num].flags & game::FL_NOTARGET
? "^2on"
: "^1off"));
});
add_sv("noclip", [](const int client_num, const params_sv&)
{
if (!dvars::sv_cheats->current.enabled)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return;
}
game::mp::g_entities[client_num].client->flags ^= 1;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
utils::string::va("f \"noclip %s\"", game::mp::g_entities[client_num].client->flags & 1 ? "^2on" : "^1off"));
game::SV_GameSendServerCommand(client_num, 1,
utils::string::va("f \"noclip %s\"",
game::mp::g_entities[client_num].client->flags & 1
? "^2on"
: "^1off"));
});
add_sv("ufo", [](const int client_num, const params_sv&)
{
if (!dvars::sv_cheats->current.enabled)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return;
}
game::mp::g_entities[client_num].client->flags ^= 2;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
utils::string::va("f \"ufo %s\"", game::mp::g_entities[client_num].client->flags & 2 ? "^2on" : "^1off"));
game::SV_GameSendServerCommand(client_num, 1,
utils::string::va("f \"ufo %s\"",
game::mp::g_entities[client_num].client->flags & 2
? "^2on"
: "^1off"));
});
add_sv("setviewpos", [](const int client_num, const params_sv& params)
{
if (!dvars::sv_cheats->current.enabled)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return;
}
if (params.size() < 4)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
"f \"You did not specify the correct number of coordinates\"");
game::SV_GameSendServerCommand(client_num, 1,
"f \"You did not specify the correct number of coordinates\"");
return;
}
@ -616,14 +637,14 @@ namespace command
{
if (!dvars::sv_cheats->current.enabled)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return;
}
if (params.size() < 4)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
"f \"You did not specify the correct number of coordinates\"");
game::SV_GameSendServerCommand(client_num, 1,
"f \"You did not specify the correct number of coordinates\"");
return;
}
@ -636,13 +657,13 @@ namespace command
{
if (!dvars::sv_cheats->current.enabled)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return;
}
if (params.size() < 2)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"You did not specify a weapon name\"");
return;
}
@ -659,13 +680,13 @@ namespace command
{
if (!dvars::sv_cheats->current.enabled)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
return;
}
if (params.size() < 2)
{
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
game::SV_GameSendServerCommand(client_num, 1, "f \"You did not specify a weapon name\"");
return;
}

View File

@ -84,14 +84,15 @@ namespace console
void print_stub(const char* fmt, ...)
{
char buffer[4096]{};
va_list ap;
va_start(ap, fmt);
[[maybe_unused]] const auto len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
print_message(buffer);
char buffer[4096]{};
const auto res = vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
(void)res;
print_message(buffer);
va_end(ap);
}
void append_text(const char* text)

View File

@ -110,7 +110,7 @@ namespace dedicated
return;
}
game::netadr_s target{};
game::netadr_t target{};
if (server_list::get_master_server(target))
{
network::send(target, "heartbeat", "IW6");
@ -198,6 +198,25 @@ namespace dedicated
return hwnd;
}
void sys_error_stub(const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
scheduler::once([]()
{
command::execute("map_rotate");
}, scheduler::pipeline::main, 3s);
game::Com_Error(game::ERR_DROP, "%s", buffer);
}
void add_commands()
{
command::add("map", [](const command::params& params)
@ -345,6 +364,9 @@ namespace dedicated
utils::hook::nop(0x1404F8BE1, 2); // ^
utils::hook::set<uint8_t>(0x140328660, 0xC3); // Disable image pak file loading
// Stop crashing from sys_errors
utils::hook::jump(0x1404FF510, sys_error_stub);
// Reduce min required memory
utils::hook::set<uint64_t>(0x1404FA6BD, 0x80000000);
utils::hook::set<uint64_t>(0x1404FA76F, 0x80000000);

View File

@ -25,28 +25,28 @@ namespace dedicated_info
scheduler::loop([]()
{
const auto* sv_running = game::Dvar_FindVar("sv_running");
auto* sv_running = game::Dvar_FindVar("sv_running");
if (!sv_running || !sv_running->current.enabled)
{
console::set_title("iw6-mod Dedicated Server");
return;
}
const auto* sv_hostname = game::Dvar_FindVar("sv_hostname");
const auto* sv_maxclients = game::Dvar_FindVar("sv_maxclients");
const auto* mapname = game::Dvar_FindVar("mapname");
auto* const sv_hostname = game::Dvar_FindVar("sv_hostname");
auto* const sv_maxclients = game::Dvar_FindVar("sv_maxclients");
auto* const mapname = game::Dvar_FindVar("mapname");
auto client_count = 0;
auto bot_count = 0;
for (auto i = 0; i < sv_maxclients->current.integer; ++i)
for (auto i = 0; i < sv_maxclients->current.integer; i++)
{
auto* client = &game::mp::svs_clients[i];
auto* client = &game::mp::svs->clients[i];
auto* self = &game::mp::g_entities[i];
if (client->header.state > game::CS_FREE && self && self->client)
{
++client_count;
client_count++;
if (game::SV_BotIsBot(i))
{
++bot_count;
@ -56,9 +56,10 @@ namespace dedicated_info
std::string cleaned_hostname = sv_hostname->current.string;
utils::string::strip(sv_hostname->current.string, cleaned_hostname.data(), cleaned_hostname.size() + 1);
utils::string::strip(sv_hostname->current.string, cleaned_hostname.data(),
cleaned_hostname.size() + 1);
console::set_title(utils::string::va("%s on %s [%d/%d] (%d)", cleaned_hostname.data(),
console::set_title(utils::string::va("%s on %s [%d/%d] (%d)", cleaned_hostname.c_str(),
mapname->current.string, client_count,
sv_maxclients->current.integer, bot_count));
}, scheduler::pipeline::main, 1s);

View File

@ -222,7 +222,7 @@ namespace demonware
va_list ap;
va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
printf("%s: %s\n", function, buffer);
va_end(ap);

View File

@ -54,8 +54,8 @@ namespace discord
discord_presence.details = utils::string::va("%s on %s", gametype, map);
discord_presence.partySize = game::mp::cgArray->snap != nullptr
? game::mp::cgArray->snap->numClients
discord_presence.partySize = game::mp::cg->snap != nullptr
? game::mp::cg->snap->numClients
: 1;
if (game::Dvar_GetBool("xblive_privatematch"))
@ -65,13 +65,13 @@ namespace discord
}
else
{
auto* host_name = reinterpret_cast<char*>(0x14187EBC4);
auto* host_name = game::mp::cgs->szHostName;
utils::string::strip(host_name, host_name, std::strlen(host_name) + 1);
discord_presence.state = host_name;
discord_presence.partyMax = party::server_client_count();
std::hash<game::netadr_s> hash_fn;
std::hash<game::netadr_t> hash_fn;
static const auto nonce = utils::cryptography::random::get_integer();
const auto& address = party::get_target();

View File

@ -1,8 +1,8 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "console.hpp"
@ -11,7 +11,7 @@
namespace dvar_cheats
{
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::DvarValue* value)
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::dvar_value* value)
{
if (dvar && dvar->name == "sv_cheats"s)
{
@ -110,7 +110,7 @@ namespace dvar_cheats
a.jmp(0x1404F0D74);
});
void cg_set_client_dvar_from_server(const int local_client_num, game::mp::cg_s* cg, const char* dvar_id, const char* value)
void cg_set_client_dvar_from_server(const int local_client_num, game::mp::cg_t* cg, const char* dvar_id, const char* value)
{
if (dvar_id == "cg_fov"s || dvar_id == "com_maxfps"s)
{
@ -136,7 +136,7 @@ namespace dvar_cheats
const auto* dvar = game::Scr_GetString(0); // grab the original dvar again since it's never stored on stack
const auto* command = utils::string::va("q %s \"%s\"", dvar, value);
game::engine::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command);
game::SV_GameSendServerCommand(entity_num, 1, command);
}
const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a)

View File

@ -1,16 +1,16 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "fastfiles.hpp"
#include "command.hpp"
#include "console.hpp"
#include "fastfiles.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/memory.hpp>
#include <utils/string.hpp>
#include <utils/io.hpp>
namespace fastfiles
{
@ -22,7 +22,7 @@ namespace fastfiles
void db_try_load_x_file_internal(const char* zone_name, const int zone_flags, const int is_base_map)
{
console::info("Loading fastfile %s\n", zone_name);
db_try_load_x_file_internal_hook.invoke<void>(zone_name, zone_flags, is_base_map);
return db_try_load_x_file_internal_hook.invoke<void>(zone_name, zone_flags, is_base_map);
}
void dump_gsc_script(const std::string& name, game::XAssetHeader header)
@ -32,12 +32,6 @@ namespace fastfiles
return;
}
const auto out_name = std::format("gsc_dump/{}.gscbin", name);
if (utils::io::file_exists(out_name))
{
return;
}
std::string buffer;
buffer.append(header.scriptfile->name, std::strlen(header.scriptfile->name) + 1);
buffer.append(reinterpret_cast<char*>(&header.scriptfile->compressedLen), 4);
@ -46,38 +40,12 @@ namespace fastfiles
buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen);
buffer.append(reinterpret_cast<char*>(header.scriptfile->bytecode), header.scriptfile->bytecodeLen);
const auto out_name = std::format("gsc_dump/{}.gscbin", name);
utils::io::write_file(out_name, buffer);
console::info("Dumped %s\n", out_name.c_str());
console::info("Dumped %s\n", out_name.data());
}
void dump_csv_table(const std::string& name, game::XAssetHeader header)
{
if (!dvars::g_dump_string_tables->current.enabled)
{
return;
}
const auto out_name = std::format("csv_dump/{}.csv", name);
if (utils::io::file_exists(out_name))
{
return;
}
std::string buffer;
for (auto row = 0; row < header.stringTable->rowCount; row++)
{
for (auto column = 0; column < header.stringTable->columnCount; column++)
{
const auto* string = header.stringTable->values[(row * header.stringTable->columnCount) + column].string;
buffer.append(utils::string::va("%s%s", string ? string : "", (column == header.stringTable->columnCount - 1) ? "\n" : ","));
}
}
utils::io::write_file(out_name, buffer);
console::info("Dumped %s\n", out_name.c_str());
}
game::XAssetHeader db_find_x_asset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
{
@ -90,11 +58,6 @@ namespace fastfiles
dump_gsc_script(name, result);
}
if (type == game::ASSET_TYPE_STRINGTABLE)
{
dump_csv_table(name, result);
}
if (diff > 100)
{
console::print(
@ -147,11 +110,26 @@ namespace fastfiles
db_find_x_asset_header_hook.create(game::DB_FindXAssetHeader, db_find_x_asset_header_stub);
dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts to binary format");
dvars::g_dump_string_tables = game::Dvar_RegisterBool("g_dumpStringTables", false, game::DVAR_FLAG_NONE, "Dump CSV files");
utils::hook::call(SELECT_VALUE(0x1402752DF, 0x140156350), p_mem_free_stub);
utils::hook::call(SELECT_VALUE(0x140276004, 0x140324259), p_mem_free_stub);
command::add("loadzone", [](const command::params& params)
{
if (params.size() < 2)
{
console::info("usage: loadzone <zone>\n");
return;
}
game::XZoneInfo info;
info.name = params.get(1);
info.allocFlags = 1;
info.freeFlags = 0;
game::DB_LoadXAssets(&info, 1, game::DBSyncMode::DB_LOAD_SYNC);
});
command::add("materiallist", [](const command::params& params)
{
game::DB_EnumXAssets_FastFile(game::ASSET_TYPE_MATERIAL, [](const game::XAssetHeader header, void*)
@ -166,10 +144,6 @@ namespace fastfiles
if (!game::environment::is_sp())
{
reallocate_asset_pool(game::ASSET_TYPE_WEAPON, 320);
// Allow loading of unsigned fastfiles
utils::hook::set<uint8_t>(0x1402FBF23, 0xEB); // DB_LoadXFile
utils::hook::nop(0x1402FC445, 2); // DB_SetFileLoadCompressor
}
}
};

View File

@ -83,22 +83,15 @@ namespace filesystem
void startup()
{
const auto base = std::filesystem::current_path();
register_path(base / "iw6");
register_path("iw6");
register_path(get_binary_directory() + "\\data");
if (get_binary_directory() != base)
{
register_path(base / "data");
}
// game's search paths
register_path(base / "devraw");
register_path(base / "devraw_shared");
register_path(base / "raw_shared");
register_path(base / "raw");
register_path(base / "main");
register_path("devraw");
register_path("devraw_shared");
register_path("raw_shared");
register_path("raw");
register_path("main");
}
void check_for_startup()

View File

@ -74,6 +74,23 @@ namespace fps
++data->index;
}
void enforce_fps_limit()
{
const auto* maxfps = game::Dvar_FindVar("com_maxfps");
const auto max = (maxfps) ? std::min(2 * maxfps->current.integer, maxfps->domain.integer.max + 250) : 1250;
const auto fps = static_cast<std::int32_t>(static_cast<float>(1000.0f /
static_cast<float>(cg_perf.average)) + 9.313225746154785e-10);
if (fps > max)
{
// workaround to limit fps
scheduler::once([]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}, scheduler::pipeline::renderer);
}
}
void perf_update()
{
cg_perf.count = 32;
@ -84,6 +101,7 @@ namespace fps
cg_perf.previous_ms = cg_perf.current_ms;
perf_calc_fps(&cg_perf, cg_perf.frame_ms);
enforce_fps_limit();
utils::hook::invoke<void>(SELECT_VALUE(0x1405806E0, 0x140658E30));
}

View File

@ -63,7 +63,7 @@ namespace game_console
void clear()
{
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
strncpy_s(con.buffer, "", sizeof(con.buffer));
con.cursor = 0;
fixed_input = "";
@ -249,7 +249,7 @@ namespace game_console
dvars::con_inputDvarInactiveValueColor->current.vector, offset);
}
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
con.globals.may_auto_complete = true;
}
else if (matches.size() > 1)
@ -274,7 +274,7 @@ namespace game_console
}
}
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), sizeof(con.globals.auto_complete_choice));
con.globals.may_auto_complete = true;
}
}
@ -364,11 +364,11 @@ namespace game_console
void print_internal(const char* fmt, ...)
{
char va_buffer[1024]{};
char va_buffer[0x200] = { 0 };
va_list ap;
va_start(ap, fmt);
vsnprintf(va_buffer, sizeof(va_buffer), fmt, ap);
vsprintf_s(va_buffer, fmt, ap);
va_end(ap);
const auto formatted = std::string(va_buffer);
@ -424,7 +424,7 @@ namespace game_console
con.buffer[1] = '\0';
}
game::I_strncat(con.buffer, sizeof(con.buffer), con.globals.auto_complete_choice);
strncat_s(con.buffer, con.globals.auto_complete_choice, 64);
con.cursor = static_cast<int>(std::string(con.buffer).length());
if (con.cursor != 254)
@ -505,7 +505,7 @@ namespace game_console
{
if (key == game::keyNum_t::K_F10)
{
if(game::mp::svs_clients[localClientNum].header.state > game::CS_FREE)
if(game::mp::svs->clients[localClientNum].header.state > game::CS_FREE)
{
return false;
}
@ -549,7 +549,7 @@ namespace game_console
if (history_index != -1)
{
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
@ -564,7 +564,7 @@ namespace game_console
if (history_index != -1)
{
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
@ -674,7 +674,7 @@ namespace game_console
}
}
game::cmd_function_s* cmd = (*game::cmd_functions);
game::cmd_function_t* cmd = (*game::cmd_functions);
while (cmd)
{
if (cmd->name)
@ -721,7 +721,7 @@ namespace game_console
con.output_visible = false;
con.display_line_offset = 0;
con.line_count = 0;
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
strncpy_s(con.buffer, "", sizeof(con.buffer));
con.globals.x = 0.0f;
con.globals.y = 0.0f;
@ -729,7 +729,7 @@ namespace game_console
con.globals.font_height = 0.0f;
con.globals.may_auto_complete = false;
con.globals.info_line_count = 0;
game::I_strncpyz(con.globals.auto_complete_choice, "", sizeof(con.globals.auto_complete_choice));
strncpy_s(con.globals.auto_complete_choice, "", sizeof(con.globals.auto_complete_choice));
// add clear command
command::add("clear", [&]()

View File

@ -3,10 +3,12 @@
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "console.hpp"
#include "game_log.hpp"
#include "scheduler.hpp"
#include "scripting.hpp"
#include "console.hpp"
#include "game_log.hpp"
#include "gsc/script_extension.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
@ -21,7 +23,7 @@ namespace game_log
char buf[1024]{};
std::size_t out_chars = 0;
for (std::uint32_t i = 0; i < game::Scr_GetNumParam(); ++i)
for (auto i = 0u; i < game::Scr_GetNumParam(); ++i)
{
const auto* value = game::Scr_GetString(i);
const auto len = std::strlen(value);
@ -32,7 +34,7 @@ namespace game_log
break;
}
game::I_strncat(buf, sizeof(buf), value);
strncat_s(buf, value, _TRUNCATE);
}
g_log_printf("%s", buf);
@ -47,15 +49,22 @@ namespace game_log
return;
}
char buffer[1024]{};
char buffer[0x400]{};
va_list ap;
va_start(ap, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, ap);
vsprintf_s(buffer, fmt, ap);
va_end(ap);
const auto time = *game::level_time / 1000;
utils::io::write_file(log, utils::string::va("%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, buffer), true);
utils::io::write_file(log, utils::string::va("%3i:%i%i %s",
time / 60,
time % 60 / 10,
time % 60 % 10,
buffer
), true);
}
class component final : public component_interface
@ -93,14 +102,9 @@ namespace game_log
g_log_printf("InitGame\n");
});
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
scripting::on_shutdown([](int free_scripts)
{
if (post_shutdown)
{
return;
}
console::info("==== ShutdownGame (%d) ====\n", clear_scripts);
console::info("==== ShutdownGame (%d) ====\n", free_scripts);
g_log_printf("ShutdownGame:\n");
g_log_printf("------------------------------------------------------------\n");

View File

@ -133,7 +133,7 @@ namespace gameplay
return jump_height;
}
void jump_apply_slowdown_stub(game::mp::playerState_s* ps)
void jump_apply_slowdown_stub(game::mp::playerState_t* ps)
{
assert(ps->pm_flags & game::PMF_JUMPING);
@ -163,7 +163,7 @@ namespace gameplay
}
}
float jump_get_slowdown_friction(game::mp::playerState_s* ps)
float jump_get_slowdown_friction(game::mp::playerState_t* ps)
{
assert(ps->pm_flags & game::PMF_JUMPING);
assert(ps->pm_time <= game::JUMP_LAND_SLOWDOWN_TIME);
@ -181,7 +181,7 @@ namespace gameplay
return 2.5f;
}
float jump_reduce_friction_stub(game::mp::playerState_s* ps)
float jump_reduce_friction_stub(game::mp::playerState_t* ps)
{
float control;
@ -199,7 +199,7 @@ namespace gameplay
return control;
}
float jump_get_land_factor(game::mp::playerState_s* ps)
float jump_get_land_factor(game::mp::playerState_t* ps)
{
assert(ps->pm_flags & game::PMF_JUMPING);
assert(ps->pm_time <= game::JUMP_LAND_SLOWDOWN_TIME);
@ -219,9 +219,9 @@ namespace gameplay
void jump_start_stub(game::pmove_t* pm, game::pml_t* pml, float height)
{
static_assert(offsetof(game::mp::playerState_s, groundEntityNum) == 0x70);
static_assert(offsetof(game::mp::playerState_s, pm_time) == 0x8);
static_assert(offsetof(game::mp::playerState_s, sprintState.sprintButtonUpRequired) == 0x240);
static_assert(offsetof(game::mp::playerState_t, groundEntityNum) == 0x70);
static_assert(offsetof(game::mp::playerState_t, pm_time) == 0x8);
static_assert(offsetof(game::mp::playerState_t, sprintState.sprintButtonUpRequired) == 0x240);
static_assert(offsetof(game::pml_t, frametime) == 0x24);
static_assert(offsetof(game::pml_t, walking) == 0x2C);
static_assert(offsetof(game::pml_t, groundPlane) == 0x30);
@ -229,9 +229,9 @@ namespace gameplay
float factor;
float velocity_sqrd;
game::mp::playerState_s* ps;
game::mp::playerState_t* ps;
ps = static_cast<game::mp::playerState_s*>(pm->ps);
ps = static_cast<game::mp::playerState_t*>(pm->ps);
assert(ps);
@ -295,7 +295,7 @@ namespace gameplay
}
}
void pm_weapon_use_ammo_stub(game::playerState_s* ps, game::Weapon weapon,
void pm_weapon_use_ammo_stub(game::playerState_t* ps, game::Weapon weapon,
bool is_alternate, int amount, game::PlayerHandIndex hand)
{
if (!dvars::player_sustainAmmo->current.enabled)
@ -304,10 +304,10 @@ namespace gameplay
}
}
game::mp::gentity_s* weapon_rocket_launcher_fire_stub(game::mp::gentity_s* ent, game::Weapon weapon, float spread, game::weaponParms* wp,
game::mp::gentity_t* weapon_rocket_launcher_fire_stub(game::mp::gentity_t* ent, game::Weapon weapon, float spread, game::weaponParms* wp,
const float* gun_vel, game::mp::missileFireParms* fire_parms, bool magic_bullet)
{
auto* result = utils::hook::invoke<game::mp::gentity_s*>(0x1403DB8A0, ent, weapon, spread, wp, gun_vel, fire_parms, magic_bullet);
auto* result = utils::hook::invoke<game::mp::gentity_t*>(0x1403DB8A0, ent, weapon, spread, wp, gun_vel, fire_parms, magic_bullet);
if (ent->client != nullptr && wp->weapDef->inventoryType != game::WEAPINVENTORY_EXCLUSIVE)
{

View File

@ -113,6 +113,7 @@ namespace gsc
return res;
}
}
unsigned int scr_get_object(unsigned int index)
@ -283,23 +284,28 @@ namespace gsc
public:
void post_unpack() override
{
scr_emit_function_hook.create(SELECT_VALUE(0x1403D3350, 0x14042E150), &scr_emit_function_stub);
if (game::environment::is_sp())
{
return;
}
utils::hook::call(SELECT_VALUE(0x1403D32E4, 0x14042E0E4), compile_error_stub); // LinkFile
utils::hook::call(SELECT_VALUE(0x1403D3338, 0x14042E138), compile_error_stub); // LinkFile
utils::hook::call(SELECT_VALUE(0x1403D342A, 0x14042E22B), find_variable_stub); // Scr_EmitFunction
scr_emit_function_hook.create(0x14042E150, &scr_emit_function_stub);
utils::hook::call(0x14042E0E4, compile_error_stub); // LinkFile
utils::hook::call(0x14042E138, compile_error_stub); // LinkFile
utils::hook::call(0x14042E22B, find_variable_stub); // Scr_EmitFunction
// Restore basic error messages to scr functions
utils::hook::jump(game::Scr_GetObject, scr_get_object);
utils::hook::jump(game::Scr_GetConstString, scr_get_const_string);
utils::hook::jump(game::Scr_GetConstIString, scr_get_const_istring);
utils::hook::jump(game::Scr_GetVector, scr_get_vector);
utils::hook::jump(game::Scr_GetInt, scr_get_int);
utils::hook::jump(game::Scr_GetFloat, scr_get_float);
utils::hook::jump(0x140438ED0, scr_get_object);
utils::hook::jump(0x140438AD0, scr_get_const_string);
utils::hook::jump(0x1404388B0, scr_get_const_istring);
utils::hook::jump(0x1404393D0, scr_get_vector);
utils::hook::jump(0x140438E10, scr_get_int);
utils::hook::jump(0x140438D60, scr_get_float);
utils::hook::jump(SELECT_VALUE(0x1403DE150, 0x1404390B0), scr_get_pointer_type);
utils::hook::jump(SELECT_VALUE(0x1403DE320, 0x140439280), scr_get_type);
utils::hook::jump(SELECT_VALUE(0x1403DE390, 0x1404392F0), scr_get_type_name);
utils::hook::jump(0x1404390B0, scr_get_pointer_type);
utils::hook::jump(0x140439280, scr_get_type);
utils::hook::jump(0x1404392F0, scr_get_type_name);
}
void pre_destroy() override

View File

@ -37,60 +37,9 @@ namespace gsc
bool force_error_print = false;
std::optional<std::string> gsc_error_msg;
std::vector<devmap_entry> devmap_entries{};
std::optional<devmap_entry> get_devmap_entry(const std::uint8_t* codepos)
{
const auto itr = std::ranges::find_if(devmap_entries, [codepos](const devmap_entry& entry) -> bool
{
return codepos >= entry.bytecode && codepos < entry.bytecode + entry.size;
});
if (itr != devmap_entries.end())
{
return *itr;
}
return {};
}
std::optional<std::pair<std::uint16_t, std::uint16_t>> get_line_and_col_for_codepos(const std::uint8_t* codepos)
{
const auto entry = get_devmap_entry(codepos);
if (!entry.has_value())
{
return {};
}
std::optional<std::pair<std::uint16_t, std::uint16_t>> best_line_info{};
std::uint32_t best_codepos = 0;
assert(codepos >= entry->bytecode);
const std::uint32_t codepos_offset = static_cast<std::uint32_t>(codepos - entry->bytecode);
for (const auto& instruction : entry->devmap)
{
if (instruction.codepos > codepos_offset)
{
continue;
}
if (best_line_info.has_value() && codepos_offset - instruction.codepos > codepos_offset - best_codepos)
{
continue;
}
best_line_info = { { instruction.line, instruction.col } };
best_codepos = instruction.codepos;
}
return best_line_info;
}
unsigned int scr_get_function_stub(const char** p_name, int* type)
{
const auto result = game::Scr_GetFunction(p_name, type);
const auto result = utils::hook::invoke<unsigned int>(0x1403CD9F0, p_name, type);
for (const auto& [name, func] : functions)
{
@ -171,36 +120,27 @@ namespace gsc
const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos : frame->fs.pos;
const auto function = find_function(frame->fs.pos);
const char* location;
if (function.has_value())
{
location = utils::string::va("function \"%s\" in file \"%s\"", function.value().first.data(), function.value().second.data());
console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data());
}
else
{
location = utils::string::va("unknown location %p", pos);
console::warn("\tat unknown location %p\n", pos);
}
const auto line_info = get_line_and_col_for_codepos(reinterpret_cast<const std::uint8_t*>(pos));
if (line_info.has_value())
{
location = utils::string::va("%s line \"%d\" column \"%d\"", location, line_info->first, line_info->second);
}
console::warn("\tat %s\n", location);
}
}
void vm_error_stub(const unsigned __int64 mark_pos)
void vm_error_stub(int mark_pos)
{
if (!dvars::com_developer_script->current.enabled && !force_error_print)
{
game::LargeLocalResetToMark(mark_pos);
utils::hook::invoke<void>(0x1404E4D00, mark_pos);
return;
}
console::warn("******* script runtime error ********\n");
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(SELECT_VALUE(0x1455BE740, 0x144D57840));
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x144D57840);
const std::string error = gsc_error_msg.has_value() ? std::format(": {}", gsc_error_msg.value()) : std::string();
@ -226,7 +166,7 @@ namespace gsc
print_callstack();
console::warn("************************************\n");
game::LargeLocalResetToMark(mark_pos);
utils::hook::invoke<void>(0x1404E4D00, mark_pos);
}
void inc_in_param()
@ -288,22 +228,6 @@ namespace gsc
scr_error(utils::string::va("Assert fail: %s", game::Scr_GetString(0)));
}
void scr_cmd_is_dedicated_server()
{
game::Scr_AddInt(game::environment::is_dedi());
}
void scr_bot_auto_connect_enabled()
{
if (game::environment::is_dedi() && dvars::sv_botsAutoJoin->current.enabled)
{
game::Scr_AddInt(1); // 2 seems to be unused (incomplete GSC/game mode)
return;
}
game::Scr_AddInt(game::BG_BotsConnectType());
}
const char* get_code_pos(const int index)
{
if (static_cast<unsigned int>(index) >= game::scr_VmPub->outparamcount)
@ -324,22 +248,6 @@ namespace gsc
}
}
void add_devmap_entry(std::uint8_t* codepos, std::size_t size, const std::string& name, xsk::gsc::buffer devmap_buf)
{
std::vector<dev_map_instruction> devmap{};
const auto* devmap_ptr = reinterpret_cast<const dev_map*>(devmap_buf.data);
devmap.resize(devmap_ptr->num_instructions);
std::memcpy(devmap.data(), devmap_ptr->instructions, sizeof(dev_map_instruction) * devmap_ptr->num_instructions);
devmap_entries.emplace_back(codepos, size, name, std::move(devmap));
}
void clear_devmap()
{
devmap_entries.clear();
}
void add_function(const std::string& name, game::BuiltinFunction function)
{
++function_id_start;
@ -363,18 +271,27 @@ namespace gsc
utils::hook::set<game::BuiltinFunction>(SELECT_VALUE(0x14086F468, 0x1409E6CE8), scr_print);
utils::hook::set<game::BuiltinFunction>(SELECT_VALUE(0x14086F480, 0x1409E6D00), scr_print_ln);
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353B + 1, 0x14042E33B + 1), 0x1000); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353C, 0x14042E33C), 0x1000); // Scr_RegisterFunction
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D3542 + 4, 0x14042E342 + 4), RVA(&func_table)); // Scr_RegisterFunction
// utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0
utils::hook::inject(SELECT_VALUE(0x1403D38E4 + 3, 0x14042E734 + 3), &func_table); // Scr_BeginLoadScripts
utils::hook::nop(SELECT_VALUE(0x1403E0BDD + 5, 0x14043BBBE + 5), 2);
utils::hook::call(SELECT_VALUE(0x1403E0BDD, 0x14043BBBE), vm_call_builtin_function);
if (game::environment::is_sp())
{
return;
}
utils::hook::call(SELECT_VALUE(0x1403D391F, 0x14042E76F), scr_get_function_stub);
utils::hook::nop(0x14043BBBE + 5, 2);
utils::hook::call(0x14043BBBE, vm_call_builtin_function);
utils::hook::call(SELECT_VALUE(0x1403E1ED0, 0x14043CEB1), vm_error_stub);
utils::hook::call(0x14043CEB1, vm_error_stub);
utils::hook::call(0x14042E76F, scr_get_function_stub);
utils::hook::set<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E6E20, assert_cmd);
add_function("replacefunc", []
{
@ -392,19 +309,10 @@ namespace gsc
command::execute(cmd);
});
if (game::environment::is_sp())
add_function("isdedicated", []
{
return;
}
utils::hook::set<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E6E20, assert_cmd);
utils::hook::set<game::BuiltinFunction>(0x1409E94D0, scr_cmd_is_dedicated_server);
dvars::sv_botsAutoJoin = game::Dvar_RegisterBool("sv_botsAutoJoin", false, game::DVAR_FLAG_NONE, "");
utils::hook::set<game::BuiltinFunction>(0x1409E92F0, scr_bot_auto_connect_enabled);
game::Scr_AddInt(game::environment::is_dedi());
});
}
};
}

View File

@ -1,36 +1,9 @@
#pragma once
#include <xsk/gsc/engine/iw6_pc.hpp>
namespace gsc
{
extern void* func_table[0x1000];
#pragma pack(push, 1)
struct dev_map_instruction
{
std::uint32_t codepos;
std::uint16_t line;
std::uint16_t col;
};
struct dev_map
{
std::uint32_t num_instructions;
dev_map_instruction instructions[1];
};
#pragma pack(pop)
struct devmap_entry
{
const std::uint8_t* bytecode;
std::size_t size;
std::string script_name;
std::vector<dev_map_instruction> devmap;
};
void add_devmap_entry(std::uint8_t*, std::size_t, const std::string&, xsk::gsc::buffer);
void clear_devmap();
void add_function(const std::string& name, game::BuiltinFunction function);
void scr_error(const char* error);

View File

@ -3,16 +3,15 @@
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/compression.hpp>
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/hook.hpp>
#include <utils/memory.hpp>
#include <utils/compression.hpp>
#include "component/console.hpp"
#include "component/filesystem.hpp"
#include "component/console.hpp"
#include "component/scripting.hpp"
#include "script_extension.hpp"
#include "script_loading.hpp"
namespace gsc
@ -33,7 +32,6 @@ namespace gsc
init_handles.clear();
loaded_scripts.clear();
script_allocator.clear();
clear_devmap();
}
bool read_raw_script_file(const std::string& name, std::string* data)
@ -116,12 +114,6 @@ namespace gsc
loaded_scripts[real_name] = script_file_ptr;
const auto devmap = std::get<2>(output_script);
if (devmap.size > 0 && (gsc_ctx->build() & xsk::gsc::build::dev_maps) != xsk::gsc::build::prod)
{
add_devmap_entry(script_file_ptr->bytecode, byte_code_size, real_name, devmap);
}
return script_file_ptr;
}
catch (const std::exception& ex)
@ -188,9 +180,13 @@ namespace gsc
}
}
void load_scripts_from_folder(const std::filesystem::path& root_dir, const std::filesystem::path& script_dir)
void load_scripts(const std::filesystem::path& root_dir)
{
console::info("Scanning directory '%s' for custom GSC scripts...\n", script_dir.generic_string().data());
const std::filesystem::path script_dir = root_dir / "scripts";
if (!utils::io::directory_exists(script_dir.generic_string()))
{
return;
}
const auto scripts = utils::io::list_files(script_dir.generic_string());
for (const auto& script : scripts)
@ -208,44 +204,6 @@ namespace gsc
}
}
void load_scripts(const std::filesystem::path& root_dir)
{
const auto load = [&root_dir](const std::filesystem::path& folder) -> void
{
const std::filesystem::path script_dir = root_dir / folder;
if (utils::io::directory_exists(script_dir.generic_string()))
{
load_scripts_from_folder(root_dir, script_dir);
}
};
const std::filesystem::path base_dir = "scripts";
load(base_dir);
const auto* map_name = game::Dvar_FindVar("mapname");
if (game::environment::is_sp())
{
const std::filesystem::path game_folder = "sp";
load(base_dir / game_folder);
load(base_dir / game_folder / map_name->current.string);
}
else
{
const std::filesystem::path game_folder = "mp";
load(base_dir / game_folder);
load(base_dir / game_folder / map_name->current.string);
const auto* game_type = game::Dvar_FindVar("g_gametype");
load(base_dir / game_folder / game_type->current.string);
}
}
int db_is_x_asset_default(game::XAssetType type, const char* name)
{
if (loaded_scripts.contains(name))
@ -260,22 +218,12 @@ namespace gsc
{
utils::hook::invoke<void>(0x1403CCB10);
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path);
}
}
int g_scr_load_script_and_label_stub(game::ScriptFunctions* functions)
{
const auto result = utils::hook::invoke<int>(0x140349790, functions);
clear();
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path);
}
return result;
}
void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size)
@ -302,26 +250,7 @@ namespace gsc
utils::hook::invoke<void>(0x1403D2CA0);
}
void scr_load_level_singleplayer_stub()
{
for (auto& function_handle : main_handles)
{
console::info("Executing '%s::main'\n", function_handle.first.data());
const auto thread = game::Scr_ExecThread(static_cast<int>(function_handle.second), 0);
game::RemoveRefToObject(thread);
}
utils::hook::invoke<void>(0x1403401B0);
for (auto& function_handle : init_handles)
{
console::info("Executing '%s::init'\n", function_handle.first.data());
const auto thread = game::Scr_ExecThread(static_cast<int>(function_handle.second), 0);
game::RemoveRefToObject(thread);
}
}
void scr_load_level_multiplayer_stub()
void scr_load_level_stub()
{
utils::hook::invoke<void>(0x1403CDC60);
@ -403,7 +332,7 @@ namespace gsc
public:
loading()
{
gsc_ctx = std::make_unique<xsk::gsc::iw6_pc::context>(xsk::gsc::instance::server);
gsc_ctx = std::make_unique<xsk::gsc::iw6_pc::context>();
}
void post_unpack() override
@ -416,35 +345,31 @@ namespace gsc
utils::hook::call(SELECT_VALUE(0x14032D1E0, 0x1403CCED9), scr_begin_load_scripts_stub); // GScr_LoadScripts
utils::hook::call(SELECT_VALUE(0x14032D345, 0x1403CD08D), scr_end_load_scripts_stub); // GScr_LoadScripts
// ProcessScript
utils::hook::call(SELECT_VALUE(0x1403DC887, 0x1404378D7), find_script);
utils::hook::call(SELECT_VALUE(0x1403DC897, 0x1404378E7), db_is_x_asset_default);
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
if (game::environment::is_sp())
{
if (clear_scripts && post_shutdown)
return;
}
// ProcessScript
utils::hook::call(0x1404378D7, find_script);
utils::hook::call(0x1404378E7, db_is_x_asset_default);
// GScr_LoadScripts
utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub);
// Exec script handles
utils::hook::call(0x14039F64E, g_load_structs_stub);
utils::hook::call(0x14039F653, scr_load_level_stub);
scripting::on_shutdown([](int free_scripts)
{
if (free_scripts)
{
clear();
}
});
if (game::environment::is_sp())
{
utils::hook::call(0x14034996F, g_scr_load_script_and_label_stub);
utils::hook::call(0x140316591, scr_load_level_singleplayer_stub);
}
else
{
// GScr_LoadScripts
utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub);
// Exec script handles
utils::hook::call(0x14039F64E, g_load_structs_stub);
utils::hook::call(0x14039F653, scr_load_level_multiplayer_stub);
}
}
};
}

View File

@ -50,8 +50,8 @@ namespace input
return;
}
cl_char_event_hook.create(SELECT_VALUE(0x14023CE50, 0x1402C2AE0), cl_char_event_stub);
cl_key_event_hook.create(SELECT_VALUE(0x14023D070, 0x1402C2CE0), cl_key_event_stub);
cl_char_event_hook.create(game::CL_CharEvent, cl_char_event_stub);
cl_key_event_hook.create(game::CL_KeyEvent, cl_key_event_stub);
}
};
}

View File

@ -10,15 +10,43 @@ namespace logger
{
namespace
{
utils::hook::detour com_error_hook;
const game::dvar_t* logger_dev = nullptr;
void print_com_error(int, const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
console::error("%s", buffer);
}
void com_error_stub(const int error, const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
console::error("Error: %s\n", buffer);
com_error_hook.invoke<void>(error, "%s", buffer);
}
void print_warning(const char* msg, ...)
{
char buffer[2048]{};
va_list ap;
va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
console::warn("%s", buffer);
@ -30,7 +58,7 @@ namespace logger
va_list ap;
va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
console::info("%s", buffer);
@ -47,7 +75,7 @@ namespace logger
va_list ap;
va_start(ap, msg);
vsnprintf(buffer, sizeof(buffer), msg, ap);
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
va_end(ap);
console::info("%s", buffer);
@ -113,6 +141,13 @@ namespace logger
sub_1401DAA40();
}
if (!game::environment::is_sp())
{
utils::hook::call(0x140501AE3, print_com_error);
}
com_error_hook.create(game::Com_Error, com_error_stub);
// Make havok script's print function actually print
utils::hook::jump(SELECT_VALUE(0x1406283A4, 0x140732184), print);

View File

@ -99,7 +99,7 @@ namespace map_rotation
console::error("%s: %s contains invalid data!\n", ex.what(), sv_map_rotation->name);
}
#ifdef _DEBUG
console::info("dedicated_rotation size after parsing is '%llu'\n", dedicated_rotation.get_entries_size());
console::info("dedicated_rotation size after parsing is '%llu'", dedicated_rotation.get_entries_size());
#endif
}

View File

@ -1,234 +0,0 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include "console.hpp"
#include "mods.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace mods
{
namespace
{
utils::hook::detour sys_create_file_hook;
void db_build_os_path_from_source(const char* zone_name, game::FF_DIR source, int size, char* filename)
{
char user_map[MAX_PATH]{};
switch (source)
{
case game::FFD_DEFAULT:
(void)game::Com_sprintf(filename, size, "%s\\%s.ff", std::filesystem::current_path().string().c_str(), zone_name);
break;
case game::FFD_MOD_DIR:
assert(mods::is_using_mods());
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), (*dvars::fs_gameDirVar)->current.string, zone_name);
break;
case game::FFD_USER_MAP:
game::I_strncpyz(user_map, zone_name, sizeof(user_map));
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), "usermaps", user_map, zone_name);
break;
default:
assert(false && "inconceivable");
break;
}
}
game::Sys_File sys_create_file_stub(const char* dir, const char* filename)
{
auto result = sys_create_file_hook.invoke<game::Sys_File>(dir, filename);
if (result.handle != INVALID_HANDLE_VALUE)
{
return result;
}
if (!is_using_mods())
{
return result;
}
// .ff extension was added previously
if (!std::strcmp(filename, "mod.ff") && mods::db_mod_file_exists())
{
char file_path[MAX_PATH]{};
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(file_path), file_path);
result.handle = game::Sys_OpenFileReliable(file_path);
}
return result;
}
void db_load_x_assets_stub(game::XZoneInfo* zone_info, unsigned int zone_count, game::DBSyncMode sync_mode)
{
std::vector<game::XZoneInfo> zones(zone_info, zone_info + zone_count);
if (db_mod_file_exists())
{
zones.emplace_back("mod", game::DB_ZONE_COMMON | game::DB_ZONE_CUSTOM, 0);
}
game::DB_LoadXAssets(zones.data(), static_cast<unsigned int>(zones.size()), sync_mode);
}
const auto skip_extra_zones_stub = utils::hook::assemble([](utils::hook::assembler& a)
{
const auto skip = a.newLabel();
const auto original = a.newLabel();
a.pushad64();
a.test(ebp, game::DB_ZONE_CUSTOM); // allocFlags
a.jnz(skip);
a.bind(original);
a.popad64();
a.mov(rdx, 0x140835F28);
a.mov(rcx, rsi);
a.call_aligned(strcmp);
a.jmp(0x1403217C0);
a.bind(skip);
a.popad64();
a.mov(r15d, 0x80);
a.not_(r15d);
a.and_(ebp, r15d);
a.jmp(0x1403217F6);
});
bool fs_game_dir_domain_func(game::dvar_t* dvar, game::DvarValue new_value)
{
if (*new_value.string == '\0')
{
return true;
}
if (game::I_strnicmp(new_value.string, "mods", 4) != 0)
{
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
return false;
}
if (5 < std::strlen(new_value.string) && (new_value.string[4] == '\\' || new_value.string[4] == '/'))
{
const auto* s1 = std::strstr(new_value.string, "..");
const auto* s2 = std::strstr(new_value.string, "::");
if (s1 == nullptr && s2 == nullptr)
{
return true;
}
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
return false;
}
// Invalid path specified
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
return false;
}
}
bool is_using_mods()
{
return (*dvars::fs_gameDirVar) && *(*dvars::fs_gameDirVar)->current.string;
}
bool db_mod_file_exists()
{
if (!*(*dvars::fs_gameDirVar)->current.string)
{
return false;
}
char filename[MAX_PATH]{};
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(filename), filename);
if (auto zone_file = game::Sys_OpenFileReliable(filename); zone_file != INVALID_HANDLE_VALUE)
{
::CloseHandle(zone_file);
return true;
}
return false;
}
class component final : public component_interface
{
public:
static_assert(sizeof(game::Sys_File) == 8);
void post_unpack() override
{
dvars::fs_gameDirVar = reinterpret_cast<game::dvar_t**>(SELECT_VALUE(0x145856D38, 0x147876000));
// Remove DVAR_INIT from fs_game
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x14041C085 + 2, 0x1404DDA45 + 2), SELECT_VALUE(game::DVAR_FLAG_NONE, game::DVAR_FLAG_SERVERINFO));
utils::hook::inject(SELECT_VALUE(0x14041C097 + 3, 0x1404DDA57 + 3), &fs_game_dir_domain_func);
if (game::environment::is_sp())
{
return;
}
// Don't load eng_ + patch_ with loadzone
utils::hook::nop(0x1403217B1, 15);
utils::hook::jump(0x1403217B1, skip_extra_zones_stub, true);
// Add custom zone paths
sys_create_file_hook.create(game::Sys_CreateFile, sys_create_file_stub);
// Load mod.ff
utils::hook::call(0x1405E7113, db_load_x_assets_stub); // R_LoadGraphicsAssets According to myself but I don't remember where I got it from
command::add("loadmod", [](const command::params& params) -> void
{
if (params.size() != 2)
{
console::info("USAGE: %s \"mods/<mod name>\"", params.get(0));
return;
}
std::string mod_name = utils::string::to_lower(params.get(1));
if (!mod_name.empty() && !mod_name.starts_with("mods/"))
{
mod_name = "mods/" + mod_name;
}
// change fs_game if needed
if (mod_name != (*dvars::fs_gameDirVar)->current.string)
{
game::Dvar_SetString((*dvars::fs_gameDirVar), mod_name.c_str());
command::execute("vid_restart\n");
}
});
command::add("unloadmod", [](const command::params& params) -> void
{
if (*dvars::fs_gameDirVar == nullptr || *(*dvars::fs_gameDirVar)->current.string == '\0')
{
return;
}
game::Dvar_SetString(*dvars::fs_gameDirVar, "");
command::execute("vid_restart\n");
});
// TODO: without a way to monitor all the ways fs_game can be changed there is no way to detect when we
// should unregister the path from the internal filesystem we use
// HINT: It could be done in fs_game_dir_domain_func, but I haven't tested if that's the best place to monitor for changes and register/unregister the mods folder
}
};
}
REGISTER_COMPONENT(mods::component)

View File

@ -1,7 +0,0 @@
#pragma once
namespace mods
{
bool is_using_mods();
bool db_mod_file_exists();
}

View File

@ -20,7 +20,7 @@ namespace network
return callbacks;
}
bool handle_command(game::netadr_s* address, const char* command, game::msg_t* message)
bool handle_command(game::netadr_t* address, const char* command, game::msg_t* message)
{
const auto cmd_string = utils::string::to_lower(command);
auto& callbacks = get_callbacks();
@ -61,7 +61,7 @@ namespace network
a.jmp(0x1402C64EE);
}
int net_compare_base_address(const game::netadr_s* a1, const game::netadr_s* a2)
int net_compare_base_address(const game::netadr_t* a1, const game::netadr_t* a2)
{
if (a1->type == a2->type)
{
@ -83,12 +83,12 @@ namespace network
return false;
}
int net_compare_address(const game::netadr_s* a1, const game::netadr_s* a2)
int net_compare_address(const game::netadr_t* a1, const game::netadr_t* a2)
{
return net_compare_base_address(a1, a2) && a1->port == a2->port;
}
void reconnect_migrated_client(game::mp::client_t*, game::netadr_s* from, const int, const int, const char*, const char*, bool)
void reconnect_migrated_client(game::mp::client_t*, game::netadr_t* from, const int, const int, const char*, const char*, bool)
{
// This happens when a client tries to rejoin after being recently disconnected, OR by a duplicated guid
// We don't want this to do anything. It decides to crash seemingly randomly
@ -107,14 +107,14 @@ namespace network
get_callbacks()[utils::string::to_lower(command)] = callback;
}
int dw_send_to_stub(const int size, const char* src, game::netadr_s* addr)
int dw_send_to_stub(const int size, const char* src, game::netadr_t* addr)
{
sockaddr s = {};
game::NetadrToSockadr(addr, &s);
return sendto(*game::query_socket, src, size, 0, &s, 16) >= 0;
}
void send(const game::netadr_s& address, const std::string& command, const std::string& data, const char separator)
void send(const game::netadr_t& address, const std::string& command, const std::string& data, const char separator)
{
std::string packet = "\xFF\xFF\xFF\xFF";
packet.append(command);
@ -124,7 +124,7 @@ namespace network
send_data(address, packet);
}
void send_data(const game::netadr_s& address, const std::string& data)
void send_data(const game::netadr_t& address, const std::string& data)
{
if (address.type == game::NA_LOOPBACK)
{
@ -136,12 +136,12 @@ namespace network
}
}
bool are_addresses_equal(const game::netadr_s& a, const game::netadr_s& b)
bool are_addresses_equal(const game::netadr_t& a, const game::netadr_t& b)
{
return net_compare_address(&a, &b);
}
const char* net_adr_to_string(const game::netadr_s& a)
const char* net_adr_to_string(const game::netadr_t& a)
{
if (a.type == game::NA_LOOPBACK)
{
@ -286,7 +286,7 @@ namespace network
if (!game::environment::is_dedi())
{
// we need this on the client for RCon
on("print", [](const game::netadr_s& address, const std::string& message)
on("print", [](const game::netadr_t& address, const std::string& message)
{
if (address != party::get_target())
{

View File

@ -2,23 +2,23 @@
namespace network
{
using callback = std::function<void(const game::netadr_s&, const std::string&)>;
using callback = std::function<void(const game::netadr_t&, const std::string&)>;
void on(const std::string& command, const callback& callback);
void send(const game::netadr_s& address, const std::string& command, const std::string& data = {}, char separator = ' ');
void send_data(const game::netadr_s& address, const std::string& data);
void send(const game::netadr_t& address, const std::string& command, const std::string& data = {}, char separator = ' ');
void send_data(const game::netadr_t& address, const std::string& data);
bool are_addresses_equal(const game::netadr_s& a, const game::netadr_s& b);
bool are_addresses_equal(const game::netadr_t& a, const game::netadr_t& b);
const char* net_adr_to_string(const game::netadr_s& a);
const char* net_adr_to_string(const game::netadr_t& a);
}
inline bool operator==(const game::netadr_s& a, const game::netadr_s& b)
inline bool operator==(const game::netadr_t& a, const game::netadr_t& b)
{
return network::are_addresses_equal(a, b); //
}
inline bool operator!=(const game::netadr_s& a, const game::netadr_s& b)
inline bool operator!=(const game::netadr_t& a, const game::netadr_t& b)
{
return !(a == b); //
}
@ -26,20 +26,20 @@ inline bool operator!=(const game::netadr_s& a, const game::netadr_s& b)
namespace std
{
template <>
struct equal_to<game::netadr_s>
struct equal_to<game::netadr_t>
{
using result_type = bool;
bool operator()(const game::netadr_s& lhs, const game::netadr_s& rhs) const
bool operator()(const game::netadr_t& lhs, const game::netadr_t& rhs) const
{
return network::are_addresses_equal(lhs, rhs);
}
};
template <>
struct hash<game::netadr_s>
struct hash<game::netadr_t>
{
size_t operator()(const game::netadr_s& x) const noexcept
size_t operator()(const game::netadr_t& x) const noexcept
{
return hash<uint32_t>()(*reinterpret_cast<const uint32_t*>(&x.ip[0])) ^ hash<uint16_t>()(x.port);
}

View File

@ -61,7 +61,7 @@ namespace notifies
scheduler::once([params, message, client_num]
{
const auto* guid = game::SV_GetGuid(client_num);
const auto* name = game::mp::svs_clients[client_num].name;
const auto* name = game::mp::svs->clients[client_num].name;
game_log::g_log_printf("%s;%s;%i;%s;%s\n",
params.get(0),
@ -126,7 +126,7 @@ namespace notifies
a.lea(eax, dword_ptr(r15, -0x17));
a.mov(dword_ptr(rbp, 0x60), r15d);
a.jmp(SELECT_VALUE(0x1403DF5B3, 0x14043A593));
a.jmp(0x14043A593);
a.bind(replace);
@ -174,22 +174,22 @@ namespace notifies
public:
void post_unpack() override
{
utils::hook::jump(SELECT_VALUE(0x1403DF5A4, 0x14043A584), utils::hook::assemble(vm_execute_stub), true);
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
{
if (clear_scripts && post_shutdown)
{
vm_execute_hooks.clear();
}
});
if (game::environment::is_sp())
{
return;
}
utils::hook::call(0x1404724DD, client_command_stub);
utils::hook::jump(0x14043A584, utils::hook::assemble(vm_execute_stub), true);
scripting::on_shutdown([](const bool free_scripts)
{
if (free_scripts)
{
vm_execute_hooks.clear();
}
});
}
};
}

View File

@ -2,7 +2,6 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp"
#include "console.hpp"
@ -27,7 +26,7 @@ namespace party
{
struct
{
game::netadr_s host{};
game::netadr_t host{};
std::string challenge{};
bool hostDefined{false};
} connect_state;
@ -38,7 +37,7 @@ namespace party
int sv_maxclients;
void connect_to_party(const game::netadr_s& target, const std::string& mapname, const std::string& gametype)
void connect_to_party(const game::netadr_t& target, const std::string& mapname, const std::string& gametype)
{
if (game::environment::is_sp())
{
@ -59,7 +58,7 @@ namespace party
// CL_ConnectFromParty
char session_info[0x100] = {};
reinterpret_cast<void(*)(int, char*, const game::netadr_s*, const char*, const char*)>(0x1402C5700)(
reinterpret_cast<void(*)(int, char*, const game::netadr_t*, const char*, const char*)>(0x1402C5700)(
0, session_info, &target, mapname.data(), gametype.data());
}
@ -82,26 +81,6 @@ namespace party
a.mov(ecx, 2);
a.jmp(0x1402C617D);
});
utils::info_string get_info()
{
utils::info_string info;
info.set("gamename", "IW6");
info.set("hostname", dvars::get_string("sv_hostname"));
info.set("gametype", dvars::get_string("g_gametype"));
info.set("sv_motd", dvars::get_string("sv_motd"));
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
info.set("mapname", dvars::get_string("mapname"));
info.set("isPrivate", dvars::get_string("g_password").empty() ? "0" : "1");
info.set("clients", std::to_string(get_client_count()));
info.set("bots", std::to_string(get_bot_count()));
info.set("sv_maxclients", std::to_string(*game::mp::svs_clientCount));
info.set("protocol", std::to_string(PROTOCOL));
info.set("shortversion", SHORTVERSION);
return info;
}
}
void switch_gamemode_if_necessary(const std::string& gametype)
@ -141,9 +120,9 @@ namespace party
int get_client_count()
{
auto count = 0;
for (auto i = 0; i < *game::mp::svs_clientCount; ++i)
for (auto i = 0; i < game::mp::svs->clientCount; ++i)
{
if (game::mp::svs_clients[i].header.state >= game::CS_CONNECTED)
if (game::mp::svs->clients[i].header.state >= game::CS_CONNECTED)
{
++count;
}
@ -155,10 +134,10 @@ namespace party
int get_bot_count()
{
auto count = 0;
for (auto i = 0; i < *game::mp::svs_clientCount; ++i)
for (auto i = 0; i < game::mp::svs->clientCount; ++i)
{
if (game::mp::svs_clients[i].header.state >= game::CS_CONNECTED &&
game::mp::svs_clients[i].testClient != game::TC_NONE)
if (game::mp::svs->clients[i].header.state >= game::CS_CONNECTED &&
game::mp::svs->clients[i].testClient != game::TC_NONE)
{
++count;
}
@ -167,7 +146,7 @@ namespace party
return count;
}
game::netadr_s& get_target()
game::netadr_t& get_target()
{
return connect_state.host;
}
@ -179,27 +158,24 @@ namespace party
int get_client_num_from_name(const std::string& name)
{
for (auto i = 0; !name.empty() && i < *game::mp::svs_clientCount; ++i)
for (auto i = 0; !name.empty() && i < game::mp::svs->clientCount; ++i)
{
if (!game::mp::g_entities[i].client)
if (game::mp::g_entities[i].client)
{
continue;
}
char client_name[16] = {0};
strncpy_s(client_name, game::mp::g_entities[i].client->sess.cs.name, sizeof(client_name));
game::I_CleanStr(client_name);
char client_name[16]{};
game::I_strncpyz(client_name, game::mp::g_entities[i].client->sess.cs.name, sizeof(client_name));
game::I_CleanStr(client_name);
if (client_name == name)
{
return i;
if (client_name == name)
{
return i;
}
}
}
return -1;
}
void connect(const game::netadr_s& target)
void connect(const game::netadr_t& target)
{
if (game::environment::is_sp())
{
@ -276,7 +252,7 @@ namespace party
return;
}
game::netadr_s target{};
game::netadr_t target{};
if (game::NET_StringToAdr(params[1], &target))
{
connect(target);
@ -307,7 +283,7 @@ namespace party
}
const auto client_num = atoi(params.get(1));
if (client_num < 0 || client_num >= *game::mp::svs_clientCount)
if (client_num < 0 || client_num >= game::mp::svs->clientCount)
{
return;
}
@ -344,7 +320,7 @@ namespace party
const std::string name = params.get(1);
if (name == "all"s)
{
for (auto i = 0; i < *game::mp::svs_clientCount; ++i)
for (auto i = 0; i < game::mp::svs->clientCount; ++i)
{
scheduler::once([i, reason]
{
@ -355,7 +331,7 @@ namespace party
}
const auto client_num = get_client_num_from_name(name);
if (client_num < 0 || client_num >= *game::mp::svs_clientCount)
if (client_num < 0 || client_num >= game::mp::svs->clientCount)
{
return;
}
@ -384,7 +360,7 @@ namespace party
const auto message = params.join(2);
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
game::SV_GameSendServerCommand(client_num, 0, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
console::info("%s -> %i: %s\n", name, client_num, message.data());
});
@ -398,7 +374,7 @@ namespace party
const auto client_num = atoi(params.get(1));
const auto message = params.join(2);
game::engine::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data()));
game::SV_GameSendServerCommand(client_num, 0, utils::string::va("%c \"%s\"", 84, message.data()));
console::info("%i: %s\n", client_num, message.data());
});
@ -412,7 +388,7 @@ namespace party
const auto message = params.join(1);
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
game::SV_GameSendServerCommand(-1, 0, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
console::info("%s: %s\n", name, message.data());
});
@ -425,67 +401,36 @@ namespace party
const auto message = params.join(1);
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data()));
game::SV_GameSendServerCommand(-1, 0, utils::string::va("%c \"%s\"", 84, message.data()));
console::info("%s\n", message.data());
});
network::on("getInfo", [](const game::netadr_s& target, const std::string& data)
network::on("getInfo", [](const game::netadr_t& target, const std::string& data)
{
utils::info_string info = get_info();
utils::info_string info;
info.set("challenge", data);
info.set("gamename", "IW6");
info.set("hostname", dvars::get_string("sv_hostname"));
info.set("gametype", dvars::get_string("g_gametype"));
info.set("sv_motd", dvars::get_string("sv_motd"));
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
info.set("mapname", dvars::get_string("mapname"));
info.set("isPrivate", dvars::get_string("g_password").empty() ? "0" : "1");
info.set("clients", std::to_string(get_client_count()));
info.set("bots", std::to_string(get_bot_count()));
info.set("sv_maxclients", std::to_string(game::mp::svs->clientCount));
info.set("protocol", std::to_string(PROTOCOL));
info.set("shortversion", SHORTVERSION);
network::send(target, "infoResponse", info.build(), '\n');
});
network::on("getStatus", [](const game::netadr_s& target, const std::string& data)
{
std::string player_list;
utils::info_string info = get_info();
info.set("challenge", data);
const auto* sv_running = game::Dvar_FindVar("sv_running");
if (!sv_running || !sv_running->current.enabled)
{
return;
}
for (auto i = 0; i < game::Dvar_FindVar("sv_maxclients")->current.integer; ++i)
{
auto* client = &game::mp::svs_clients[i];
auto* self = &game::mp::g_entities[i];
if (client->header.state < game::CS_ACTIVE)
{
continue;
}
if (!self || !self->client)
{
continue;
}
if (game::SV_BotIsBot(i))
{
continue;
}
const auto score = game::G_GetClientScore(i);
const auto ping = game::mp::svs_clients[i].ping;
const std::string name = game::mp::svs_clients[i].name;
player_list.append(std::format("{} {} \"{}\"\n", score, ping, name));
}
network::send(target, "statusResponse", info.build() + "\n"s + player_list + "\n"s, '\n');
});
if (game::environment::is_dedi())
{
return;
}
network::on("infoResponse", [](const game::netadr_s& target, const std::string& data)
network::on("infoResponse", [](const game::netadr_t& target, const std::string& data)
{
const utils::info_string info(data);
server_list::handle_info_response(target, info);

View File

@ -7,7 +7,7 @@ namespace party
void reset_connect_state();
void connect(const game::netadr_s& target);
void connect(const game::netadr_t& target);
void map_restart();
[[nodiscard]] int server_client_count();
@ -15,5 +15,5 @@ namespace party
[[nodiscard]] int get_client_count();
[[nodiscard]] int get_bot_count();
[[nodiscard]] game::netadr_s& get_target();
[[nodiscard]] game::netadr_t& get_target();
}

View File

@ -2,7 +2,6 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "game/engine/sv_game.hpp"
#include "command.hpp"
#include "console.hpp"
@ -73,10 +72,8 @@ namespace patches
if (exec_params.size() == 2)
{
std::string file_name = exec_params.get(1);
if (!file_name.ends_with(".cfg"))
{
if (file_name.find(".cfg") == std::string::npos)
file_name.append(".cfg");
}
const auto file = filesystem::file(file_name);
if (file.exists())
@ -153,7 +150,7 @@ namespace patches
return 0;
}
game::Font_s* get_chat_font_handle()
game::Font_t* get_chat_font_handle()
{
return game::R_RegisterFont("fonts/bigFont");
}
@ -190,11 +187,11 @@ namespace patches
}
utils::hook::detour cmd_lui_notify_server_hook;
void cmd_lui_notify_server_stub(game::mp::gentity_s* ent)
void cmd_lui_notify_server_stub(game::mp::gentity_t* ent)
{
command::params_sv params{};
const auto menu_id = atoi(params.get(1));
const auto client = &game::mp::svs_clients[ent->s.clientNum];
const auto client = &game::mp::svs->clients[ent->s.clientNum];
// 9 => "end_game"
if (menu_id == 9 && client->header.netchan.remoteAddress.type != game::NA_LOOPBACK)
@ -293,12 +290,6 @@ namespace patches
static void patch_mp()
{
// Bypass Arxan function
utils::hook::nop(0x1404758C0, 16);
utils::hook::jump(0x1404758C0, game::engine::SV_GameSendServerCommand, true);
utils::hook::call(0x140477399, game::engine::SV_SendServerCommand);
// Register dvars
com_register_dvars_hook.create(0x140413A90, &com_register_dvars_stub);
@ -335,10 +326,6 @@ namespace patches
dvars::override::register_int("igs_s1", 1, 0, 1, 0);
dvars::override::register_int("igs_crossgame", 1, 0, 1, 0);
// Required by UI scripts. Missing when joining a dedi and causes crashes
game::Dvar_RegisterInt("scr_gun_winlimit", 1, 0, 10, game::DVAR_FLAG_REPLICATED, "Win limit for Gun Game");
game::Dvar_RegisterInt("scr_gun_scorelimit", 18, 1, 1000, game::DVAR_FLAG_REPLICATED, "Score limit for Gun Game");
// Patch game chat on resolutions higher than 1080p to use the right font
utils::hook::call(0x14025C825, get_chat_font_handle);
utils::hook::call(0x1402BC42F, get_chat_font_handle);
@ -370,9 +357,6 @@ namespace patches
utils::hook::nop(0x1403A1A0F, 1);
// ^^
utils::hook::nop(0x1403A072F, 5); // LiveStorage_RecordMovementInMatchdata
// Disable Com_Error in NET_SendPacket
utils::hook::nop(0x140501AE3, 5);
}
static void patch_sp()

View File

@ -14,11 +14,11 @@ namespace rcon
namespace
{
bool is_redirecting_ = false;
game::netadr_s redirect_target_ = {};
game::netadr_t redirect_target_ = {};
std::string redirect_buffer = {};
std::recursive_mutex redirect_lock;
void setup_redirect(const game::netadr_s& target)
void setup_redirect(const game::netadr_t& target)
{
std::lock_guard<std::recursive_mutex> $(redirect_lock);
@ -50,28 +50,26 @@ namespace rcon
for (int i = 0; i < sv_maxclients->current.integer; i++)
{
const auto client = &game::mp::svs_clients[i];
const auto client = &game::mp::svs->clients[i];
auto self = &game::mp::g_entities[i];
if (client->header.state == game::CS_FREE || !self || !self->client)
{
continue;
}
char clean_name[32]{};
game::I_strncpyz(clean_name, self->client->sess.cs.name, sizeof(clean_name));
strncpy_s(clean_name, self->client->sess.cs.name, sizeof(clean_name));
game::I_CleanStr(clean_name);
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
i,
self->client->sess.scores.score,
game::SV_BotIsBot(i) ? "Yes" : "No",
(client->header.state == game::CS_RECONNECTING) ? "CNCT" : (client->header.state == game::CS_ZOMBIE) ? "ZMBI" : utils::string::va("%4i", client->ping),
game::SV_GetGuid(i),
clean_name,
network::net_adr_to_string(client->header.netchan.remoteAddress),
client->header.netchan.remoteAddress.port)
);
if (client->header.state > game::CS_FREE && self && self->client)
{
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
i,
self->client->sess.scores.score,
game::SV_BotIsBot(i) ? "Yes" : "No",
(client->header.state == game::CS_RECONNECTING) ? "CNCT" : (client->header.state == game::CS_ZOMBIE) ? "ZMBI" : utils::string::va("%4i", client->ping),
game::SV_GetGuid(i),
clean_name,
network::net_adr_to_string(client->header.netchan.remoteAddress),
client->header.netchan.remoteAddress.port)
);
}
}
return buffer;
@ -94,7 +92,7 @@ namespace rcon
if (*reinterpret_cast<std::int32_t*>(0x1419E1AE0) >= 5) //clientUIActive.connectionState >= CA_CONNECTED
{
const auto target = *reinterpret_cast<game::netadr_s*>(0x141CB535C);
const auto target = *reinterpret_cast<game::netadr_t*>(0x141CB535C);
const auto buffer = password + " " + data;
network::send(target, "rcon", buffer);
}
@ -172,7 +170,7 @@ namespace rcon
}
else
{
network::on("rcon", [](const game::netadr_s& addr, const std::string& data)
network::on("rcon", [](const game::netadr_t& addr, const std::string& data)
{
const auto pos = data.find_first_of(" ");
if (pos == std::string::npos)

View File

@ -34,7 +34,7 @@ namespace scripting
std::unordered_map<unsigned int, std::string> canonical_string_table;
std::vector<std::function<void(int, int)>> shutdown_callbacks;
std::vector<std::function<void(int)>> shutdown_callbacks;
std::vector<std::function<void()>> init_callbacks;
void scr_load_level_stub()
@ -47,9 +47,9 @@ namespace scripting
}
}
void g_shutdown_game_stub(const int clear_scripts)
void g_shutdown_game_stub(const int free_scripts)
{
if (clear_scripts)
if (free_scripts)
{
script_function_table_sort.clear();
script_function_table.clear();
@ -59,15 +59,10 @@ namespace scripting
for (const auto& callback : shutdown_callbacks)
{
callback(clear_scripts ,false);
callback(free_scripts);
}
g_shutdown_game_hook.invoke<void>(clear_scripts);
for (const auto& callback : shutdown_callbacks)
{
callback(clear_scripts, true);
}
return g_shutdown_game_hook.invoke<void>(free_scripts);
}
void process_script_stub(const char* filename)
@ -170,7 +165,7 @@ namespace scripting
return find_token(id);
}
void on_shutdown(const std::function<void(int, int)>& callback)
void on_shutdown(const std::function<void(int)>& callback)
{
shutdown_callbacks.push_back(callback);
}

View File

@ -8,7 +8,7 @@ namespace scripting
extern std::string current_file;
void on_shutdown(const std::function<void(int, int)>& callback);
void on_shutdown(const std::function<void(int)>& callback);
void on_init(const std::function<void()>& callback);
std::optional<std::string> get_canonical_string(unsigned int id);

View File

@ -10,8 +10,6 @@ namespace security
{
namespace
{
utils::hook::detour ui_replace_directive_hook;
void set_cached_playerdata_stub(const int localclient, const int index1, const int index2)
{
if (index1 >= 0 && index1 < 18 && index2 >= 0 && index2 < 42)
@ -32,71 +30,6 @@ namespace security
utils::hook::invoke<void>(0x140472500, client, msg);
}
void ui_replace_directive_stub(const int local_client_num, const char* src_string, char* dst_string, const int dst_buffer_size)
{
assert(src_string);
if (!src_string)
{
return;
}
assert(dst_string);
if (!dst_string)
{
return;
}
assert(dst_buffer_size > 0);
if (dst_buffer_size <= 0)
{
return;
}
constexpr std::size_t MAX_HUDELEM_TEXT_LEN = 0x100;
if (std::strlen(src_string) > MAX_HUDELEM_TEXT_LEN)
{
return;
}
ui_replace_directive_hook.invoke<void>(local_client_num, src_string, dst_string, dst_buffer_size);
}
int hud_elem_set_enum_string_stub(char* string, const char* format, ...)
{
va_list ap;
va_start(ap, format);
const auto len = vsnprintf(string, 0x800, format, ap);
va_end(ap);
string[0x800 - 1] = '\0';
return len;
}
int sv_add_bot_stub(char* string, const char* format, ...)
{
va_list ap;
va_start(ap, format);
const auto len = vsnprintf(string, 0x400, format, ap);
va_end(ap);
string[0x400 - 1] = '\0';
return len;
}
int sv_add_test_client_stub(char* string, const char* format, ...)
{
va_list ap;
va_start(ap, format);
const auto len = vsnprintf(string, 0x400, format, ap);
va_end(ap);
string[0x400 - 1] = '\0';
return len;
}
}
class component final : public component_interface
@ -104,22 +37,13 @@ namespace security
public:
void post_unpack() override
{
// sprinf
utils::hook::call(SELECT_VALUE(0x140310D0F, 0x140399B0F), hud_elem_set_enum_string_stub);
if (game::environment::is_sp()) return;
// Patch vulnerability in PlayerCards_SetCachedPlayerData
utils::hook::call(0x140287C5C, set_cached_playerdata_stub);
// sprinf
utils::hook::call(0x140470A88, sv_add_bot_stub);
utils::hook::call(0x140470F68, sv_add_test_client_stub);
// It is possible to make the server hang if left unchecked
utils::hook::call(0x14047A29A, sv_execute_client_message_stub);
ui_replace_directive_hook.create(0x1404D8A00, ui_replace_directive_stub);
}
};
}

View File

@ -33,14 +33,14 @@ namespace server_list
std::string map_name;
std::string game_type;
char in_game;
game::netadr_s address;
game::netadr_t address;
};
struct
{
game::netadr_s address{};
game::netadr_t address{};
volatile bool requesting = false;
std::unordered_map<game::netadr_s, int> queued_servers{};
std::unordered_map<game::netadr_t, int> queued_servers{};
} master_state;
volatile bool update_server_list = false;
@ -307,12 +307,12 @@ namespace server_list
}
}
bool get_master_server(game::netadr_s& address)
bool get_master_server(game::netadr_t& address)
{
return game::NET_StringToAdr("server.alterware.dev:20810", &address);
}
void handle_info_response(const game::netadr_s& address, const utils::info_string& info)
void handle_info_response(const game::netadr_t& address, const utils::info_string& info)
{
int start_time{};
const auto now = game::Sys_Milliseconds();
@ -436,7 +436,7 @@ namespace server_list
scheduler::loop(do_frame_work, scheduler::pipeline::main);
network::on("getServersResponse", [](const game::netadr_s& target, const std::string& data)
network::on("getServersResponse", [](const game::netadr_t& target, const std::string& data)
{
{
std::lock_guard<std::mutex> _(mutex);
@ -469,7 +469,7 @@ namespace server_list
break;
}
game::netadr_s address{};
game::netadr_t address{};
address.type = game::NA_IP;
address.localNetID = game::NS_CLIENT1;
std::memcpy(&address.ip[0], data.data() + i + 0, 4);

View File

@ -3,8 +3,8 @@
namespace server_list
{
bool get_master_server(game::netadr_s& address);
void handle_info_response(const game::netadr_s& address, const utils::info_string& info);
bool get_master_server(game::netadr_t& address);
void handle_info_response(const game::netadr_t& address, const utils::info_string& info);
bool sl_key_event(int key, int down);
}

View File

@ -50,8 +50,8 @@ namespace slowmotion
for (auto i = 0; i < game::Dvar_FindVar("sv_maxclients")->current.integer; i++)
{
auto* client = &game::mp::svs_clients[i];
client->nextSnapshotTime = *game::mp::serverTime - 1;
auto* client = &game::mp::svs->clients[i];
client->nextSnapshotTime = game::mp::svs->time - 1;
}
}
}
@ -66,7 +66,7 @@ namespace slowmotion
utils::hook::jump(0x1403B4A10, scr_cmd_set_slow_motion);
// Detour used here instead of call hook because Com_TimeScaleMsec is called from arxan encrypted function
com_timescale_msec_hook.create(0x140415D50, com_timescale_msec);
com_timescale_msec_hook.create(game::Com_TimeScaleMsec, com_timescale_msec);
}
};
}

View File

@ -26,8 +26,6 @@ namespace steam_proxy
ownership_state state_;
utils::binary_resource runner_file(RUNNER, "runner.exe");
bool is_disabled() { return true; }
}
class component final : public component_interface
@ -35,7 +33,7 @@ namespace steam_proxy
public:
void post_load() override
{
if (game::environment::is_dedi() || is_disabled())
if (game::environment::is_dedi())
{
return;
}

View File

@ -195,27 +195,6 @@ namespace ui_scripting
setup_functions();
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x14017B120)); // hks::base_print
lua["directoryexists"] = [](const std::string& string)
{
return utils::io::directory_exists(string);
};
lua["listfiles"] = [](const std::string& string)
{
return utils::io::list_files(string);
};
lua["directoryisempty"] = [](const std::string& string)
{
return utils::io::directory_is_empty(string);
};
lua["fileexists"] = [](const std::string& string)
{
return utils::io::file_exists(string);
};
lua["table"]["unpack"] = lua["unpack"];
lua["luiglobals"] = lua;

View File

@ -30,7 +30,7 @@ namespace demonware
uint64_t file_id;
uint32_t create_time;
uint32_t modified_time;
bool visibility;
bool priv;
uint64_t owner_id;
std::string filename;
uint32_t file_size;
@ -41,7 +41,7 @@ namespace demonware
buffer->write_uint64(this->file_id);
buffer->write_uint32(this->create_time);
buffer->write_uint32(this->modified_time);
buffer->write_bool(this->visibility);
buffer->write_bool(this->priv);
buffer->write_uint64(this->owner_id);
buffer->write_string(this->filename);
}
@ -52,7 +52,7 @@ namespace demonware
buffer->read_uint64(&this->file_id);
buffer->read_uint32(&this->create_time);
buffer->read_uint32(&this->modified_time);
buffer->read_bool(&this->visibility);
buffer->read_bool(&this->priv);
buffer->read_uint64(&this->owner_id);
buffer->read_string(&this->filename);
}

View File

@ -10,35 +10,19 @@ namespace demonware
this->register_service(4, &bdGroup::get_groups);
}
void bdGroup::set_groups(i_server* server, byte_buffer* buffer)
void bdGroup::set_groups(i_server* server, byte_buffer* /*buffer*/) const
{
uint32_t entries_count{};
buffer->read_array_header(game::BD_BB_UNSIGNED_INTEGER32_TYPE, &entries_count);
//uint32_t groupCount;
// TODO: Implement array reading
auto reply = server->create_reply(this->get_sub_type());
buffer->set_use_data_types(false);
for (uint32_t i = 0; i < entries_count; ++i)
{
uint32_t group_id{};
buffer->read_uint32(&group_id);
if (group_id < ARRAYSIZE(this->groups))
{
this->groups[group_id] = 999;
}
}
buffer->set_use_data_types(true);
reply->send();
}
void bdGroup::get_groups(i_server* server, byte_buffer* buffer)
{
uint32_t group_count;
buffer->read_array_header(game::BD_BB_UNSIGNED_INTEGER32_TYPE, &group_count);
buffer->read_array_header(8, &group_count);
auto reply = server->create_reply(this->get_sub_type());

View File

@ -9,7 +9,7 @@ namespace demonware
bdGroup();
private:
void set_groups(i_server* server, byte_buffer* buffer);
void set_groups(i_server* server, byte_buffer* buffer) const;
void get_groups(i_server* server, byte_buffer* buffer);
uint32_t groups[512]{};

View File

@ -119,11 +119,11 @@ namespace demonware
void bdStorage::set_legacy_user_file(i_server* server, byte_buffer* buffer) const
{
bool visibility;
bool priv;
std::string filename, data;
buffer->read_string(&filename);
buffer->read_bool(&visibility);
buffer->read_bool(&priv);
buffer->read_blob(&data);
const auto id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
@ -139,11 +139,11 @@ namespace demonware
info->file_id = id;
info->filename = filename;
info->create_time = static_cast<uint32_t>(std::time(nullptr));
info->create_time = uint32_t(time(nullptr));
info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size());
info->file_size = uint32_t(data.size());
info->owner_id = 0;
info->visibility = visibility;
info->priv = priv;
auto reply = server->create_reply(this->get_sub_type());
reply->add(info);
@ -170,11 +170,11 @@ namespace demonware
info->file_id = id;
info->filename = "<>";
info->create_time = static_cast<uint32_t>(std::time(nullptr));
info->create_time = uint32_t(time(nullptr));
info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size());
info->file_size = uint32_t(data.size());
info->owner_id = 0;
info->visibility = false;
info->priv = false;
auto reply = server->create_reply(this->get_sub_type());
reply->add(info);
@ -207,12 +207,12 @@ namespace demonware
void bdStorage::list_legacy_user_files(i_server* server, byte_buffer* buffer) const
{
uint64_t owner;
uint64_t unk;
uint32_t date;
uint16_t num_results, offset;
std::string filename, data;
buffer->read_uint64(&owner);
buffer->read_uint64(&unk);
buffer->read_uint32(&date);
buffer->read_uint16(&num_results);
buffer->read_uint16(&offset);
@ -229,9 +229,9 @@ namespace demonware
info->filename = filename;
info->create_time = 0;
info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size());
info->owner_id = owner;
info->visibility = false;
info->file_size = uint32_t(data.size());
info->owner_id = 0;
info->priv = false;
reply->add(info);
}
@ -260,9 +260,9 @@ namespace demonware
info->filename = filename;
info->create_time = 0;
info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size());
info->file_size = uint32_t(data.size());
info->owner_id = 0;
info->visibility = false;
info->priv = false;
reply->add(info);
}
@ -312,13 +312,13 @@ namespace demonware
void bdStorage::set_user_file(i_server* server, byte_buffer* buffer) const
{
bool visibility;
bool priv;
uint64_t owner;
std::string game, filename, data;
buffer->read_string(&game);
buffer->read_string(&filename);
buffer->read_bool(&visibility);
buffer->read_bool(&priv);
buffer->read_blob(&data);
buffer->read_uint64(&owner);
@ -329,11 +329,11 @@ namespace demonware
info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
info->filename = filename;
info->create_time = static_cast<uint32_t>(std::time(nullptr));
info->create_time = uint32_t(time(nullptr));
info->modified_time = info->create_time;
info->file_size = static_cast<uint32_t>(data.size());
info->file_size = uint32_t(data.size());
info->owner_id = owner;
info->visibility = visibility;
info->priv = priv;
auto reply = server->create_reply(this->get_sub_type());
reply->add(info);

View File

@ -16,7 +16,6 @@ namespace dvars
game::dvar_t* con_inputCmdMatchColor = nullptr;
game::dvar_t* sv_cheats = nullptr;
game::dvar_t* sv_botsAutoJoin = nullptr;
game::dvar_t* g_playerEjection = nullptr;
game::dvar_t* g_playerCollision = nullptr;
@ -25,7 +24,6 @@ namespace dvars
game::dvar_t* g_rocketPushbackScale = nullptr;
game::dvar_t* g_enableElevators = nullptr;
game::dvar_t* g_dump_scripts = nullptr;
game::dvar_t* g_dump_string_tables = nullptr;
game::dvar_t* g_log = nullptr;
game::dvar_t* bg_surfacePenetration = nullptr;
@ -48,9 +46,7 @@ namespace dvars
game::dvar_t* cg_legacyCrashHandling = nullptr;
game::dvar_t* com_developer_script = nullptr;
game::dvar_t** com_developer = nullptr;
game::dvar_t** fs_gameDirVar = nullptr;
game::dvar_t** com_developer;
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain)
{

View File

@ -15,7 +15,6 @@ namespace dvars
extern game::dvar_t* con_inputCmdMatchColor;
extern game::dvar_t* sv_cheats;
extern game::dvar_t* sv_botsAutoJoin;
extern game::dvar_t* g_playerCollision;
extern game::dvar_t* g_playerEjection;
@ -24,7 +23,6 @@ namespace dvars
extern game::dvar_t* g_rocketPushbackScale;
extern game::dvar_t* g_enableElevators;
extern game::dvar_t* g_dump_scripts;
extern game::dvar_t* g_dump_string_tables;
extern game::dvar_t* g_log;
extern game::dvar_t* bg_surfacePenetration;
@ -49,8 +47,6 @@ namespace dvars
extern game::dvar_t* com_developer_script;
extern game::dvar_t** com_developer;
extern game::dvar_t** fs_gameDirVar;
std::string dvar_get_vector_domain(int components, const game::dvar_limits& domain);
std::string dvar_get_domain(game::dvar_type type, const game::dvar_limits& domain);
}

View File

@ -1,206 +0,0 @@
#include <std_include.hpp>
#include <game/game.hpp>
#include "sv_game.hpp"
#include <component/console.hpp>
#include <utils/string.hpp>
namespace game::engine
{
char* SV_ExpandNewlines(char* in)
{
static char string[1024];
unsigned int l = 0;
while (*in && l < sizeof(string) - 3)
{
if (*in == '\n')
{
string[l++] = '\\';
string[l++] = 'n';
}
else
{
if (*in != '\x14' && *in != '\x15')
{
string[l++] = *in;
}
}
++in;
}
string[l] = '\0';
return string;
}
void SV_CullIgnorableServerCommands(mp::client_t* client)
{
int to = client->reliableSent + 1;
for (int from = to; from <= client->reliableSequence; ++from)
{
int from_index = from & 0x7F;
assert(client->netBuf.reliableCommandInfo[from_index].time >= 0);
if (client->netBuf.reliableCommandInfo[from_index].type)
{
int to_index = to & 0x7F;
if (to_index != from_index)
{
client->netBuf.reliableCommandInfo[to_index] = client->netBuf.reliableCommandInfo[from_index];
}
++to;
}
}
client->reliableSequence = to - 1;
}
void SV_DelayDropClient(mp::client_t* drop, const char* reason)
{
assert(drop);
assert(reason);
assert(drop->header.state != CS_FREE);
if (drop->header.state == CS_ZOMBIE)
{
#ifdef _DEBUG
console::info("(drop->dropReason) = %s", drop->dropReason);
#endif
}
else if (!drop->dropReason)
{
drop->dropReason = reason;
}
}
void SV_AddServerCommand(mp::client_t* client, svscmd_type type, const char* cmd)
{
static_assert(offsetof(mp::client_t, netBuf.reliableCommandInfo[0].cmd) == 0xC44);
if (client->testClient == TC_BOT)
{
return;
}
if (client->reliableSequence - client->reliableAcknowledge < 64 && client->header.state == CS_ACTIVE || (SV_CullIgnorableServerCommands(client), type))
{
int len = static_cast<int>(std::strlen(cmd)) + 1;
int to = SV_CanReplaceServerCommand(client, reinterpret_cast<const unsigned char*>(cmd), len);
if (to < 0)
{
++client->reliableSequence;
}
else
{
int from = to + 1;
while (from <= client->reliableSequence)
{
client->netBuf.reliableCommandInfo[to & 0x7F] = client->netBuf.reliableCommandInfo[from & 0x7F];
++from;
++to;
}
}
if (client->reliableSequence - client->reliableAcknowledge == 129)
{
#ifdef _DEBUG
console::info("===== pending server commands =====\n");
int i = 0;
for (i = client->reliableAcknowledge + 1; i <= client->reliableSequence; ++i)
{
console::info("cmd %5d: %8d: %s\n", i, client->netBuf.reliableCommandInfo[i & 0x7F].time, client->netBuf.reliableCommandInfo[i & 0x7F].cmd);
}
console::info("cmd %5d: %8d: %s\n", i, *game::mp::serverTime, cmd);
#endif
NET_OutOfBandPrint(NS_SERVER, &client->header.netchan.remoteAddress, "disconnect");
SV_DelayDropClient(client, "EXE_SERVERCOMMANDOVERFLOW");
type = SV_CMD_RELIABLE;
cmd = utils::string::va("%c \"EXE_SERVERCOMMANDOVERFLOW\"", 'r');
}
int index = client->reliableSequence & 0x7F;
MSG_WriteReliableCommandToBuffer(cmd, client->netBuf.reliableCommandInfo[index].cmd, sizeof(client->netBuf.reliableCommandInfo[index].cmd));
client->netBuf.reliableCommandInfo[index].time = *game::mp::serverTime;
client->netBuf.reliableCommandInfo[index].type = type;
}
}
void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...)
{
mp::client_t* client;
int j, len;
va_list va;
const auto server_command_buf_large = std::make_unique<char[]>(0x20000);
va_start(va, fmt);
len = vsnprintf(server_command_buf_large.get(), 0x20000, fmt, va);
va_end(va);
assert(len >= 0);
if (cl)
{
SV_AddServerCommand(cl, type, server_command_buf_large.get());
return;
}
if (environment::is_dedi() && !std::strncmp(server_command_buf_large.get(), "print", 5))
{
console::info("broadcast: %s\n", SV_ExpandNewlines(server_command_buf_large.get()));
}
const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
for (j = 0, client = mp::svs_clients; j < sv_maxclients->current.integer; j++, client++)
{
if (client->header.state < CS_CLIENTLOADING)
{
continue;
}
SV_AddServerCommand(client, type, server_command_buf_large.get());
}
}
void SV_GameSendServerCommand(int clientNum, svscmd_type type, const char* text)
{
[[maybe_unused]] const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
if (clientNum == -1)
{
SV_SendServerCommand(nullptr, type, "%s", text);
return;
}
assert(sv_maxclients->current.integer >= 1 && sv_maxclients->current.integer <= 18);
assert(static_cast<unsigned>(clientNum) < sv_maxclients->current.unsignedInt);
SV_SendServerCommand(&mp::svs_clients[clientNum], type, "%s", text);
}
void SV_ReconnectClients(int savepersist)
{
const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
for (int i = 0; i < sv_maxclients->current.integer; ++i)
{
mp::client_t* client = &mp::svs_clients[i];
if (client->header.state < CS_CONNECTED)
{
continue;
}
SV_AddServerCommand(client, SV_CMD_RELIABLE, utils::string::va("%c", savepersist != 0 ? 107 : 118));
const char* denied = ClientConnect(i, client->scriptId);
if (denied)
{
SV_DropClient(client, denied, true);
console::info("SV_MapRestart_f: dropped client %i - denied!\n", i);
}
else if (client->header.state == CS_ACTIVE)
{
SV_ClientEnterWorld(client, &client->lastUsercmd);
}
}
}
}

View File

@ -1,8 +0,0 @@
#pragma once
namespace game::engine
{
void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...);
void SV_GameSendServerCommand(int clientNum, svscmd_type type, const char* text);
void SV_ReconnectClients(int savepersist);
}

View File

@ -25,13 +25,6 @@ namespace game
return sv_cmd_args->argv[sv_cmd_args->nesting][index];
}
HANDLE Sys_OpenFileReliable(const char* filename)
{
return ::CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr);
}
namespace environment
{
launcher::mode mode = launcher::mode::none;

View File

@ -63,8 +63,6 @@ namespace game
[[nodiscard]] int SV_Cmd_Argc();
[[nodiscard]] const char* SV_Cmd_Argv(int index);
[[nodiscard]] HANDLE Sys_OpenFileReliable(const char* filename);
[[nodiscard]] bool is_headless();
void show_error(const std::string& text, const std::string& title = "Error");

File diff suppressed because it is too large Load Diff

View File

@ -8,19 +8,17 @@ namespace game
* Functions
**************************************************************/
WEAK symbol<void(unsigned int id)> AddRefToObject{0x1403D7A10, 0x1404326D0};
WEAK symbol<void(unsigned int id)> AddRefToObject{0, 0x1404326D0};
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x1403D7740, 0x1404326E0};
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0, 0x1404329B0};
WEAK symbol<ObjectVariableValue*(unsigned int* index)> AllocVariable{0x1403D7A70, 0x140432A10};
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x1403D90F0, 0x1404340C0};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x1403D8FE0, 0x140433FB0};
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0, 0x140433FB0};
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x1403D91C0, 0x140434190};
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x140139D80};
WEAK symbol<void(unsigned int weapon, bool isAlternate, char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0, 0x140239370};
WEAK symbol<void()> BG_ClearWeaponDef{0x0, 0x140238D20};
WEAK symbol<bool()> BG_BotsConnectType{0x0, 0x140217080};
WEAK symbol<void()> Com_Frame_Try_Block_Function{0x1403BC980, 0x1404131A0};
WEAK symbol<const char*(char const**)> Com_Parse{0x1404313E0, 0x1404F50E0};
@ -30,8 +28,6 @@ namespace game
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1404158C0};
WEAK symbol<void(const char* text_in)> Com_TokenizeString{0x1403B4150, 0x1403F7CC0};
WEAK symbol<void()> Com_EndTokenizeString{0x1403B37C0, 0x1403F7330};
WEAK symbol<void()> Com_StreamSync_UpdateLaunchData{0x0, 0x140411B50};
WEAK symbol<int(char* dest, int size, const char* fmt, ...)> Com_sprintf{0x140432310, 0x1404F6260};
WEAK symbol<void(const char* message)> Conbuf_AppendText{0x14043DDE0, 0x1405028C0};
@ -40,15 +36,31 @@ namespace game
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x1403B3050, 0x1403F6B50};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer, void (int, int, const char*))> Cbuf_ExecuteBufferInternal{0x1403B3160, 0x1403F6C60};
WEAK symbol<bool()> CL_IsCgameInitialized{0x140234DA0, 0x1402B9A70};
WEAK symbol<void(int localClientNum, int latestSequence)> CG_ExecuteNewServerCommands{0, 0x140288500};
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x1401F2E20, 0x140271320};
WEAK symbol<void(int localClientNum, mp::cg_s* cg, const char* dvar, const char* value)> CG_SetClientDvarFromServer{0x0, 0x14028A2C0};
WEAK symbol<void*(const mp::playerState_t* ps)> CG_GetVehicleDef{0, 0x140229760};
WEAK symbol<void(int localClientNum, const mp::centity_t* cent, unsigned int event, unsigned int eventParm, bool isPlayerView)> CG_HandleTurretFire{0, 0x140269DF0};
WEAK symbol<void(int localClientNum, mp::cg_t* cg, const char* dvar, const char* value)> CG_SetClientDvarFromServer{0, 0x14028A2C0};
WEAK symbol<void(int localClientNum, const mp::snapshot_t* oldSnap, const mp::snapshot_t* newSnap)> CG_UpdateOmnvars{0, 0x14028E770};
WEAK symbol<void(const char* cmdName, void (), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x1403B3570, 0x1403F7070};
WEAK symbol<void(int localClientNum, int key)> CL_CharEvent{0x14023CE50, 0x1402C2AE0};
WEAK symbol<bool(int localClientNum, int clientNum, char* buffer, int length)> CL_GetClientNameColorize{0, 0x1402CFA60};
WEAK symbol<bool(const mp::clientActive_t* cl, const int serverTime,
mp::playerState_t* to)> CL_GetPredictedPlayerInformationForServerTime{0, 0x1402CC710};
WEAK symbol<bool(const mp::clientActive_t* cl, const int serverTime,
PlayerVehicleState* predictedPlayerVehState)> CL_GetPredictedVehicleForServerTime{0, 0x1402CC7F0};
WEAK symbol<bool()> CL_IsCgameInitialized{0x140234DA0, 0x1402B9A70};
WEAK symbol<void(int localClientNum, int key, int down, unsigned int time)> CL_KeyEvent{0x14023D070, 0x1402C2CE0};
WEAK symbol<void(int localClientNum, msg_t*)> CL_ParseServerMessage{0, 0x1402CDFC0};
WEAK symbol<bool(int localClientNum, int weapon, int useAltMode)> CG_SelectWeapon{0, 0x1402AB310};
WEAK symbol<void(int localClientNum)> CL_SetCGameTime{0, 0x1402B9D80};
WEAK symbol<void(int localClientNum)> CL_WritePacket{0, 0x1402C1E70};
WEAK symbol<void(const char* cmdName, void (), cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{0x1403B3570, 0x1403F7070};
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x1403B3B10, 0x1403F7680};
WEAK symbol<int(int msec)> Com_TimeScaleMsec{0, 0x140415D50};
WEAK symbol<void (XAssetType type, void (__cdecl *func)(XAssetHeader, void*), void* inData, bool includeOverride)> DB_EnumXAssets_FastFile{0x140271F50, 0x14031EF90};
WEAK symbol<void(XAssetType type, void (__cdecl *func)(XAssetHeader, void*), void* inData, bool includeOverride)> DB_EnumXAssets_FastFile{0x140271F50, 0x14031EF90};
WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)> DB_EnumXAssets_Internal{0x140271FC0, 0x14031F000};
WEAK symbol<XAssetEntry*(XAssetType type, const char* name)> DB_FindXAssetEntry{0x140272230, 0x14031F2D0};
WEAK symbol<const char* (const XAsset* asset)> DB_GetXAssetName{0x14024FB10, 0x1402FB160};
@ -61,9 +73,8 @@ namespace game
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x140272E80, 0x14031FF80};
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x140272D50, 0x14031FE50};
WEAK symbol<int(const char* zoneName)> DB_IsLocalized{0x140273210, 0x140320360};
WEAK symbol<int(XAssetType type, void** assets, int maxCount)> DB_GetAllXAssetOfType_FastFile{0x0, 0x14031FC00};
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, PMem_Source source)> PMem_AllocFromSource_NoDebug{0x140430B80, 0x1404F46C0};
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, PMem_Source source)> PMem_AllocFromSource_NoDebug{0x140430B80, 0x001404F46C0};
WEAK symbol<void(const char* name, PMem_Direction allocDir)> PMem_Free{0x140430EC0 , 0x1404F4A30};
WEAK symbol<void*(unsigned int size)> Hunk_AllocateTempMemoryHighInternal{0x140423C70, 0x1404E4E20};
@ -90,7 +101,7 @@ namespace game
WEAK symbol<void(const dvar_t* dvar, const char* string)> Dvar_SetString{0x14042D6E0, 0x1404F08E0};
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x14042D000, 0x1404F00B0};
WEAK symbol<void()> Dvar_Sort{0x14042DEF0, 0x1404F1210};
WEAK symbol<const char*(dvar_t* dvar, DvarValue value)> Dvar_ValueToString{0x14042E710, 0x1404F1A30};
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x14042E710, 0x1404F1A30};
WEAK symbol<long long (const char* qpath, char** buffer)> FS_ReadFile{0x14041D0B0, 0x1404DE900};
WEAK symbol<void(void* buffer)> FS_FreeFile{0x14041D0A0, 0x1404DE8F0};
@ -98,33 +109,32 @@ namespace game
WEAK symbol<void(const char *path, const char *dir, int bLanguageDirectory, int iLanguage)> FS_AddGameDirectory{0x14041A120, 0x1404DC570};
WEAK symbol<void(const char *path, const char *dir)> FS_AddLocalizedGameDirectory{0x14041A2F0, 0x1404DC760};
WEAK symbol<Weapon(const char* pickupName, int model)> G_FindItem{0x140462490, 0x14021B7E0};
WEAK symbol<int(playerState_s* ps, Weapon weapon, int dualWield, int startInAltMode, int usedBefore)> G_GivePlayerWeapon{0x140359E20, 0x1403DA5E0};
WEAK symbol<Weapon(const char* name)> G_GetWeaponForName{0x140359890, 0x1403DA060};
WEAK symbol<void()> G_SetupLevelWeaponDef{0x0, 0x1403DA910};
WEAK symbol<void()> G_Glass_Update{0x14030E680, 0x140397450};
WEAK symbol<void (playerState_s* ps, Weapon weapon, int hadWeapon)> G_InitializeAmmo{0x140311F00, 0x14039AEA0};
WEAK symbol<void(int clientNum, Weapon weapon)> G_SelectWeapon{0x14035A200, 0x1403DA880};
WEAK symbol<int(playerState_s* ps, Weapon weapon)> G_TakePlayerWeapon{0x14035A350, 0x1403DA9C0};
WEAK symbol<unsigned int (const char* name, /*ConfigString*/ unsigned int start, unsigned int max, int create,
const char* errormsg)> G_FindConfigstringIndex{0x0, 0x140161F90};
WEAK symbol<int(int server_time)> G_RunFrame{0x0, 0x1403A05E0};
WEAK symbol<int(int clientNun)> G_GetClientScore{0x0, 0x14039EF60};
WEAK symbol<bool(int localClientNum)> GetRemoteEyeValues{0, 0x1402A0190};
WEAK symbol<game_hudelem_s*(int clientNum, int teamNum)> HudElem_Alloc{0x0, 0x1403997E0};
WEAK symbol<Weapon(const char* pickupName, int model)> G_FindItem{0x140462490, 0x14021B7E0};
WEAK symbol<int(playerState_t* ps, Weapon weapon, int dualWield, int startInAltMode, int usedBefore)> G_GivePlayerWeapon{0x140359E20, 0x1403DA5E0};
WEAK symbol<Weapon(const char* name)> G_GetWeaponForName{0x140359890, 0x1403DA060};
WEAK symbol<void()> G_Glass_Update{0x14030E680, 0x140397450};
WEAK symbol<void (playerState_t* ps, Weapon weapon, int hadWeapon)> G_InitializeAmmo{0x140311F00, 0x14039AEA0};
WEAK symbol<void(int clientNum, Weapon weapon)> G_SelectWeapon{0x14035A200, 0x1403DA880};
WEAK symbol<int(playerState_t* ps, Weapon weapon)> G_TakePlayerWeapon{0x14035A350, 0x1403DA9C0};
WEAK symbol<unsigned int (const char* name, /*ConfigString*/ unsigned int start, unsigned int max, int create,
const char* errormsg)> G_FindConfigstringIndex{0, 0x140161F90};
WEAK symbol<int(int server_time)> G_RunFrame{0, 0x1403A05E0};
WEAK symbol<game_hudelem_t*(int clientNum, int teamNum)> HudElem_Alloc{0, 0x1403997E0};
WEAK symbol<char*(char* string)> I_CleanStr{0x140432460, 0x1404F63C0};
WEAK symbol<void(char* dest, const char* src, int destsize)> I_strncpyz{0x140432810, 0x1404F67A0};
WEAK symbol<void(char* dest, int size, const char* src)> I_strncat{0x140432740, 0x1404F66D0};
WEAK symbol<char*(GfxImage* image, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipCount,
uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const void* initData)> Image_Setup{0x140517910, 0x1405E4380};
uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const void* initData)>
Image_Setup{0x140517910, 0x1405E4380};
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x14023D9A0, 0x1402C40E0};
WEAK symbol<unsigned int (int)> Live_SyncOnlineDataFlags{0, 0x1405ABF70};
WEAK symbol<void(int localControllerIndex)> LiveStorage_StatsWriteNotNeeded{0x1403BA420, 0x140409120};
WEAK symbol<const char*()> LiveStorage_FetchFFotD{0x140415FD0, 0x1404D7C00};
WEAK symbol<bool(int controllerIndex, unsigned int name, int value, StatsGroup statsGroup)> LiveStorage_PlayerDataSetIntByName{0x1403B8C20, 0x140404730};
WEAK symbol<bool(std::uint8_t* persistentData, const char* lookupString, int value, std::uint8_t* modifiedFlags, StatsGroup statsGroup)> LiveStorage_PlayerDataSetReservedInt{0x1403B8D00, 0x140404820};
WEAK symbol<std::int64_t(std::uint8_t* persistentData, const char* lookupString, const StatsGroup statsGroup)> LiveStorage_PlayerDataGetReservedInt{0x1403B84F0, 0x140403CF0};
@ -141,31 +151,36 @@ namespace game
WEAK symbol<StructuredDataDef*(const char* filename, unsigned int maxSize)>StructuredDataDef_GetAsset{0, 0x1404E6560};
WEAK symbol<StringTable*(const char* fileName, const StringTable** tablePtr)>StringTable_GetAsset{0, 0x1404E6170};
WEAK symbol<const char*(const StringTable* table, int row, int column)> StringTable_GetColumnValueForRow{0, 0x1404E61A0};
WEAK symbol<int(const StringTable* table, int comparisonColumn, const char* value)> StringTable_LookupRowNumForValue{0, 0x1404E6260};
WEAK symbol<int(const char* string)> StringTable_HashString{0x1404259A0, 0x1404E6320};
WEAK symbol<const char*(const StringTable* table, const int row, const int column)> StringTable_GetColumnValueForRow{0, 0x1404E61A0};
WEAK symbol<int(const StringTable* table, const int comparisonColumn, const char* value)> StringTable_LookupRowNumForValue{0, 0x1404E6260};
WEAK symbol<bool(int localClientNum, void* lua_vm)> LUI_IntermissionBegan{0, 0x1401CEB40};
WEAK symbol<void(int localClientNum, const char* menuName, int isPopup, int isModal, unsigned int isExclusive)> LUI_OpenMenu{0x1403FD460, 0x1404B3610};
// Made up name, replaced by ScopedCriticalSection on Black Ops 3
WEAK symbol<void()> LUI_EnterCriticalSection{0x1401AE940, 0x1401CD040};
WEAK symbol<void()> LUI_LeaveCriticalSection{0x1401B0AA0, 0x1401CF1A0};
WEAK symbol<bool(int localClientNum, const char* menuName)> Menu_IsMenuOpenAndVisible{0x0, 0x1404B38A0};
WEAK symbol<bool(int localClientNum, const char* menuName)> Menu_IsMenuOpenAndVisible{0, 0x1404B38A0};
WEAK symbol<Material*(const char* material)> Material_RegisterHandle{0x140523D90, 0x1405F0E20};
WEAK symbol<void(netsrc_t, netadr_s*, const char*)> NET_OutOfBandPrint{0, 0x14041D5C0};
WEAK symbol<void(netsrc_t sock, int length, const void* data, const netadr_s* to)> NET_SendLoopPacket{0, 0x14041D780};
WEAK symbol<bool(const char* s, netadr_s* a)> NET_StringToAdr{0, 0x14041D870};
WEAK symbol<void(netadr_s*, sockaddr*)> NetadrToSockadr{0, 0x1404E53D0};
WEAK symbol<int(msg_t*)> MSG_ReadLong{0, 0x1404181C0};
WEAK symbol<void(msg_t*, int, unsigned int)> MSG_WriteBits{0, 0x140418740};
WEAK symbol<void(netsrc_t, netadr_t*, const char*)> NET_OutOfBandPrint{0, 0x14041D5C0};
WEAK symbol<void(netsrc_t sock, int length, const void* data, const netadr_t* to)> NET_SendLoopPacket{0, 0x14041D780};
WEAK symbol<bool(const char* s, netadr_t* a)> NET_StringToAdr{0, 0x14041D870};
WEAK symbol<void(netadr_t*, sockaddr*)> NetadrToSockadr{0, 0x1404E53D0};
WEAK symbol<void*(int)> Omnvar_GetDef{0, 0x1404F3E80};
WEAK symbol<void(float x, float y, float width, float height, float s0, float t0, float s1, float t1,
float* color, Material* material)> R_AddCmdDrawStretchPic{0x140234460, 0x140600BE0};
WEAK symbol<void(const char*, int, Font_s*, float, float, float, float, float, float*, int)> R_AddCmdDrawText{0x140533E40, 0x140601070};
WEAK symbol<void(const char*, int, Font_s*, float, float, float, float, float, const float*, int, int, char)> R_AddCmdDrawTextWithCursor{0x140534170, 0x1406013A0};
WEAK symbol<Font_s*(const char* font)> R_RegisterFont{0x1405130B0, 0x1405DFAC0};
WEAK symbol<void(const char*, int, const Font_t*, float, float, float, float, float, const float*, int)> R_AddCmdDrawText{0x140533E40, 0x140601070};
WEAK symbol<void(const char*, int, const Font_t*, float, float, float, float, float, const float*, int, int, char)> R_AddCmdDrawTextWithCursor{0x140534170, 0x1406013A0};
WEAK symbol<Font_t*(const char* font)> R_RegisterFont{0x1405130B0, 0x1405DFAC0};
WEAK symbol<void()> R_SyncRenderThread{0x140535AF0, 0x140602D30};
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x140513390, 0x1405DFDB0};
WEAK symbol<int(const char* text, int maxChars, Font_t* font)> R_TextWidth{0x140513390, 0x1405DFDB0};
WEAK symbol<ScreenPlacement*()> ScrPlace_GetViewPlacement{0x14024D150, 0x1402F6D40};
@ -173,35 +188,30 @@ namespace game
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1403D84F0, 0x1404334A0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0, 0x1404333A0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int id)> GetVariableName{0x1403D8E90, 0x140433E60};
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x1403DC810, 0x140437860};
WEAK symbol<void (VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x1403DC810, 0x140437860};
WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x1403D9AF0, 0x140434A10};
WEAK symbol<const char*(unsigned int index)> Scr_GetString{0x1403DE200, 0x140439160};
WEAK symbol<unsigned int(unsigned int index)> Scr_GetConstString{0x1403DDAC0, 0x140438AD0};
WEAK symbol<unsigned int(unsigned int index)> Scr_GetConstIString{0x1403DD8A0, 0x1404388B0};
WEAK symbol<void(int value)> Scr_AddInt{0x0, 0x140437E70};
WEAK symbol<void(const char* value)> Scr_AddString{0x0, 0x1404381D0};
WEAK symbol<int(unsigned int index)> Scr_GetInt{0x1403DDEB0, 0x140438E10};
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0x1403DDD50, 0x140438D60};
WEAK symbol<void(unsigned int index, float* vectorValue)> Scr_GetVector{0x1403DE470, 0x1404393D0};
WEAK symbol<unsigned int(unsigned int index)> Scr_GetObject{0x1403DDF70, 0x140438ED0};
WEAK symbol<const char*(unsigned int index)> Scr_GetString{0, 0x140439160};
WEAK symbol<void(int value)> Scr_AddInt{0, 0x140437E70};
WEAK symbol<void(const char* value)> Scr_AddString{0, 0x1404381D0};
WEAK symbol<int(unsigned int index)> Scr_GetInt{0, 0x140438E10};
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0, 0x140438D60};
WEAK symbol<unsigned int()> Scr_GetNumParam{0x1403DDF60, 0x140438EC0};
WEAK symbol<void()> Scr_ClearOutParams{0x1403DD500, 0x140438600};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x1403DBDC0, 0x140436E10};
WEAK symbol<void(int entnum, unsigned int classnum)> Scr_AddEntityNum{0x0, 0x140437F60};
WEAK symbol<void(int entnum, unsigned int classnum)> Scr_AddEntityNum{0, 0x140437F60};
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x140350E70, 0x1403D3FE0};
WEAK symbol<void(unsigned int id, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyId{0x1403DE730, 0x140439700};
WEAK symbol<void(unsigned int stringValue, unsigned int paramcount)> Scr_NotifyLevel{0x0, 0x1404397D0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0x0, 0x140436D60};
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x1403D9BA0, 0x140434AC0};
WEAK symbol<void(unsigned int stringValue, unsigned int paramcount)> Scr_NotifyLevel{0, 0x1404397D0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0, 0x140436D60};
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0, 0x140434AC0};
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x1403DD600, 0x1404386E0};
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x1403D3C50, 0x14042EAA0};
WEAK symbol<unsigned int(const char* script, unsigned int name)> Scr_GetFunctionHandle{0x1403D3AD0 , 0x14042E920};
WEAK symbol<unsigned int(void* func, int type, unsigned int name)> Scr_RegisterFunction{0x1403D3530, 0x14042E330};
WEAK symbol<unsigned int(const char** pName, int* type)> Scr_GetFunction{0x14034A950, 0x1403CD9F0};
WEAK symbol<void()> Scr_ErrorInternal{0x1403DD580 , 0x140438660};
WEAK symbol<void()> Scr_ErrorInternal{0, 0x140438660};
WEAK symbol<int(unsigned int)> GetObjectType{0x1403D8D30, 0x140433CF0};
WEAK symbol<int(unsigned int)> GetObjectType{0, 0x140433CF0};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0, 0x14043A280};
@ -216,35 +226,32 @@ namespace game
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x1403B4560, 0x1403F80D0};
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0, 0x1403F8150};
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0, 0x1403F8110};
WEAK symbol<void()> SV_MatchEnd{0x0, 0x14047A090};
WEAK symbol<void()> SV_MatchEnd{0, 0x14047A090};
WEAK symbol<void(netadr_s* from)> SV_DirectConnect{0, 0x140471390};
WEAK symbol<void(netadr_t* from)> SV_DirectConnect{0, 0x140471390};
WEAK symbol<void(int, int, const char*)> SV_GameSendServerCommand{0x140490F40, 0x1404758C0};
WEAK symbol<bool()> SV_Loaded{0x140491820, 0x1404770C0};
WEAK symbol<void(int localClientNum, const char* map, bool mapIsPreloaded)> SV_StartMap{0, 0x140470170};
WEAK symbol<void(int localClientNum, const char* map, bool mapIsPreloaded, bool migrate)> SV_StartMapForParty{0, 0x1404702F0};
WEAK symbol<void(mp::client_t* cl, int isReconnectingClient)> SV_StreamSync_ClientConnect{0x0, 0x140488080};
WEAK symbol<const char*(int clientNum, unsigned __int16 scriptPersId)> ClientConnect{0x0, 0x140387630};
WEAK symbol<void(mp::client_t* client, usercmd_s* cmd)> SV_ClientEnterWorld{0x0, 0x1404710F0};
WEAK symbol<void(mp::client_t* drop, const char* reason, bool tellThem)> SV_DropClient{0x0, 0x140472110};
WEAK symbol<mp::gentity_s*(const char*, unsigned int, unsigned int, unsigned int)> SV_AddBot{0, 0x140470920};
WEAK symbol<mp::gentity_t*(const char*, unsigned int, unsigned int, unsigned int)> SV_AddBot{0, 0x140470920};
WEAK symbol<bool(int clientNum)> SV_BotIsBot{0, 0x140461340};
WEAK symbol<const char*()> SV_BotGetRandomName{0, 0x140460B80};
WEAK symbol<void(mp::gentity_s*)> SV_SpawnTestClient{0, 0x1404740A0};
WEAK symbol<void(mp::gentity_t*)> SV_SpawnTestClient{0, 0x1404740A0};
WEAK symbol<void(mp::client_t*, const char*, int)> SV_ExecuteClientCommand{0, 0x140472430};
WEAK symbol<void()> SV_FastRestart{0x14048B890, 0x14046F440};
WEAK symbol<playerState_s*(int num)> SV_GetPlayerstateForClientNum{0x140490F80, 0x140475A10};
WEAK symbol<playerState_t*(int num)> SV_GetPlayerstateForClientNum{0x140490F80, 0x140475A10};
WEAK symbol<const char*(int clientNum)> SV_GetGuid{0, 0x140475990};
WEAK symbol<void(int clientNum, const char* reason)> SV_KickClientNum{0, 0x14046F730};
WEAK symbol<bool(mp::client_t*, const char*, int)> SV_Netchan_Transmit{0, 0x14047CC60};
WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0, 0x140477450};
WEAK symbol<int(mp::client_t* client, const unsigned char* cmd, int cmdSize)> SV_CanReplaceServerCommand{0x0, 0x140478F00};
WEAK symbol<void(const char* error, ...)> Sys_Error{0x14043AC20, 0x1404FF510};
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x1403C2D40, 0x140423920};
WEAK symbol<int()> Sys_Milliseconds{0x14043D2A0, 0x140501CA0};
WEAK symbol<void()> Sys_ShowConsole{0x14043E650, 0x140503130};
WEAK symbol<bool(int, void const*, const netadr_s*)> Sys_SendPacket{0x14043D000, 0x140501A00};
WEAK symbol<bool(int, void const*, const netadr_t*)> Sys_SendPacket{0x14043D000, 0x140501A00};
WEAK symbol<void*(int valueIndex)> Sys_GetValue{0x1403C2C30, 0x1404237D0};
WEAK symbol<bool()> Sys_IsMainThread{0x1478FC470, 0x140423950};
WEAK symbol<HANDLE(Sys_Folder folder, const char* baseFilename)> Sys_CreateFile{0x140434B10, 0x1404F8FD0};
@ -255,7 +262,9 @@ namespace game
WEAK symbol<const char*(const char*)> UI_LocalizeMapname{0, 0x1404B96D0};
WEAK symbol<const char*(const char*)> UI_LocalizeGametype{0, 0x1404B90F0};
WEAK symbol<void(int localClientNum, const char* srcString, char* dstString, int dstBufferSize)> UI_ReplaceDirective{0x0, 0x1404D8A00};
WEAK symbol<void(int localClientNum, mp::cg_t* cg, vec3_t* outViewOrigin,
vec3_t* outViewAngles)> VehicleCam_UpdatePlayerControlCam{0, 0x140568CA0};
WEAK symbol<DWOnlineStatus(int)> dwGetLogOnStatus{0, 0x140589490};
@ -264,13 +273,7 @@ namespace game
WEAK symbol<void(const pmove_t* move, trace_t* trace, const float*,
const float*, const Bounds*, int, int)> PM_trace{0, 0x140225DB0};
WEAK symbol<void(void* ps)> Jump_ClearState{0x0, 0x140213120};
WEAK symbol<void(const char* pszCommand, char* pszBuffer, int iBufferSize)> MSG_WriteReliableCommandToBuffer{0x0, 0x1404232B0};
WEAK symbol<void(unsigned __int64 markPos)> LargeLocalResetToMark{0x140423B50, 0x1404E4D00};
WEAK symbol<int(const char* s0, const char* s1, int n)> I_strnicmp{0x140432840, 0x1404F67D0};
WEAK symbol<void(void* ps)> Jump_ClearState{0, 0x140213120};
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14062E030, 0x140738060};
WEAK symbol<int (jmp_buf* Buf)> _setjmp{0x14062F030, 0x140739060};
@ -283,7 +286,7 @@ namespace game
WEAK symbol<CmdArgs> cmd_args{0x144CE7F70, 0x144518480};
WEAK symbol<CmdArgs> sv_cmd_args{0x144CE8020, 0x144518530};
WEAK symbol<cmd_function_s*> cmd_functions{0x144CE80C8, 0x1445185D8};
WEAK symbol<cmd_function_t*> cmd_functions{0x144CE80C8, 0x1445185D8};
WEAK symbol<int> dvarCount{0x1458CBA3C, 0x1478EADF4};
WEAK symbol<dvar_t*> sortedDvars{0x1458CBA60, 0x1478EAE10};
@ -320,38 +323,37 @@ namespace game
WEAK symbol<function_stack_t> scr_function_stack{0x1455BE708, 0x144D57808};
WEAK symbol<unsigned int> scr_levelEntityId{0x1452A9F30, 0x144A43020};
WEAK symbol<int> level_time{0x0, 0x1443F4B6C};
WEAK symbol<int> level_finished{0x0, 0x1443F6FAC};
WEAK symbol<int> level_savepersist{0x0, 0x1443F5710};
WEAK symbol<int> level_time{0, 0x1443F4B6C};
WEAK symbol<int> level_finished{0, 0x1443F6FAC};
WEAK symbol<int> level_savepersist{0, 0x1443F5710};
WEAK symbol<DWORD> threadIds{0x144DE6640, 0x1446B4960};
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0x145F525A8, 0x1480350D8};
WEAK symbol<GfxDrawMethod_t> gfxDrawMethod{0x145F525A8, 0x1480350D8};
namespace sp
{
WEAK symbol<gentity_s> g_entities{0x143C91600, 0};
WEAK symbol<gentity_t> g_entities{0x143C91600, 0};
WEAK symbol<XZone> g_zones_0{0x1434892D8, 0};
}
namespace mp
{
WEAK symbol<cg_s> cgArray{0, 0x14176EC00};
WEAK symbol<cg_t> cg{0, 0x14176EC00};
WEAK symbol<cgs_t> cgs{0, 0x14187EB80};
WEAK symbol<clientActive_t> cl{0, 0x1419E1C70};
WEAK symbol<clientConnection_t> clc{0, 0x141CB5350};
WEAK symbol<clientStatic_t> cls{0, 0x141D1AB60};
WEAK symbol<serverStatic_t> svs{0, 0x144DF9580};
WEAK symbol<gentity_s> g_entities{0, 0x14427A0E0};
WEAK symbol<int> svs_clientCount{0, 0x14647B28C};
WEAK symbol<client_t> svs_clients{0, 0x14647B290};
WEAK symbol<std::uint32_t> sv_serverId_value{0, 0x144DF9478};
WEAK symbol<int> gameTime{0, 0x1443F4B6C};
WEAK symbol<int> serverTime{0, 0x14647B280};
WEAK symbol<gentity_t> g_entities{0, 0x14427A0E0};
WEAK symbol<centity_t> centities{0, 0x141887350};
WEAK symbol<XZone> g_zones_0{0, 0x143A46498};
WEAK symbol<int> s_launchDataAvailable{0x0, 0x1445CE354};
WEAK symbol<connstate_t> connstate{0, 0x1419E1AE0};
WEAK symbol<int> gameTime{0, 0x1443F4B6C};
WEAK symbol<std::uint32_t> sv_serverId_value{0, 0x144DF9478};
}
namespace hks

View File

@ -4,6 +4,7 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "utils/command_line.hpp"
#include <utils/flags.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
@ -64,7 +65,9 @@ LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info)
&exception_information, nullptr, nullptr))
{
char buf[4096]{};
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n", exception_info->ExceptionRecord->ExceptionCode, exception_info->ExceptionRecord->ExceptionAddress);
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n",
exception_info->ExceptionRecord->ExceptionCode,
exception_info->ExceptionRecord->ExceptionAddress);
game::show_error(buf);
}
@ -165,8 +168,23 @@ FARPROC load_binary(const launcher::mode mode)
std::string data;
if (!utils::io::read_file(binary, &data))
{
throw std::runtime_error(
"Failed to read game binary! Please select the correct path in the launcher settings.");
// Check the first argument to see if the current directory needs to changed
// Required when the game is used to open a file (like a demo file)
const auto& args = utils::command_line::get_args();
if (!args.empty())
{
const auto& binary_dir = args.front();
if (binary_dir.filename().string().ends_with("iw6-mod.exe"))
{
std::filesystem::current_path(binary_dir.parent_path());
}
}
if (!utils::io::read_file(binary, &data))
{
throw std::runtime_error(
"Failed to read game binary! Please select the correct path in the launcher settings.");
}
}
#ifdef INJECT_HOST_AS_LIB

View File

@ -5,9 +5,7 @@
#define BINARY_PAYLOAD_SIZE 0x0B500000
// Decide whether to load the game as lib or to inject it
#if 1
#define INJECT_HOST_AS_LIB
#endif
#pragma warning(push)
#pragma warning(disable: 4100)
@ -48,15 +46,18 @@
#undef min
#endif
#include <algorithm>
#include <array>
#include <atomic>
#include <cassert>
#include <chrono>
#include <filesystem>
#include <format>
#include <fstream>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <numeric>
#include <optional>
#include <queue>
#include <random>

View File

@ -0,0 +1,35 @@
#include "command_line.hpp"
#include "nt.hpp"
#include <shellapi.h>
#include <winrt/Windows.Foundation.h>
namespace utils::command_line
{
template <typename type, typename deleter>
constexpr std::unique_ptr<type, deleter> make_unique_ptr(type* ptr, deleter&& del)
{
return std::unique_ptr<type, deleter&&>(ptr, std::forward<deleter>(del));
}
const std::vector<std::filesystem::path>& get_args()
{
static const auto args = []()
{
auto argc = 0;
const auto cmd_line = winrt::check_pointer(::GetCommandLineW());
const auto up = make_unique_ptr(winrt::check_pointer(::CommandLineToArgvW(cmd_line, &argc)), ::LocalFree);
const auto argv = up.get();
std::vector<std::filesystem::path> vec(argc);
std::transform(argv, argv + argc, vec.begin(), [](const auto* path)
{
return std::filesystem::path(path);
});
return vec;
}();
return args;
}
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <filesystem>
#include <vector>
namespace utils::command_line
{
[[nodiscard]] const std::vector<std::filesystem::path>& get_args();
}

View File

@ -109,6 +109,86 @@ namespace utils::compression
result.resize(length);
return result;
}
std::vector<std::uint8_t> decompress(std::span<const std::uint8_t> input, std::size_t exp_output_size, double growth_rate)
{
auto decompress_internal = [](std::uint8_t* output, uLong* output_size, const std::uint8_t* input, uLong* input_size)
{
return uncompress2(
reinterpret_cast<Bytef*>(output), output_size,
reinterpret_cast<const Bytef*>(input), input_size
);
};
auto output_size = static_cast<uLong>(std::max(exp_output_size, std::size_t(256)));
std::vector<std::uint8_t> output(output_size);
auto input_size = static_cast<uLong>(input.size());
auto status = decompress_internal(output.data(), &output_size, input.data(), &input_size);
if (status == Z_BUF_ERROR)
{
const auto decompression_ratio = (input_size / static_cast<double>(output.size())) - 0.04;
output_size = static_cast<uLong>(std::max(input.size() / decompression_ratio, static_cast<double>(output_size)));
output.resize(output_size);
input_size = static_cast<uLong>(input.size());
status = decompress_internal(output.data(), &output_size, input.data(), &input_size);
}
for (growth_rate = std::max(growth_rate, 1.0); status == Z_BUF_ERROR;)
{
output_size = 4 + static_cast<uLong>(growth_rate * output.size());
output.resize(output_size);
input_size = static_cast<uLong>(input.size());
status = decompress_internal(output.data(), &output_size, input.data(), &input_size);
}
if (status != Z_OK)
{
output.clear();
return output;
}
output.resize(output_size);
return output;
}
std::vector<std::uint8_t> compress(std::span<const std::uint8_t> input, std::size_t exp_output_size, double growth_rate)
{
auto compress_internal = [](std::uint8_t* output, uLong* output_size, const std::uint8_t* input, uLong input_size)
{
return compress2(
reinterpret_cast<Bytef*>(output), output_size,
reinterpret_cast<const Bytef*>(input), input_size,
Z_BEST_COMPRESSION
);
};
auto output_size = static_cast<uLong>(std::max(exp_output_size, std::size_t(256)));
std::vector<std::uint8_t> output(output_size);
const auto input_size = static_cast<uLong>(input.size());
auto status = compress_internal(output.data(), &output_size, input.data(), input_size);
for (growth_rate = std::max(growth_rate, 1.0); status == Z_BUF_ERROR;)
{
output_size = 4 + static_cast<uLong>(growth_rate * output.size());
output.resize(output_size);
status = compress_internal(output.data(), &output_size, input.data(), input_size);
}
if (status != Z_OK)
{
output.clear();
return output;
}
output.resize(output_size);
return output;
}
}
namespace zip

View File

@ -1,7 +1,9 @@
#pragma once
#include <span>
#include <string>
#include <unordered_map>
#include <vector>
#define CHUNK 16384u
@ -11,6 +13,9 @@ namespace utils::compression
{
std::string compress(const std::string& data);
std::string decompress(const std::string& data);
std::vector<std::uint8_t> compress(std::span<const std::uint8_t> input, std::size_t exp_output_size, double growth_rate = 2.0);
std::vector<std::uint8_t> decompress(std::span<const std::uint8_t> input, std::size_t exp_output_size, double growth_rate = 2.0);
}
namespace zip

View File

@ -2,6 +2,7 @@
#include "string.hpp"
#include <MinHook.h>
#include <cassert>
namespace utils::hook
{
@ -279,6 +280,7 @@ namespace utils::hook
void* result = nullptr;
runtime.add(&result, &code);
assert(result);
return result;
}

View File

@ -1,25 +0,0 @@
#include "properties.hpp"
#include <gsl/gsl>
#include <ShlObj.h>
namespace utils::properties
{
std::filesystem::path get_appdata_path()
{
PWSTR path;
if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path)))
{
throw std::runtime_error("Failed to read APPDATA path!");
}
auto _ = gsl::finally([&path]
{
CoTaskMemFree(path);
});
static auto appdata = std::filesystem::path(path) / "alterware";
return appdata;
}
}

View File

@ -1,7 +0,0 @@
#pragma once
#include <filesystem>
namespace utils::properties
{
std::filesystem::path get_appdata_path();
}

View File

@ -140,6 +140,32 @@ namespace utils::string
*out = '\0';
}
std::string strip(std::string_view sv, bool strip_default_color)
{
std::string in(sv);
for (std::size_t i = 0; i + 1 < in.size();)
{
if (in[i] == '^' && static_cast<std::size_t>(in[i + 1] - '0') < 0xC)
{
if (in[i + 1] != '7' || strip_default_color)
{
in.erase(in.begin() + i, in.begin() + i + 2);
}
else
{
i += 2;
}
}
else
{
i += 1;
}
}
return in;
}
std::string convert(const std::wstring& wstr)
{
std::string result;

View File

@ -1,6 +1,7 @@
#pragma once
#include "memory.hpp"
#include <cstdint>
#include <span>
template <class Type, size_t n>
constexpr auto ARRAY_COUNT(Type(&)[n]) { return n; }
@ -29,7 +30,7 @@ namespace utils::string
while (true)
{
const int res = vsnprintf(entry->buffer, entry->size, format, ap);
const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
if (res > 0) break; // Success
if (res == 0) return nullptr; // Error
@ -91,6 +92,7 @@ namespace utils::string
std::string get_clipboard_data();
void strip(const char* in, char* out, size_t max);
std::string strip(std::string_view sv, bool strip_default_color = false);
std::string convert(const std::wstring& wstr);
std::wstring convert(const std::string& str);