allow loading of custom zones (basic mod support) (#172)
* allow loading of custom zones * remove game:: * remove unnecessary hook * mod support - fs_game support - precache all weapon files loaded in database - parse startup variables correctly Co-authored-by: quaK <38787176+Joelrau@users.noreply.github.com>
This commit is contained in:
parent
fd7d136bf3
commit
690310fd76
@ -11,6 +11,7 @@
|
||||
#include "fastfiles.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "logfile.hpp"
|
||||
#include "dvars.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
@ -22,7 +23,6 @@ namespace command
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour client_command_hook;
|
||||
utils::hook::detour parse_commandline_hook;
|
||||
|
||||
std::unordered_map<std::string, std::function<void(params&)>> handlers;
|
||||
std::unordered_map<std::string, std::function<void(int, params_sv&)>> handlers_sv;
|
||||
@ -105,10 +105,44 @@ namespace command
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
void parse_commandline_stub()
|
||||
void parse_startup_variables()
|
||||
{
|
||||
auto& com_num_console_lines = *reinterpret_cast<int*>(0x35634B8_b);
|
||||
auto* com_console_lines = reinterpret_cast<char**>(0x35634C0_b);
|
||||
|
||||
for (int i = 0; i < com_num_console_lines; i++)
|
||||
{
|
||||
game::Cmd_TokenizeString(com_console_lines[i]);
|
||||
|
||||
// only +set dvar value
|
||||
if (game::Cmd_Argc() >= 3 && game::Cmd_Argv(0) == "set"s)
|
||||
{
|
||||
const std::string& dvar_name = game::Cmd_Argv(1);
|
||||
const std::string& value = game::Cmd_Argv(2);
|
||||
|
||||
const auto* dvar = game::Dvar_FindVar(dvar_name.data());
|
||||
if (dvar)
|
||||
{
|
||||
game::Dvar_SetCommand(dvar->hash, "", value.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
dvars::on_register(dvar_name, [dvar_name, value]()
|
||||
{
|
||||
game::Dvar_SetCommand(game::generateHashValue(dvar_name.data()), "", value.data());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
game::Cmd_EndTokenizeString();
|
||||
}
|
||||
}
|
||||
|
||||
void parse_commandline_stub(char* commandline)
|
||||
{
|
||||
//utils::hook::invoke<void>(0x17CB60_b, commandline); // Com_ParseCommandLine
|
||||
parse_command_line();
|
||||
parse_commandline_hook.invoke<void>();
|
||||
parse_startup_variables();
|
||||
}
|
||||
|
||||
game::dvar_t* dvar_command_stub()
|
||||
@ -531,7 +565,7 @@ namespace command
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_commandline_hook.create(0x157D50_b, parse_commandline_stub);
|
||||
utils::hook::call(0x15C44B_b, parse_commandline_stub);
|
||||
add_commands_mp();
|
||||
}
|
||||
|
||||
|
@ -263,6 +263,8 @@ namespace dvars
|
||||
utils::hook::detour dvar_register_vector3_hook;
|
||||
utils::hook::detour dvar_register_enum_hook;
|
||||
|
||||
utils::hook::detour dvar_register_new_hook;
|
||||
|
||||
utils::hook::detour dvar_set_bool_hook;
|
||||
utils::hook::detour dvar_set_float_hook;
|
||||
utils::hook::detour dvar_set_int_hook;
|
||||
@ -407,6 +409,26 @@ namespace dvars
|
||||
return dvar_register_enum_hook.invoke<game::dvar_t*>(hash, name, value_list, default_index, flags);
|
||||
}
|
||||
|
||||
std::unordered_map<int, std::function<void()>> dvar_on_register_function_map;
|
||||
void on_register(const std::string& name, const std::function<void()>& callback)
|
||||
{
|
||||
dvar_on_register_function_map[game::generateHashValue(name.data())] = callback;
|
||||
}
|
||||
|
||||
game::dvar_t* dvar_register_new(const int hash, const char* name, game::dvar_type type, unsigned int flags,
|
||||
game::dvar_value* value, game::dvar_limits* domain, const char* description)
|
||||
{
|
||||
auto* dvar = dvar_register_new_hook.invoke<game::dvar_t*>(hash, name, type, flags, value, domain, description);
|
||||
|
||||
if (dvar && dvar_on_register_function_map.find(hash) != dvar_on_register_function_map.end())
|
||||
{
|
||||
dvar_on_register_function_map[hash]();
|
||||
dvar_on_register_function_map.erase(hash);
|
||||
}
|
||||
|
||||
return dvar;
|
||||
}
|
||||
|
||||
void dvar_set_bool(game::dvar_t* dvar, bool boolean)
|
||||
{
|
||||
const auto disabled = find_dvar(disable::set_bool_disables, dvar->hash);
|
||||
@ -505,6 +527,8 @@ namespace dvars
|
||||
dvar_register_vector3_hook.create(SELECT_VALUE(0x419A00_b, 0x182DB0_b), &dvar_register_vector3);
|
||||
dvar_register_enum_hook.create(SELECT_VALUE(0x419500_b, 0x182700_b), &dvar_register_enum);
|
||||
|
||||
dvar_register_new_hook.create(SELECT_VALUE(0x41B1D0_b, 0x184DF0_b), &dvar_register_new);
|
||||
|
||||
if (!game::environment::is_sp())
|
||||
{
|
||||
dvar_register_bool_hashed_hook.create(SELECT_VALUE(0x0, 0x182420_b), &dvar_register_bool_hashed);
|
||||
|
@ -26,4 +26,6 @@ namespace dvars
|
||||
void set_string(const std::string& name, const std::string& string);
|
||||
void set_from_string(const std::string& name, const std::string& value);
|
||||
}
|
||||
|
||||
void on_register(const std::string& name, const std::function<void()>& callback);
|
||||
}
|
||||
|
@ -82,6 +82,131 @@ namespace fastfiles
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
utils::hook::detour db_read_stream_file_hook;
|
||||
void db_read_stream_file_stub(int a1, int a2)
|
||||
{
|
||||
// always use lz4 compressor type when reading stream files
|
||||
*game::g_compressor = 4;
|
||||
return db_read_stream_file_hook.invoke<void>(a1, a2);
|
||||
}
|
||||
|
||||
void skip_extra_zones_stub(utils::hook::assembler& a)
|
||||
{
|
||||
const auto skip = a.newLabel();
|
||||
const auto original = a.newLabel();
|
||||
|
||||
a.pushad64();
|
||||
a.test(esi, game::DB_ZONE_CUSTOM); // allocFlags
|
||||
a.jnz(skip);
|
||||
|
||||
a.bind(original);
|
||||
a.popad64();
|
||||
a.mov(rdx, 0x8E2F80_b);
|
||||
a.mov(rcx, rbp);
|
||||
a.call(0x840A20_b);
|
||||
a.jmp(0x398070_b);
|
||||
|
||||
a.bind(skip);
|
||||
a.popad64();
|
||||
a.mov(r14d, game::DB_ZONE_CUSTOM);
|
||||
a.not_(r14d);
|
||||
a.and_(esi, r14d);
|
||||
a.jmp(0x39814F_b);
|
||||
}
|
||||
|
||||
utils::hook::detour sys_createfile_hook;
|
||||
HANDLE sys_create_file_stub(game::Sys_Folder folder, const char* base_filename)
|
||||
{
|
||||
auto* fs_basepath = game::Dvar_FindVar("fs_basepath");
|
||||
auto* fs_game = game::Dvar_FindVar("fs_game");
|
||||
|
||||
std::string dir = fs_basepath ? fs_basepath->current.string : "";
|
||||
std::string mod_dir = fs_game ? fs_game->current.string : "";
|
||||
|
||||
if (base_filename == "mod.ff"s)
|
||||
{
|
||||
if (!mod_dir.empty())
|
||||
{
|
||||
auto path = utils::string::va("%s\\%s\\%s", dir.data(), mod_dir.data(), base_filename);
|
||||
if (utils::io::file_exists(path))
|
||||
{
|
||||
return CreateFileA(path, 0x80000000, 1u, 0, 3u, 0x60000000u, 0);
|
||||
}
|
||||
}
|
||||
return (HANDLE)-1;
|
||||
}
|
||||
|
||||
return sys_createfile_hook.invoke<HANDLE>(folder, base_filename);
|
||||
}
|
||||
|
||||
template <typename T> inline void merge(std::vector<T>* target, T* source, size_t length)
|
||||
{
|
||||
if (source)
|
||||
{
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
target->push_back(source[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> inline void merge(std::vector<T>* target, std::vector<T> source)
|
||||
{
|
||||
for (auto& entry : source)
|
||||
{
|
||||
target->push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void load_pre_gfx_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode)
|
||||
{
|
||||
std::vector<game::XZoneInfo> data;
|
||||
merge(&data, zoneInfo, zoneCount);
|
||||
|
||||
// code_pre_gfx_mp
|
||||
|
||||
game::DB_LoadXAssets(data.data(), static_cast<std::uint32_t>(data.size()), syncMode);
|
||||
}
|
||||
|
||||
void load_post_gfx_and_ui_and_common_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode)
|
||||
{
|
||||
std::vector<game::XZoneInfo> data;
|
||||
merge(&data, zoneInfo, zoneCount);
|
||||
|
||||
// code_post_gfx_mp
|
||||
// ui_mp
|
||||
// common_mp
|
||||
|
||||
if (fastfiles::exists("mod"))
|
||||
{
|
||||
data.push_back({ "mod", game::DB_ZONE_COMMON | game::DB_ZONE_CUSTOM, 0 });
|
||||
}
|
||||
|
||||
game::DB_LoadXAssets(data.data(), static_cast<std::uint32_t>(data.size()), syncMode);
|
||||
}
|
||||
|
||||
void load_ui_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode)
|
||||
{
|
||||
std::vector<game::XZoneInfo> data;
|
||||
merge(&data, zoneInfo, zoneCount);
|
||||
|
||||
// ui_mp
|
||||
|
||||
game::DB_LoadXAssets(data.data(), static_cast<std::uint32_t>(data.size()), syncMode);
|
||||
}
|
||||
}
|
||||
|
||||
bool exists(const std::string& zone)
|
||||
{
|
||||
auto is_localized = game::DB_IsLocalized(zone.data());
|
||||
auto handle = game::Sys_CreateFile((is_localized ? game::SF_ZONE_LOC : game::SF_ZONE), utils::string::va("%s.ff", zone.data()));
|
||||
if (handle != (HANDLE)-1)
|
||||
{
|
||||
CloseHandle(handle);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string get_current_fastfile()
|
||||
@ -113,6 +238,68 @@ namespace fastfiles
|
||||
db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub);
|
||||
|
||||
g_dump_scripts = dvars::register_bool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts");
|
||||
|
||||
// Allow loading of unsigned fastfiles
|
||||
if (!game::environment::is_sp())
|
||||
{
|
||||
utils::hook::nop(0x368153_b, 2); // DB_InflateInit
|
||||
}
|
||||
|
||||
// Allow loading of mixed compressor types
|
||||
utils::hook::nop(SELECT_VALUE(0x1C4BE7_b, 0x3687A7_b), 2);
|
||||
|
||||
// Fix compressor type on streamed file load
|
||||
db_read_stream_file_hook.create(SELECT_VALUE(0x1FB9D0_b, 0x3A1BF0_b), db_read_stream_file_stub);
|
||||
|
||||
// Don't load extra zones with loadzone
|
||||
if (!game::environment::is_sp())
|
||||
{
|
||||
// TODO: SP?
|
||||
utils::hook::nop(0x398061_b, 15);
|
||||
utils::hook::jump(0x398061_b, utils::hook::assemble(skip_extra_zones_stub), true);
|
||||
}
|
||||
|
||||
if (!game::environment::is_sp())
|
||||
{
|
||||
// Add custom zone paths
|
||||
sys_createfile_hook.create(game::Sys_CreateFile, sys_create_file_stub);
|
||||
|
||||
// load our custom pre_gfx zones
|
||||
utils::hook::call(0x15C3FD_b, load_pre_gfx_zones);
|
||||
utils::hook::call(0x15C75D_b, load_pre_gfx_zones);
|
||||
|
||||
// load our custom ui and common zones
|
||||
utils::hook::call(0x686421_b, load_post_gfx_and_ui_and_common_zones);
|
||||
|
||||
// load our custom ui zones
|
||||
utils::hook::call(0x17C6D2_b, load_ui_zones);
|
||||
}
|
||||
|
||||
command::add("loadzone", [](const command::params& params)
|
||||
{
|
||||
if (params.size() < 2)
|
||||
{
|
||||
console::info("usage: loadzone <zone>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const char* name = params.get(1);
|
||||
|
||||
if (!fastfiles::exists(name))
|
||||
{
|
||||
console::warn("loadzone: zone \"%s\" could not be found!\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
game::XZoneInfo info;
|
||||
info.name = name;
|
||||
info.allocFlags = game::DB_ZONE_GAME;
|
||||
info.freeFlags = 0;
|
||||
|
||||
info.allocFlags |= game::DB_ZONE_CUSTOM; // skip extra zones with this flag!
|
||||
|
||||
game::DB_LoadXAssets(&info, 1, game::DBSyncMode::DB_LOAD_ASYNC);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
namespace fastfiles
|
||||
{
|
||||
bool exists(const std::string& zone);
|
||||
|
||||
std::string get_current_fastfile();
|
||||
|
||||
void enum_assets(const game::XAssetType type,
|
||||
|
@ -83,6 +83,9 @@ namespace filesystem
|
||||
get_search_paths().insert(".");
|
||||
get_search_paths().insert("h1-mod");
|
||||
get_search_paths().insert("data");
|
||||
|
||||
// fs_game flags
|
||||
utils::hook::set<uint32_t>(0x189275_b, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -247,13 +247,13 @@ namespace party
|
||||
return connect_state.challenge;
|
||||
}
|
||||
|
||||
void start_map(const std::string& mapname)
|
||||
void start_map(const std::string& mapname, bool dev)
|
||||
{
|
||||
if (game::Live_SyncOnlineDataFlags(0) > 32)
|
||||
{
|
||||
scheduler::once([=]()
|
||||
{
|
||||
command::execute("map " + mapname, false);
|
||||
start_map(mapname, dev);
|
||||
}, scheduler::pipeline::main, 1s);
|
||||
}
|
||||
else
|
||||
@ -299,6 +299,8 @@ namespace party
|
||||
command::execute(utils::string::va("party_maxplayers %i", maxclients->current.integer), true);
|
||||
}*/
|
||||
|
||||
command::execute((dev ? "sv_cheats 1" : "sv_cheats 0"), true);
|
||||
|
||||
const auto* args = "StartServer";
|
||||
game::UI_RunMenuScript(0, &args);
|
||||
}
|
||||
@ -349,7 +351,17 @@ namespace party
|
||||
return;
|
||||
}
|
||||
|
||||
start_map(argument[1]);
|
||||
start_map(argument[1], false);
|
||||
});
|
||||
|
||||
command::add("devmap", [](const command::params& argument)
|
||||
{
|
||||
if (argument.size() != 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
party::start_map(argument[1], true);
|
||||
});
|
||||
|
||||
command::add("map_restart", []()
|
||||
|
@ -6,7 +6,7 @@ namespace party
|
||||
void reset_connect_state();
|
||||
|
||||
void connect(const game::netadr_s& target);
|
||||
void start_map(const std::string& mapname);
|
||||
void start_map(const std::string& mapname, bool dev = false);
|
||||
|
||||
void clear_sv_motd();
|
||||
game::netadr_s get_state_host();
|
||||
|
124
src/client/component/weapon.cpp
Normal file
124
src/client/component/weapon.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "console.hpp"
|
||||
#include "command.hpp"
|
||||
#include "fastfiles.hpp"
|
||||
|
||||
#include "utils/hook.hpp"
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour g_setup_level_weapon_def_hook;
|
||||
void g_setup_level_weapon_def_stub()
|
||||
{
|
||||
// precache level weapons first
|
||||
g_setup_level_weapon_def_hook.invoke<void>();
|
||||
|
||||
std::vector<game::WeaponDef*> weapons;
|
||||
|
||||
// find all weapons in asset pools
|
||||
fastfiles::enum_assets(game::ASSET_TYPE_WEAPON, [&weapons](game::XAssetHeader header)
|
||||
{
|
||||
weapons.push_back(header.weapon);
|
||||
}, false);
|
||||
|
||||
// sort weapons
|
||||
std::sort(weapons.begin(), weapons.end(), [](game::WeaponDef* weapon1, game::WeaponDef* weapon2)
|
||||
{
|
||||
return std::string_view(weapon1->name) <
|
||||
std::string_view(weapon2->name);
|
||||
});
|
||||
|
||||
// precache items
|
||||
for (std::size_t i = 0; i < weapons.size(); i++)
|
||||
{
|
||||
console::debug("precaching weapon \"%s\"\n", weapons[i]->name);
|
||||
game::G_GetWeaponForName(weapons[i]->name);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_weapon_field(const std::string& weapon_name, unsigned int field, T value)
|
||||
{
|
||||
auto weapon = game::DB_FindXAssetHeader(game::ASSET_TYPE_WEAPON, weapon_name.data(), false).data;
|
||||
if (weapon)
|
||||
{
|
||||
if (field && field < (0xE20 + sizeof(T)))
|
||||
{
|
||||
*reinterpret_cast<T*>(reinterpret_cast<std::uintptr_t>(weapon) + field) = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("weapon field: %d is higher than the size of weapon struct!\n", field);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("weapon %s not found!\n", weapon_name.data());
|
||||
}
|
||||
}
|
||||
|
||||
void set_weapon_field_float(const std::string& weapon_name, unsigned int field, float value)
|
||||
{
|
||||
set_weapon_field<float>(weapon_name, field, value);
|
||||
}
|
||||
|
||||
void set_weapon_field_int(const std::string& weapon_name, unsigned int field, int value)
|
||||
{
|
||||
set_weapon_field<int>(weapon_name, field, value);
|
||||
}
|
||||
|
||||
void set_weapon_field_bool(const std::string& weapon_name, unsigned int field, bool value)
|
||||
{
|
||||
set_weapon_field<bool>(weapon_name, field, value);
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
g_setup_level_weapon_def_hook.create(0x462630_b, g_setup_level_weapon_def_stub);
|
||||
|
||||
#ifdef DEBUG
|
||||
command::add("setWeaponFieldFloat", [](const command::params& params)
|
||||
{
|
||||
if (params.size() <= 3)
|
||||
{
|
||||
console::info("usage: setWeaponFieldInt <weapon> <field> <value>\n");
|
||||
return;
|
||||
}
|
||||
set_weapon_field_float(params.get(1), atoi(params.get(2)), static_cast<float>(atof(params.get(3))));
|
||||
});
|
||||
|
||||
command::add("setWeaponFieldInt", [](const command::params& params)
|
||||
{
|
||||
if (params.size() <= 3)
|
||||
{
|
||||
console::info("usage: setWeaponFieldInt <weapon> <field> <value>\n");
|
||||
return;
|
||||
}
|
||||
set_weapon_field_int(params.get(1), atoi(params.get(2)), static_cast<int>(atoi(params.get(3))));
|
||||
});
|
||||
|
||||
command::add("setWeaponFieldBool", [](const command::params& params)
|
||||
{
|
||||
if (params.size() <= 3)
|
||||
{
|
||||
console::info("usage: setWeaponFieldBool <weapon> <field> <value>\n");
|
||||
return;
|
||||
}
|
||||
set_weapon_field_bool(params.get(1), atoi(params.get(2)), static_cast<bool>(atoi(params.get(3))));
|
||||
});
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(weapon::component)
|
@ -1010,6 +1010,20 @@ namespace game
|
||||
DB_LOAD_SYNC_SKIP_ALWAYS_LOADED = 0x5,
|
||||
};
|
||||
|
||||
enum DBAllocFlags : std::int32_t
|
||||
{
|
||||
DB_ZONE_NONE = 0x0,
|
||||
DB_ZONE_COMMON = 0x1,
|
||||
DB_ZONE_UI = 0x2,
|
||||
DB_ZONE_GAME = 0x4,
|
||||
DB_ZONE_LOAD = 0x8,
|
||||
DB_ZONE_DEV = 0x10,
|
||||
DB_ZONE_BASEMAP = 0x20,
|
||||
DB_ZONE_TRANSIENT_POOL = 0x40,
|
||||
DB_ZONE_TRANSIENT_MASK = 0x40,
|
||||
DB_ZONE_CUSTOM = 0x200 // added for custom zone loading
|
||||
};
|
||||
|
||||
struct XZoneInfo
|
||||
{
|
||||
const char* name;
|
||||
@ -1397,6 +1411,11 @@ namespace game
|
||||
const char* name;
|
||||
};
|
||||
|
||||
struct WeaponDef
|
||||
{
|
||||
const char* name;
|
||||
};
|
||||
|
||||
union XAssetHeader
|
||||
{
|
||||
void* data;
|
||||
@ -1408,6 +1427,7 @@ namespace game
|
||||
LuaFile* luaFile;
|
||||
GfxImage* image;
|
||||
TTF* ttf;
|
||||
WeaponDef* weapon;
|
||||
};
|
||||
|
||||
struct XAsset
|
||||
|
@ -164,6 +164,9 @@ namespace game
|
||||
WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x0, 0x0};
|
||||
WEAK symbol<XAssetHeader(XAssetType type, const char* name,
|
||||
int createDefault)> DB_FindXAssetHeader{0x1F1120, 0x3950C0};
|
||||
WEAK symbol<bool(const char* zone, int source)> DB_FileExists{0x1F0D50, 0x394DC0};
|
||||
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{0x1F31E0, 0x397500};
|
||||
WEAK symbol<bool(const char* zoneName)> DB_IsLocalized{0x0, 0x396790};
|
||||
|
||||
WEAK symbol<void(int clientNum, const char* menu,
|
||||
int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x3F20A0, 0x1E1210};
|
||||
@ -207,6 +210,7 @@ namespace game
|
||||
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x3AB100, 0x4F79C0};
|
||||
WEAK symbol<bool(int, void const*, const netadr_s*)> Sys_SendPacket{0x0, 0x5BDA90};
|
||||
WEAK symbol<bool(const char* path)> Sys_FileExists{0x0, 0x0};
|
||||
WEAK symbol<HANDLE(Sys_Folder, const char* baseFilename)> Sys_CreateFile{0x0, 0x5B2860};
|
||||
|
||||
WEAK symbol<const char*(const char*)> UI_GetMapDisplayName{0x0, 0x4DDEE0};
|
||||
WEAK symbol<const char*(const char*)> UI_GetGameTypeDisplayName{0x0, 0x4DD8C0};
|
||||
@ -238,6 +242,7 @@ namespace game
|
||||
WEAK symbol<int> connectionState{0x0, 0x2EC82C8};
|
||||
|
||||
WEAK symbol<int> g_poolSize{0x0, 0x0};
|
||||
WEAK symbol<int> g_compressor{0x2574804, 0x3962804};
|
||||
|
||||
WEAK symbol<scrVarGlob_t> scr_VarGlob{0xBD80E00, 0xB138180};
|
||||
WEAK symbol<scrVmPub_t> scr_VmPub{0xC3F4E20, 0xB7AE3C0};
|
||||
|
Loading…
Reference in New Issue
Block a user