December 2069 Update
This commit is contained in:
parent
0857be79df
commit
079f90631c
2
deps/GSL
vendored
2
deps/GSL
vendored
@ -1 +1 @@
|
||||
Subproject commit e64c97fc2cfc11992098bb38eda932de275e3f4d
|
||||
Subproject commit b8ac820fe1efde9c63ca290f4ae8670f938a7dae
|
2
deps/WinToast
vendored
2
deps/WinToast
vendored
@ -1 +1 @@
|
||||
Subproject commit 821c4818ade1aa4da56ac753285c159ce26fd597
|
||||
Subproject commit a78ce469b456c06103b3b30d4bd37e7bb80da30c
|
2
deps/asmjit
vendored
2
deps/asmjit
vendored
@ -1 +1 @@
|
||||
Subproject commit 118ae6ced160f68dd142a0d758aa7efb4356196a
|
||||
Subproject commit cfc9f813cc6ccda63cad872edb32b38e0662bedb
|
2
deps/gsc-tool
vendored
2
deps/gsc-tool
vendored
@ -1 +1 @@
|
||||
Subproject commit b8e30e6334aa33ea731caf8d0700a9e6c7794c09
|
||||
Subproject commit 9ff3b871ef59b14eb8b32d258bf18347e14a3e18
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
||||
Subproject commit 7e863d21429f94ed6a720e24499a12a3f852bb31
|
||||
Subproject commit c900951dab1bb94bab803fc57688dac18e3b71f9
|
2
deps/libtommath
vendored
2
deps/libtommath
vendored
@ -1 +1 @@
|
||||
Subproject commit 8314bde5e5c8e5d9331460130a9d1066e324f091
|
||||
Subproject commit 5809141a3a6ec1bf3443c927c02b955e19224016
|
2
deps/minhook
vendored
2
deps/minhook
vendored
@ -1 +1 @@
|
||||
Subproject commit f5485b8454544c2f034c78f8f127c1d03dea3636
|
||||
Subproject commit c1a7c3843bd1a5fe3eb779b64c0d823bca3dc339
|
2
deps/rapidjson
vendored
2
deps/rapidjson
vendored
@ -1 +1 @@
|
||||
Subproject commit 6089180ecb704cb2b136777798fa1be303618975
|
||||
Subproject commit d621dc9e9c77f81e5c8a35b8dcc16dcd63351321
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
||||
Subproject commit 643e17b7498d12ab8d15565662880579692f769d
|
||||
Subproject commit ef24c4c7502169f016dcd2a26923dbaf3216748c
|
@ -5,14 +5,16 @@
|
||||
|
||||
#include "auth.hpp"
|
||||
#include "command.hpp"
|
||||
#include "network.hpp"
|
||||
#include "console.hpp"
|
||||
#include "network.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/smbios.hpp>
|
||||
#include <utils/info_string.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>
|
||||
|
||||
namespace auth
|
||||
{
|
||||
@ -31,13 +33,26 @@ namespace auth
|
||||
|
||||
std::string get_hw_profile_guid()
|
||||
{
|
||||
auto hw_profile_path = (utils::properties::get_appdata_path() / "s1-guid.dat").generic_string();
|
||||
if (utils::io::file_exists(hw_profile_path))
|
||||
{
|
||||
std::string hw_profile_info;
|
||||
if (utils::io::read_file(hw_profile_path, &hw_profile_info) && !hw_profile_info.empty())
|
||||
{
|
||||
return hw_profile_info;
|
||||
}
|
||||
}
|
||||
|
||||
HW_PROFILE_INFO info;
|
||||
if (!GetCurrentHwProfileA(&info))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::string{info.szHwProfileGuid, sizeof(info.szHwProfileGuid)};
|
||||
auto hw_profile_info = std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) };
|
||||
utils::io::write_file(hw_profile_path, hw_profile_info);
|
||||
|
||||
return hw_profile_info;
|
||||
}
|
||||
|
||||
std::string get_protected_data()
|
||||
|
@ -33,9 +33,7 @@ namespace bullet
|
||||
return;
|
||||
}
|
||||
|
||||
dvars::bg_surfacePenetration = game::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f,
|
||||
0.0f, std::numeric_limits<float>::max(), game::DVAR_FLAG_SAVED,
|
||||
"Set to a value greater than 0 to override the surface penetration depth");
|
||||
dvars::bg_surfacePenetration = game::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f, 0.0f, std::numeric_limits<float>::max(), game::DVAR_FLAG_SAVED);
|
||||
bg_get_surface_penetration_depth_hook.create(0x1401641A0, &bg_get_surface_penetration_depth_stub);
|
||||
}
|
||||
};
|
||||
|
@ -253,7 +253,10 @@ namespace command
|
||||
|
||||
void execute(std::string command, const bool sync)
|
||||
{
|
||||
command += "\n";
|
||||
if (!command.ends_with('\n'))
|
||||
{
|
||||
command += "\n";
|
||||
}
|
||||
|
||||
if (sync)
|
||||
{
|
||||
|
@ -177,9 +177,9 @@ namespace dedicated
|
||||
return;
|
||||
}
|
||||
|
||||
game::Dvar_RegisterBool("dedicated", true, game::DVAR_FLAG_READ, "Dedicated server");
|
||||
game::Dvar_RegisterBool("dedicated", true, game::DVAR_FLAG_READ);
|
||||
|
||||
sv_lanOnly = game::Dvar_RegisterBool("sv_lanOnly", false, game::DVAR_FLAG_NONE, "Don't send heartbeat");
|
||||
sv_lanOnly = game::Dvar_RegisterBool("sv_lanOnly", false, game::DVAR_FLAG_NONE);
|
||||
|
||||
// Disable VirtualLobby
|
||||
dvars::override::register_bool("virtualLobbyEnabled", false, game::DVAR_FLAG_NONE | game::DVAR_FLAG_READ);
|
||||
|
@ -19,28 +19,28 @@ namespace dedicated_info
|
||||
|
||||
scheduler::loop([]()
|
||||
{
|
||||
auto* sv_running = game::Dvar_FindVar("sv_running");
|
||||
const auto* sv_running = game::Dvar_FindVar("sv_running");
|
||||
if (!sv_running || !sv_running->current.enabled)
|
||||
{
|
||||
console::set_title("s1-mod Dedicated Server");
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
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 bot_count = 0;
|
||||
auto client_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* self = &game::mp::g_entities[i];
|
||||
|
||||
if (client->header.state >= 1 && self && self->client)
|
||||
{
|
||||
client_count++;
|
||||
++client_count;
|
||||
if (game::SV_BotIsBot(i))
|
||||
{
|
||||
++bot_count;
|
||||
|
@ -342,7 +342,7 @@ namespace demonware
|
||||
|
||||
void bd_logger_stub(const char* const function, const char* const msg, ...)
|
||||
{
|
||||
static const auto* bd_logger_enabled = game::Dvar_RegisterBool("bd_logger_enabled", false, game::DVAR_FLAG_NONE, "Enable bdLogger");
|
||||
static const auto* bd_logger_enabled = game::Dvar_RegisterBool("bd_logger_enabled", false, game::DVAR_FLAG_NONE);
|
||||
if (!bd_logger_enabled->current.enabled)
|
||||
{
|
||||
return;
|
||||
|
@ -181,7 +181,7 @@ namespace dvar_cheats
|
||||
constexpr auto value = false;
|
||||
#endif
|
||||
|
||||
dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DVAR_FLAG_REPLICATED, "Allow cheat commands and dvars on this server");
|
||||
dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DVAR_FLAG_REPLICATED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ namespace dvars
|
||||
utils::hook::detour dvar_set_int_hook;
|
||||
utils::hook::detour dvar_set_string_hook;
|
||||
|
||||
game::dvar_t* dvar_register_bool_stub(const char* name, bool value, unsigned int flags, const char* description)
|
||||
game::dvar_t* dvar_register_bool_stub(const char* name, bool value, unsigned int flags)
|
||||
{
|
||||
auto* var = find_dvar(override::register_bool_overrides, name);
|
||||
if (var)
|
||||
@ -218,11 +218,10 @@ namespace dvars
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_bool_hook.invoke<game::dvar_t*>(name, value, flags, description);
|
||||
return dvar_register_bool_hook.invoke<game::dvar_t*>(name, value, flags);
|
||||
}
|
||||
|
||||
game::dvar_t* dvar_register_float_stub(const char* name, float value, float min, float max, unsigned int flags,
|
||||
const char* description)
|
||||
game::dvar_t* dvar_register_float_stub(const char* name, float value, float min, float max, unsigned int flags)
|
||||
{
|
||||
auto* var = find_dvar(override::register_float_overrides, name);
|
||||
if (var)
|
||||
@ -233,11 +232,10 @@ namespace dvars
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_float_hook.invoke<game::dvar_t*>(name, value, min, max, flags, description);
|
||||
return dvar_register_float_hook.invoke<game::dvar_t*>(name, value, min, max, flags);
|
||||
}
|
||||
|
||||
game::dvar_t* dvar_register_int_stub(const char* name, int value, int min, int max, unsigned int flags,
|
||||
const char* description)
|
||||
game::dvar_t* dvar_register_int_stub(const char* name, int value, int min, int max, unsigned int flags)
|
||||
{
|
||||
auto* var = find_dvar(override::register_int_overrides, name);
|
||||
if (var)
|
||||
@ -248,10 +246,10 @@ namespace dvars
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_int_hook.invoke<game::dvar_t*>(name, value, min, max, flags, description);
|
||||
return dvar_register_int_hook.invoke<game::dvar_t*>(name, value, min, max, flags);
|
||||
}
|
||||
|
||||
game::dvar_t* dvar_register_string_stub(const char* name, const char* value, unsigned int flags, const char* description)
|
||||
game::dvar_t* dvar_register_string_stub(const char* name, const char* value, unsigned int flags)
|
||||
{
|
||||
auto* var = find_dvar(override::register_string_overrides, name);
|
||||
if (var)
|
||||
@ -260,11 +258,10 @@ namespace dvars
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_string_hook.invoke<game::dvar_t*>(name, value, flags, description);
|
||||
return dvar_register_string_hook.invoke<game::dvar_t*>(name, value, flags);
|
||||
}
|
||||
|
||||
game::dvar_t* dvar_register_vector2_stub(const char* name, float x, float y, float min, float max,
|
||||
unsigned int flags, const char* description)
|
||||
game::dvar_t* dvar_register_vector2_stub(const char* name, float x, float y, float min, float max, unsigned int flags)
|
||||
{
|
||||
auto* var = find_dvar(override::register_vector2_overrides, name);
|
||||
if (var)
|
||||
@ -276,11 +273,10 @@ namespace dvars
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_vector2_hook.invoke<game::dvar_t*>(name, x, y, min, max, flags, description);
|
||||
return dvar_register_vector2_hook.invoke<game::dvar_t*>(name, x, y, min, max, flags);
|
||||
}
|
||||
|
||||
game::dvar_t* dvar_register_vector3_stub(const char* name, float x, float y, float z, float min,
|
||||
float max, unsigned int flags, const char* description)
|
||||
game::dvar_t* dvar_register_vector3_stub(const char* name, float x, float y, float z, float min, float max, unsigned int flags)
|
||||
{
|
||||
auto* var = find_dvar(override::register_vector3_overrides, name);
|
||||
if (var)
|
||||
@ -293,7 +289,7 @@ namespace dvars
|
||||
flags = var->flags;
|
||||
}
|
||||
|
||||
return dvar_register_vector3_hook.invoke<game::dvar_t*>(name, x, y, z, min, max, flags, description);
|
||||
return dvar_register_vector3_hook.invoke<game::dvar_t*>(name, x, y, z, min, max, flags);
|
||||
}
|
||||
|
||||
void dvar_set_bool_stub(game::dvar_t* dvar, bool boolean)
|
||||
|
@ -135,7 +135,7 @@ namespace fastfiles
|
||||
db_try_load_x_file_internal_hook.create(SELECT_VALUE(0x1401816F0, 0x1402741C0), &db_try_load_x_file_internal);
|
||||
|
||||
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_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE);
|
||||
|
||||
command::add("loadzone", [](const command::params& params)
|
||||
{
|
||||
|
@ -67,6 +67,11 @@ namespace filesystem
|
||||
register_path("s1");
|
||||
register_path(get_binary_directory() + "\\data");
|
||||
|
||||
if (get_binary_directory() != std::filesystem::current_path())
|
||||
{
|
||||
register_path(std::filesystem::current_path() / "data");
|
||||
}
|
||||
|
||||
// game's search paths
|
||||
register_path("devraw");
|
||||
register_path("devraw_shared");
|
||||
|
@ -137,9 +137,9 @@ namespace fps
|
||||
}
|
||||
}
|
||||
|
||||
const game::dvar_t* cg_draw_fps_register_stub(const char* dvar_name, const char** value_list, const int default_index, unsigned int /*flags*/, const char* description)
|
||||
const game::dvar_t* cg_draw_fps_register_stub(const char* dvar_name, const char** value_list, const int default_index, unsigned int /*flags*/)
|
||||
{
|
||||
cg_drawFPS = game::Dvar_RegisterEnum(dvar_name, value_list, default_index, game::DVAR_FLAG_SAVED, description);
|
||||
cg_drawFPS = game::Dvar_RegisterEnum(dvar_name, value_list, default_index, game::DVAR_FLAG_SAVED);
|
||||
return cg_drawFPS;
|
||||
}
|
||||
}
|
||||
@ -173,12 +173,12 @@ namespace fps
|
||||
scheduler::loop(cg_draw_fps, scheduler::pipeline::renderer);
|
||||
if (game::environment::is_mp())
|
||||
{
|
||||
cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Draw ping");
|
||||
cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED);
|
||||
scheduler::loop(cg_draw_ping, scheduler::pipeline::renderer);
|
||||
}
|
||||
|
||||
game::Dvar_RegisterBool("cg_infobar_ping", false, game::DVAR_FLAG_SAVED, "Show server latency");
|
||||
game::Dvar_RegisterBool("cg_infobar_fps", false, game::DVAR_FLAG_SAVED, "Show FPS counter");
|
||||
game::Dvar_RegisterBool("cg_infobar_ping", false, game::DVAR_FLAG_SAVED);
|
||||
game::Dvar_RegisterBool("cg_infobar_fps", false, game::DVAR_FLAG_SAVED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -746,33 +746,30 @@ namespace game_console
|
||||
|
||||
// add our dvars
|
||||
dvars::con_inputBoxColor = game::Dvar_RegisterVec4("con_inputBoxColor", 0.2f, 0.2f, 0.2f, 0.9f, 0.0f, 1.0f,
|
||||
game::DVAR_FLAG_SAVED,
|
||||
"color of console input box");
|
||||
game::DVAR_FLAG_SAVED);
|
||||
dvars::con_inputHintBoxColor = game::Dvar_RegisterVec4("con_inputHintBoxColor", 0.3f, 0.3f, 0.3f, 1.0f,
|
||||
0.0f, 1.0f,
|
||||
game::DVAR_FLAG_SAVED, "color of console input hint box");
|
||||
game::DVAR_FLAG_SAVED);
|
||||
dvars::con_outputBarColor = game::Dvar_RegisterVec4("con_outputBarColor", 0.5f, 0.5f, 0.5f, 0.6f, 0.0f,
|
||||
1.0f, game::DVAR_FLAG_SAVED,
|
||||
"color of console output bar");
|
||||
1.0f, game::DVAR_FLAG_SAVED);
|
||||
dvars::con_outputSliderColor = game::Dvar_RegisterVec4("con_outputSliderColor", 1.0f, 0.8f, 0.0f, 1.0f,
|
||||
0.0f, 1.0f,
|
||||
game::DVAR_FLAG_SAVED, "color of console output slider");
|
||||
game::DVAR_FLAG_SAVED);
|
||||
dvars::con_outputWindowColor = game::Dvar_RegisterVec4("con_outputWindowColor", 0.25f, 0.25f, 0.25f, 0.85f,
|
||||
0.0f,
|
||||
1.0f, game::DVAR_FLAG_SAVED, "color of console output window");
|
||||
1.0f, game::DVAR_FLAG_SAVED);
|
||||
dvars::con_inputDvarMatchColor = game::Dvar_RegisterVec4("con_inputDvarMatchColor", 1.0f, 1.0f, 0.8f, 1.0f,
|
||||
0.0f,
|
||||
1.0f, game::DVAR_FLAG_SAVED, "color of console matched dvar");
|
||||
1.0f, game::DVAR_FLAG_SAVED);
|
||||
dvars::con_inputDvarValueColor = game::Dvar_RegisterVec4("con_inputDvarValueColor", 1.0f, 1.0f, 0.8f, 1.0f,
|
||||
0.0f,
|
||||
1.0f, game::DVAR_FLAG_SAVED, "color of console matched dvar value");
|
||||
1.0f, game::DVAR_FLAG_SAVED);
|
||||
dvars::con_inputDvarInactiveValueColor = game::Dvar_RegisterVec4(
|
||||
"con_inputDvarInactiveValueColor", 0.8f, 0.8f,
|
||||
0.8f, 1.0f, 0.0f, 1.0f, game::DVAR_FLAG_SAVED,
|
||||
"color of console inactive dvar value");
|
||||
0.8f, 1.0f, 0.0f, 1.0f, game::DVAR_FLAG_SAVED);
|
||||
dvars::con_inputCmdMatchColor = game::Dvar_RegisterVec4("con_inputCmdMatchColor", 0.80f, 0.80f, 1.0f, 1.0f,
|
||||
0.0f,
|
||||
1.0f, game::DVAR_FLAG_SAVED, "color of console matched command");
|
||||
1.0f, game::DVAR_FLAG_SAVED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ namespace game_log
|
||||
|
||||
scheduler::once([]
|
||||
{
|
||||
dvars::g_log = game::Dvar_RegisterString("g_log", "logs/games_mp.log", game::DVAR_FLAG_NONE, "Log file name");
|
||||
dvars::g_log = game::Dvar_RegisterString("g_log", "logs/games_mp.log", game::DVAR_FLAG_NONE);
|
||||
}, scheduler::pipeline::main);
|
||||
|
||||
scripting::on_init([]
|
||||
@ -102,9 +102,9 @@ namespace game_log
|
||||
g_log_printf("InitGame\n");
|
||||
});
|
||||
|
||||
scripting::on_shutdown([](int free_scripts)
|
||||
scripting::on_shutdown([](const int clear_scripts) -> void
|
||||
{
|
||||
console::info("==== ShutdownGame (%d) ====\n", free_scripts);
|
||||
console::info("==== ShutdownGame (%d) ====\n", clear_scripts);
|
||||
|
||||
g_log_printf("ShutdownGame:\n");
|
||||
g_log_printf("------------------------------------------------------------\n");
|
||||
|
@ -144,54 +144,45 @@ namespace gameplay
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false,
|
||||
game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo");
|
||||
dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false, game::DVAR_FLAG_REPLICATED);
|
||||
pm_weapon_use_ammo_hook.create(SELECT_VALUE(0x1403DD050, 0x140162B20), &pm_weapon_use_ammo_stub);
|
||||
|
||||
if (game::environment::is_sp()) return;
|
||||
|
||||
// Implement player ejection dvar
|
||||
dvars::g_playerEjection = game::Dvar_RegisterBool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED,
|
||||
"Flag whether player ejection is on or off");
|
||||
dvars::g_playerEjection = game::Dvar_RegisterBool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED);
|
||||
utils::hook::call(0x1402D5E4A, stuck_in_client_stub);
|
||||
|
||||
// Implement player collision dvar
|
||||
dvars::g_playerCollision = game::Dvar_RegisterBool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED,
|
||||
"Flag whether player collision is on or off");
|
||||
dvars::g_playerCollision = game::Dvar_RegisterBool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED);
|
||||
utils::hook::call(0x1404563DA, cm_transformed_capsule_trace_stub); // SV_ClipMoveToEntity
|
||||
utils::hook::call(0x1401F7F8F, cm_transformed_capsule_trace_stub); // CG_ClipMoveToEntity
|
||||
|
||||
// Implement bouncing dvar
|
||||
utils::hook::jump(0x14014DF91, pm_bouncing_stub_mp, true);
|
||||
dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false,
|
||||
game::DVAR_FLAG_REPLICATED, "Enable bouncing");
|
||||
dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false, game::DVAR_FLAG_REPLICATED);
|
||||
|
||||
// Change jump_slowdownEnable dvar flags to just "replicated"
|
||||
utils::hook::set<uint8_t>(0x140135992, game::DVAR_FLAG_REPLICATED);
|
||||
|
||||
// Choosing the following min/max because the game would truncate larger values
|
||||
dvars::g_gravity = game::Dvar_RegisterInt("g_gravity", 800, std::numeric_limits<short>::min(),
|
||||
std::numeric_limits<short>::max(), game::DVAR_FLAG_REPLICATED, "Gravity in inches per second per second");
|
||||
dvars::g_gravity = game::Dvar_RegisterInt("g_gravity", 800, std::numeric_limits<short>::min(), std::numeric_limits<short>::max(), game::DVAR_FLAG_REPLICATED);
|
||||
utils::hook::jump(0x1402D5A5D, client_end_frame_stub, true);
|
||||
utils::hook::nop(0x1402D5A69, 1); // Nop skipped opcode
|
||||
|
||||
// Choosing the following min/max because the game would truncate larger values
|
||||
dvars::g_speed = game::Dvar_RegisterInt("g_speed", 190,
|
||||
std::numeric_limits<short>::min(), std::numeric_limits<short>::max(), game::DVAR_FLAG_REPLICATED, "Player speed");
|
||||
dvars::g_speed = game::Dvar_RegisterInt("g_speed", 190, std::numeric_limits<short>::min(), std::numeric_limits<short>::max(), game::DVAR_FLAG_REPLICATED);
|
||||
utils::hook::jump(0x1402D6A8C, client_think_real_stub, true);
|
||||
utils::hook::nop(0x1402D6A98, 4); // Nop skipped opcodes
|
||||
|
||||
dvars::jump_height = game::Dvar_RegisterFloat("jump_height", 39.0f,
|
||||
0.0f, 1000.0f, game::DVAR_FLAG_REPLICATED, "The maximum height of a player's jump");
|
||||
dvars::jump_height = game::Dvar_RegisterFloat("jump_height", 39.0f, 0.0f, 1000.0f, game::DVAR_FLAG_REPLICATED);
|
||||
utils::hook::call(0x1401352FF, jump_start_stub);
|
||||
|
||||
dvars::jump_ladderPushVel = game::Dvar_RegisterFloat("jump_ladderPushVel", 128.0f,
|
||||
0.0f, 1024.0f, game::DVAR_FLAG_REPLICATED, "The velocity of a jump off of a ladder");
|
||||
dvars::jump_ladderPushVel = game::Dvar_RegisterFloat("jump_ladderPushVel", 128.0f, 0.0f, 1024.0f, game::DVAR_FLAG_REPLICATED);
|
||||
utils::hook::jump(0x1401358B3, jump_push_off_ladder, true);
|
||||
utils::hook::nop(0x1401358BF, 4); // Nop skipped opcodes
|
||||
|
||||
dvars::g_elevators = game::Dvar_RegisterBool("g_elevators", false,
|
||||
game::DVAR_FLAG_REPLICATED, "Enable elevators");
|
||||
dvars::g_elevators = game::Dvar_RegisterBool("g_elevators", false, game::DVAR_FLAG_REPLICATED);
|
||||
utils::hook::call(0x140146134, pm_player_trace_stub);
|
||||
utils::hook::call(0x14014619B, pm_player_trace_stub);
|
||||
|
||||
|
@ -186,100 +186,6 @@ namespace gsc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scr_get_vector(unsigned int index, float* vector_value)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::scr_VmPub->top - index;
|
||||
if (value->type == game::VAR_VECTOR)
|
||||
{
|
||||
std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3]));
|
||||
return;
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not a vector", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
}
|
||||
|
||||
int scr_get_int(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::scr_VmPub->top - index;
|
||||
if (value->type == game::VAR_INTEGER)
|
||||
{
|
||||
return value->u.intValue;
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not an int", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
float scr_get_float(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::scr_VmPub->top - index;
|
||||
if (value->type == game::VAR_FLOAT)
|
||||
{
|
||||
return value->u.floatValue;
|
||||
}
|
||||
|
||||
if (value->type == game::VAR_INTEGER)
|
||||
{
|
||||
return static_cast<float>(value->u.intValue);
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not a float", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
int scr_get_pointer_type(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER)
|
||||
{
|
||||
return static_cast<int>(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue));
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scr_get_type(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
return (game::scr_VmPub->top - index)->type;
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* scr_get_type_name(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
return var_typename[(game::scr_VmPub->top - index)->type];
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
|
||||
@ -299,34 +205,130 @@ namespace gsc
|
||||
return {};
|
||||
}
|
||||
|
||||
void scr_get_vector(unsigned int index, float* vector_value)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::scr_VmPub->top - index;
|
||||
if (value->type == game::VAR_VECTOR)
|
||||
{
|
||||
std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3]));
|
||||
return;
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not a vector", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
}
|
||||
|
||||
int scr_get_int(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::scr_VmPub->top - index;
|
||||
if (value->type == game::VAR_INTEGER)
|
||||
{
|
||||
return value->u.intValue;
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not an int", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
float scr_get_float(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
auto* value = game::scr_VmPub->top - index;
|
||||
if (value->type == game::VAR_FLOAT)
|
||||
{
|
||||
return value->u.floatValue;
|
||||
}
|
||||
|
||||
if (value->type == game::VAR_INTEGER)
|
||||
{
|
||||
return static_cast<float>(value->u.intValue);
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not a float", var_typename[value->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
int scr_get_pointer_type(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER)
|
||||
{
|
||||
return static_cast<int>(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue));
|
||||
}
|
||||
|
||||
scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type]));
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scr_get_type(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
return (game::scr_VmPub->top - index)->type;
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* scr_get_type_name(unsigned int index)
|
||||
{
|
||||
if (index < game::scr_VmPub->outparamcount)
|
||||
{
|
||||
return var_typename[(game::scr_VmPub->top - index)->type];
|
||||
}
|
||||
|
||||
scr_error(va("Parameter %u does not exist", index + 1));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class error final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
scr_emit_function_hook.create(SELECT_VALUE(0x1403113D0, 0x1403ED900), &scr_emit_function_stub);
|
||||
|
||||
utils::hook::call(SELECT_VALUE(0x140311364, 0x1403ED894), compile_error_stub); // LinkFile
|
||||
utils::hook::call(SELECT_VALUE(0x1403113B8, 0x1403ED8E8), compile_error_stub); // LinkFile
|
||||
utils::hook::call(SELECT_VALUE(0x1403114AA, 0x1403ED9DB), find_variable_stub); // Scr_EmitFunction
|
||||
|
||||
// Restore basic error messages for commonly used scr functions
|
||||
utils::hook::jump(SELECT_VALUE(0x14031BD80, 0x1403F8510), scr_get_const_string);
|
||||
utils::hook::jump(game::Scr_GetInt, scr_get_int);
|
||||
utils::hook::jump(game::Scr_GetFloat, scr_get_float);
|
||||
utils::hook::jump(game::Scr_GetVector, scr_get_vector);
|
||||
|
||||
utils::hook::jump(SELECT_VALUE(0x14031C690, 0x1403F8D70), scr_get_type);
|
||||
utils::hook::jump(SELECT_VALUE(0x14031C700, 0x1403F8DE0), scr_get_type_name);
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
scr_emit_function_hook.create(0x1403ED900, &scr_emit_function_stub);
|
||||
|
||||
utils::hook::call(0x1403ED894, compile_error_stub); // LinkFile
|
||||
utils::hook::call(0x1403ED8E8, compile_error_stub); // LinkFile
|
||||
utils::hook::call(0x1403ED9DB, find_variable_stub); // Scr_EmitFunction
|
||||
|
||||
// Restore basic error messages for commonly used scr functions
|
||||
utils::hook::jump(0x1403F8990, scr_get_object);
|
||||
utils::hook::jump(0x1403F8510, scr_get_const_string);
|
||||
utils::hook::jump(0x1403F82F0, scr_get_const_istring);
|
||||
utils::hook::jump(0x140327870, scr_validate_localized_string_ref);
|
||||
utils::hook::jump(0x1403F8EC0, scr_get_vector);
|
||||
utils::hook::jump(0x1403F88D0, scr_get_int);
|
||||
utils::hook::jump(0x1403F8820, scr_get_float);
|
||||
|
||||
utils::hook::jump(0x1403F8BA0, scr_get_pointer_type);
|
||||
utils::hook::jump(0x1403F8D70, scr_get_type);
|
||||
utils::hook::jump(0x1403F8DE0, scr_get_type_name);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
|
@ -3,4 +3,11 @@
|
||||
namespace gsc
|
||||
{
|
||||
std::optional<std::pair<std::string, std::string>> find_function(const char* pos);
|
||||
|
||||
void scr_get_vector(unsigned int index, float* vector_value);
|
||||
int scr_get_int(unsigned int index);
|
||||
float scr_get_float(unsigned int index);
|
||||
int scr_get_pointer_type(unsigned int index);
|
||||
int scr_get_type(unsigned int index);
|
||||
const char* scr_get_type_name(unsigned int index);
|
||||
}
|
||||
|
@ -5,15 +5,15 @@
|
||||
|
||||
#include "game/scripting/functions.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "component/console.hpp"
|
||||
#include "component/command.hpp"
|
||||
#include "component/console.hpp"
|
||||
#include "component/notifies.hpp"
|
||||
|
||||
#include "script_error.hpp"
|
||||
#include "script_extension.hpp"
|
||||
#include "script_loading.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace gsc
|
||||
@ -39,9 +39,60 @@ namespace gsc
|
||||
|
||||
utils::hook::detour scr_register_function_hook;
|
||||
|
||||
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 = utils::hook::invoke<unsigned int>(0x1403318B0, p_name, type);
|
||||
const auto result = game::Scr_GetFunction(p_name, type);
|
||||
|
||||
for (const auto& [name, func] : functions)
|
||||
{
|
||||
@ -122,27 +173,36 @@ 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())
|
||||
{
|
||||
console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data());
|
||||
location = utils::string::va("function \"%s\" in file \"%s\"", function.value().first.data(), function.value().second.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("\tat unknown location %p\n", pos);
|
||||
location = utils::string::va("unknown location %p", 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(int mark_pos)
|
||||
void vm_error_stub(unsigned __int64 mark_pos)
|
||||
{
|
||||
if (!dvars::com_developer_script->current.enabled && !force_error_print)
|
||||
{
|
||||
utils::hook::invoke<void>(0x1404B6790, mark_pos);
|
||||
game::LargeLocalResetToMark(mark_pos);
|
||||
return;
|
||||
}
|
||||
|
||||
console::warn("******* script runtime error ********\n");
|
||||
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x148806768);
|
||||
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(SELECT_VALUE(0x14A19DE68, 0x148806768));
|
||||
|
||||
const std::string error = gsc_error_msg.has_value() ? std::format(": {}", gsc_error_msg.value()) : std::string();
|
||||
|
||||
@ -168,7 +228,7 @@ namespace gsc
|
||||
|
||||
print_callstack();
|
||||
console::warn("************************************\n");
|
||||
utils::hook::invoke<void>(0x1404B6790, mark_pos);
|
||||
game::LargeLocalResetToMark(mark_pos);
|
||||
}
|
||||
|
||||
void scr_register_function_stub(void* func, int type, unsigned int name)
|
||||
@ -219,6 +279,46 @@ 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());
|
||||
}
|
||||
|
||||
const char* get_code_pos(const int index)
|
||||
{
|
||||
if (static_cast<unsigned int>(index) >= game::scr_VmPub->outparamcount)
|
||||
{
|
||||
scr_error("Scr_GetCodePos: index is out of range");
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto* value = &game::scr_VmPub->top[-index];
|
||||
|
||||
if (value->type != game::VAR_FUNCTION)
|
||||
{
|
||||
scr_error("Scr_GetCodePos requires a function as parameter");
|
||||
return "";
|
||||
}
|
||||
|
||||
return value->u.codePosValue;
|
||||
}
|
||||
}
|
||||
|
||||
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 scr_error(const char* error)
|
||||
@ -252,33 +352,40 @@ namespace gsc
|
||||
override_function("print", &scr_print);
|
||||
override_function("println", &scr_print_ln);
|
||||
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115BC, 0x1403EDAEC), 0x1000); // Scr_RegisterFunction
|
||||
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115C2 + 4, 0x1403EDAF2 + 4), RVA(&func_table)); // Scr_RegisterFunction
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x14031F097 + 3, 0x1403FB7F7 + 3), RVA(&func_table)); // VM_Execute_0
|
||||
utils::hook::inject(SELECT_VALUE(0x140311964 + 3, 0x1403EDEE4 + 3), &func_table); // Scr_BeginLoadScripts
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::nop(0x1403FB7F7 + 5, 2);
|
||||
utils::hook::call(0x1403FB7F7, vm_call_builtin_function);
|
||||
|
||||
utils::hook::call(0x1403FCAB0, vm_error_stub); // LargeLocalResetToMark
|
||||
|
||||
utils::hook::call(0x1403EDF1F, scr_get_function_stub);
|
||||
|
||||
override_function("assert", &assert_cmd);
|
||||
override_function("assertex", &assert_ex_cmd);
|
||||
override_function("assertmsg", &assert_msg_cmd);
|
||||
|
||||
add_function("executecommand", []
|
||||
override_function("isdedicatedserver", &scr_cmd_is_dedicated_server);
|
||||
|
||||
add_function("replacefunc", []
|
||||
{
|
||||
if (scr_get_type(0) != game::VAR_FUNCTION || scr_get_type(1) != game::VAR_FUNCTION)
|
||||
{
|
||||
throw gsc_error("Parameter must be a function");
|
||||
}
|
||||
|
||||
notifies::set_gsc_hook(get_code_pos(0), get_code_pos(1));
|
||||
});
|
||||
|
||||
add_function("executecommand", []() -> void
|
||||
{
|
||||
const auto* cmd = game::Scr_GetString(0);
|
||||
command::execute(cmd);
|
||||
});
|
||||
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115BC, 0x1403EDAEC), 0x1000); // Scr_RegisterFunction
|
||||
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403115C2 + 4, 0x1403EDAF2 + 4), RVA(&func_table)); // Scr_RegisterFunction
|
||||
// utils::hook::set<std::uint32_t>(SELECT_VALUE(0x14031F097 + 3, 0x1403FB7F7 + 3), RVA(&func_table)); // VM_Execute_0
|
||||
utils::hook::inject(SELECT_VALUE(0x140311964 + 3, 0x1403EDEE4 + 3), &func_table); // Scr_BeginLoadScripts
|
||||
|
||||
utils::hook::nop(SELECT_VALUE(0x14031F097 + 5, 0x1403FB7F7 + 5), 2);
|
||||
utils::hook::call(SELECT_VALUE(0x14031F097, 0x1403FB7F7), vm_call_builtin_function);
|
||||
|
||||
utils::hook::call(SELECT_VALUE(0x14032034F, 0x1403FCAB0), vm_error_stub); // LargeLocalResetToMark
|
||||
|
||||
utils::hook::call(SELECT_VALUE(0x14031199F, 0x1403EDF1F), scr_get_function_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,9 +1,36 @@
|
||||
#pragma once
|
||||
#include <xsk/gsc/engine/s1_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 scr_error(const char* error);
|
||||
void override_function(const std::string& name, game::BuiltinFunction func);
|
||||
void add_function(const std::string& name, game::BuiltinFunction function);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "component/console.hpp"
|
||||
#include "component/scripting.hpp"
|
||||
|
||||
#include "script_extension.hpp"
|
||||
#include "script_loading.hpp"
|
||||
|
||||
namespace gsc
|
||||
@ -32,6 +33,7 @@ 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)
|
||||
@ -115,6 +117,12 @@ 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)
|
||||
@ -181,13 +189,10 @@ namespace gsc
|
||||
}
|
||||
}
|
||||
|
||||
void load_scripts(const std::filesystem::path& root_dir)
|
||||
|
||||
void load_scripts_from_folder(const std::filesystem::path& root_dir, const std::filesystem::path& script_dir)
|
||||
{
|
||||
const std::filesystem::path script_dir = root_dir / "scripts";
|
||||
if (!utils::io::directory_exists(script_dir.generic_string()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
console::info("Scanning directory '%s' for custom GSC scripts...\n", script_dir.generic_string().data());
|
||||
|
||||
const auto scripts = utils::io::list_files(script_dir.generic_string());
|
||||
for (const auto& script : scripts)
|
||||
@ -205,6 +210,44 @@ 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))
|
||||
@ -224,8 +267,6 @@ namespace gsc
|
||||
return;
|
||||
}
|
||||
|
||||
clear();
|
||||
|
||||
for (const auto& path : filesystem::get_search_paths())
|
||||
{
|
||||
load_scripts(path);
|
||||
@ -259,7 +300,38 @@ namespace gsc
|
||||
utils::hook::invoke<void>(0x1403380D0);
|
||||
}
|
||||
|
||||
void scr_load_level_stub()
|
||||
int g_scr_set_level_script_stub(game::ScriptFunctions* functions)
|
||||
{
|
||||
const auto result = utils::hook::invoke<int>(0x140262F60, functions);
|
||||
|
||||
for (const auto& path : filesystem::get_search_paths())
|
||||
{
|
||||
load_scripts(path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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>(0x140257720);
|
||||
|
||||
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()
|
||||
{
|
||||
utils::hook::invoke<void>(0x140325B90);
|
||||
|
||||
@ -358,28 +430,32 @@ namespace gsc
|
||||
utils::hook::call(SELECT_VALUE(0x1403309E9, 0x1403309E9), scr_begin_load_scripts_stub); // GScr_LoadScripts
|
||||
utils::hook::call(SELECT_VALUE(0x14023DA84, 0x140330B9C), scr_end_load_scripts_stub); // GScr_LoadScripts
|
||||
|
||||
dvars::com_developer = game::Dvar_RegisterInt("developer", 0, 0, 2, game::DVAR_FLAG_NONE, "Enable development options");
|
||||
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
|
||||
// ProcessScript
|
||||
utils::hook::call(SELECT_VALUE(0x14031AB47, 0x1403F7317), find_script);
|
||||
utils::hook::call(SELECT_VALUE(0x14031AB57, 0x1403F7327), db_is_x_asset_default);
|
||||
|
||||
dvars::com_developer = game::Dvar_RegisterInt("developer", 0, 0, 2, game::DVAR_FLAG_NONE);
|
||||
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE);
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
utils::hook::call(0x1402632A5, g_scr_set_level_script_stub);
|
||||
|
||||
utils::hook::call(0x140226931, scr_load_level_singleplayer_stub);
|
||||
}
|
||||
else
|
||||
{
|
||||
// GScr_LoadScripts
|
||||
utils::hook::call(0x140330B97, gscr_post_load_scripts_stub);
|
||||
|
||||
// Exec script handles
|
||||
utils::hook::call(0x1402F71AE, g_load_structs_stub);
|
||||
utils::hook::call(0x1402F71C7, scr_load_level_multiplayer_stub);
|
||||
}
|
||||
|
||||
// ProcessScript
|
||||
utils::hook::call(0x1403F7317, find_script);
|
||||
utils::hook::call(0x1403F7327, db_is_x_asset_default);
|
||||
|
||||
// GScr_LoadScripts
|
||||
utils::hook::call(0x140330B97, gscr_post_load_scripts_stub);
|
||||
|
||||
// Exec script handles
|
||||
utils::hook::call(0x1402F71AE, g_load_structs_stub);
|
||||
utils::hook::call(0x1402F71C7, scr_load_level_stub);
|
||||
|
||||
scripting::on_shutdown([](int free_scripts)
|
||||
scripting::on_shutdown([](const int clear_scripts) -> void
|
||||
{
|
||||
if (free_scripts)
|
||||
if (clear_scripts)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ namespace logger
|
||||
|
||||
com_error_hook.create(game::Com_Error, com_error_stub);
|
||||
|
||||
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED, "Print dev stuff");
|
||||
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -289,11 +289,11 @@ namespace map_rotation
|
||||
|
||||
scheduler::once([]
|
||||
{
|
||||
sv_map_rotation = game::Dvar_RegisterString("sv_mapRotation", "", game::DVAR_FLAG_NONE, "");
|
||||
sv_map_rotation_current = game::Dvar_RegisterString("sv_mapRotationCurrent", "", game::DVAR_FLAG_NONE, "");
|
||||
sv_map_rotation = game::Dvar_RegisterString("sv_mapRotation", "", game::DVAR_FLAG_NONE);
|
||||
sv_map_rotation_current = game::Dvar_RegisterString("sv_mapRotationCurrent", "", game::DVAR_FLAG_NONE);
|
||||
}, scheduler::pipeline::main);
|
||||
|
||||
sv_random_map_rotation = game::Dvar_RegisterBool("sv_randomMapRotation", false, game::DVAR_FLAG_NONE, "Randomize map rotation");
|
||||
sv_random_map_rotation = game::Dvar_RegisterBool("sv_randomMapRotation", false, game::DVAR_FLAG_NONE);
|
||||
|
||||
command::add("map_rotate", &perform_map_rotation);
|
||||
|
||||
|
@ -179,7 +179,7 @@ namespace network
|
||||
game::dvar_t* register_netport_stub(const char* dvarName, int value, int min, int max, unsigned int flags,
|
||||
const char* description)
|
||||
{
|
||||
auto dvar = game::Dvar_RegisterInt("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED, "Network port");
|
||||
auto dvar = game::Dvar_RegisterInt("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED);
|
||||
|
||||
// read net_port from command line
|
||||
command::read_startup_variable("net_port");
|
||||
|
@ -28,8 +28,6 @@ namespace notifies
|
||||
char empty_function[2] = {0x32, 0x34}; // CHECK_CLEAR_PARAMS, END
|
||||
const char* target_function = nullptr;
|
||||
|
||||
|
||||
|
||||
void client_command_stub(const int client_num)
|
||||
{
|
||||
if (game::mp::g_entities[client_num].client == nullptr)
|
||||
@ -137,7 +135,7 @@ namespace notifies
|
||||
a.lea(eax, dword_ptr(r15, -0x17));
|
||||
a.mov(dword_ptr(rbp, 0x68), r15d);
|
||||
|
||||
a.jmp(0x1403FA143);
|
||||
a.jmp(SELECT_VALUE(0x14031D9E3, 0x1403FA143));
|
||||
|
||||
a.bind(replace);
|
||||
|
||||
@ -185,22 +183,22 @@ namespace notifies
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
utils::hook::jump(SELECT_VALUE(0x14031D9D4, 0x1403FA134), utils::hook::assemble(vm_execute_stub), true);
|
||||
|
||||
scripting::on_shutdown([](const int clear_scripts) -> void
|
||||
{
|
||||
if (clear_scripts)
|
||||
{
|
||||
vm_execute_hooks.clear();
|
||||
}
|
||||
});
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::call(0x14043A9AD, client_command_stub);
|
||||
|
||||
utils::hook::jump(0x1403FA134, utils::hook::assemble(vm_execute_stub), true);
|
||||
|
||||
scripting::on_shutdown([](bool free_scripts)
|
||||
{
|
||||
if (free_scripts)
|
||||
{
|
||||
vm_execute_hooks.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "party.hpp"
|
||||
#include "console.hpp"
|
||||
@ -33,6 +34,29 @@ namespace party
|
||||
|
||||
int sv_maxclients;
|
||||
|
||||
utils::info_string get_info()
|
||||
{
|
||||
utils::info_string info;
|
||||
|
||||
info.set("gamename", "S1");
|
||||
info.set("hostname", dvars::get_dvar_string("sv_hostname"));
|
||||
info.set("gametype", dvars::get_dvar_string("g_gametype"));
|
||||
info.set("sv_motd", dvars::get_dvar_string("sv_motd"));
|
||||
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
|
||||
info.set("mapname", dvars::get_dvar_string("mapname"));
|
||||
info.set("isPrivate", dvars::get_dvar_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::Dvar_FindVar("sv_maxclients")->current.integer));
|
||||
info.set("protocol", std::to_string(PROTOCOL));
|
||||
info.set("shortversion", SHORTVERSION);
|
||||
info.set("playmode", utils::string::va("%i", game::Com_GetCurrentCoDPlayMode()));
|
||||
info.set("sv_running", std::to_string(dvars::get_dvar_bool("sv_running")));
|
||||
info.set("dedicated", std::to_string(dvars::get_dvar_bool("dedicated")));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void perform_game_initialization()
|
||||
{
|
||||
command::execute("onlinegame 1", true);
|
||||
@ -79,30 +103,7 @@ namespace party
|
||||
|
||||
// CL_ConnectFromParty
|
||||
char session_info[0x100] = {};
|
||||
reinterpret_cast<void(*)(int, char*, const game::netadr_s*, const char*, const char*)>(0x140209360)(
|
||||
0, session_info, &target, mapname.data(), gametype.data());
|
||||
}
|
||||
|
||||
std::string get_dvar_string(const std::string& dvar)
|
||||
{
|
||||
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
|
||||
if (dvar_value && dvar_value->current.string)
|
||||
{
|
||||
return {dvar_value->current.string};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool get_dvar_bool(const std::string& dvar)
|
||||
{
|
||||
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
|
||||
if (dvar_value && dvar_value->current.enabled)
|
||||
{
|
||||
return dvar_value->current.enabled;
|
||||
}
|
||||
|
||||
return false;
|
||||
reinterpret_cast<void(*)(int, char*, const game::netadr_s*, const char*, const char*)>(0x140209360)(0, session_info, &target, mapname.data(), gametype.data());
|
||||
}
|
||||
|
||||
void didyouknow_stub(const char* dvar_name, const char* string)
|
||||
@ -461,8 +462,7 @@ namespace party
|
||||
|
||||
scheduler::once([]()
|
||||
{
|
||||
game::Dvar_RegisterString("sv_sayName", "console", game::DvarFlags::DVAR_FLAG_NONE,
|
||||
"The name to pose as for 'say' commands");
|
||||
game::Dvar_RegisterString("sv_sayName", "console", game::DVAR_FLAG_NONE);
|
||||
}, scheduler::pipeline::main);
|
||||
|
||||
command::add("tell", [](const command::params& params)
|
||||
@ -529,27 +529,55 @@ namespace party
|
||||
|
||||
network::on("getInfo", [](const game::netadr_s& target, const std::string& data)
|
||||
{
|
||||
utils::info_string info{};
|
||||
utils::info_string info = get_info();
|
||||
info.set("challenge", data);
|
||||
info.set("gamename", "S1");
|
||||
info.set("hostname", get_dvar_string("sv_hostname"));
|
||||
info.set("gametype", get_dvar_string("g_gametype"));
|
||||
info.set("sv_motd", get_dvar_string("sv_motd"));
|
||||
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
|
||||
info.set("mapname", get_dvar_string("mapname"));
|
||||
info.set("isPrivate", get_dvar_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_numclients));
|
||||
info.set("protocol", std::to_string(PROTOCOL));
|
||||
info.set("playmode", utils::string::va("%i", game::Com_GetCurrentCoDPlayMode()));
|
||||
info.set("sv_running", std::to_string(get_dvar_bool("sv_running")));
|
||||
info.set("dedicated", std::to_string(get_dvar_bool("dedicated")));
|
||||
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 < 5)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!self || !self->client)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (game::SV_BotIsBot(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto score = game::G_GetClientScore(i);
|
||||
const auto ping = game::SV_GetClientPing(i);
|
||||
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;
|
||||
|
@ -58,28 +58,28 @@ namespace patches
|
||||
if (game::environment::is_mp())
|
||||
{
|
||||
// Make name save
|
||||
game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name.");
|
||||
game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED);
|
||||
|
||||
// Disable data validation error popup
|
||||
game::Dvar_RegisterInt("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE, "");
|
||||
game::Dvar_RegisterInt("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE);
|
||||
}
|
||||
|
||||
return com_register_dvars_hook.invoke<void>();
|
||||
}
|
||||
|
||||
game::dvar_t* register_com_maxfps_stub(const char* name, int /*value*/, int /*min*/, int /*max*/, const unsigned int /*flags*/, const char* description)
|
||||
game::dvar_t* register_com_maxfps_stub(const char* name, int /*value*/, int /*min*/, int /*max*/, const unsigned int /*flags*/)
|
||||
{
|
||||
return game::Dvar_RegisterInt(name, 0, 0, 1000, game::DVAR_FLAG_SAVED, description);
|
||||
return game::Dvar_RegisterInt(name, 0, 0, 1000, game::DVAR_FLAG_SAVED);
|
||||
}
|
||||
|
||||
game::dvar_t* register_cg_fov_stub(const char* name, float value, float min, float /*max*/, const unsigned int /*flags*/, const char* description)
|
||||
game::dvar_t* register_cg_fov_stub(const char* name, float value, float min, float /*max*/, const unsigned int /*flags*/)
|
||||
{
|
||||
return game::Dvar_RegisterFloat(name, value, min, 160, game::DVAR_FLAG_SAVED, description);
|
||||
return game::Dvar_RegisterFloat(name, value, min, 160, game::DVAR_FLAG_SAVED);
|
||||
}
|
||||
|
||||
game::dvar_t* register_fovscale_stub(const char* name, float /*value*/, float /*min*/, float /*max*/, unsigned int /*flags*/, const char* desc)
|
||||
game::dvar_t* register_fovscale_stub(const char* name, float /*value*/, float /*min*/, float /*max*/, unsigned int /*flags*/)
|
||||
{
|
||||
return game::Dvar_RegisterFloat(name, 1.0f, 0.2f, 5.0f, game::DVAR_FLAG_SAVED, desc);
|
||||
return game::Dvar_RegisterFloat(name, 1.0f, 0.2f, 5.0f, game::DVAR_FLAG_SAVED);
|
||||
}
|
||||
|
||||
int dvar_command_patch() // game makes this return an int and compares with eax instead of al -_-
|
||||
@ -252,7 +252,7 @@ namespace patches
|
||||
utils::hook::call(0x14026E63B, bsp_sys_error_stub);
|
||||
|
||||
// client side aim assist dvar
|
||||
dvars::aimassist_enabled = game::Dvar_RegisterBool("aimassist_enabled", true, game::DVAR_FLAG_SAVED, "Enables aim assist for controllers");
|
||||
dvars::aimassist_enabled = game::Dvar_RegisterBool("aimassist_enabled", true, game::DVAR_FLAG_SAVED);
|
||||
utils::hook::call(0x140003609, aim_assist_add_to_target_list);
|
||||
|
||||
// isProfanity
|
||||
@ -304,7 +304,7 @@ namespace patches
|
||||
dvars::override::register_int("cl_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // Seems unused
|
||||
dvars::override::register_int("sv_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // 60 - 0 - 1800
|
||||
|
||||
game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED, "");
|
||||
game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED);
|
||||
|
||||
// Prevent clients from ending the game as non host by sending 'end_game' lui notification
|
||||
cmd_lui_notify_server_hook.create(0x1402E9390, cmd_lui_notify_server_stub);
|
||||
|
@ -31,7 +31,7 @@ namespace ranked
|
||||
dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE);
|
||||
|
||||
// Some dvar used in gsc
|
||||
game::Dvar_RegisterBool("force_ranking", true, game::DVAR_FLAG_WRITE, "Force ranking");
|
||||
game::Dvar_RegisterBool("force_ranking", true, game::DVAR_FLAG_WRITE);
|
||||
}
|
||||
|
||||
// Always run bots, even if xblive_privatematch is 0
|
||||
|
@ -128,8 +128,7 @@ namespace rcon
|
||||
|
||||
scheduler::once([]()
|
||||
{
|
||||
game::Dvar_RegisterString("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE,
|
||||
"The password for remote console");
|
||||
game::Dvar_RegisterString("rcon_password", "", game::DVAR_FLAG_NONE);
|
||||
}, scheduler::pipeline::main);
|
||||
|
||||
command::add("status", []()
|
||||
|
@ -62,7 +62,7 @@ namespace renderer
|
||||
return;
|
||||
}
|
||||
|
||||
dvars::r_fullbright = game::Dvar_RegisterInt("r_fullbright", 0, 0, 3, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting");
|
||||
dvars::r_fullbright = game::Dvar_RegisterInt("r_fullbright", 0, 0, 3, game::DVAR_FLAG_SAVED);
|
||||
|
||||
r_init_draw_method_hook.create(SELECT_VALUE(0x14046C150, 0x140588B00), &r_init_draw_method_stub);
|
||||
r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x1404A5330, 0x1405C3AE0), &r_update_front_end_dvar_options_stub);
|
||||
|
@ -49,9 +49,9 @@ namespace scripting
|
||||
}
|
||||
}
|
||||
|
||||
void g_shutdown_game_stub(const int free_scripts)
|
||||
void g_shutdown_game_stub(const int clear_scripts)
|
||||
{
|
||||
if (free_scripts)
|
||||
if (clear_scripts)
|
||||
{
|
||||
script_function_table_sort.clear();
|
||||
script_function_table.clear();
|
||||
@ -61,10 +61,10 @@ namespace scripting
|
||||
|
||||
for (const auto& callback : shutdown_callbacks)
|
||||
{
|
||||
callback(free_scripts);
|
||||
callback(clear_scripts);
|
||||
}
|
||||
|
||||
return g_shutdown_game_hook.invoke<void>(free_scripts);
|
||||
return g_shutdown_game_hook.invoke<void>(clear_scripts);
|
||||
}
|
||||
|
||||
void process_script_stub(const char* filename)
|
||||
@ -182,10 +182,6 @@ namespace scripting
|
||||
scr_set_thread_position_hook.create(SELECT_VALUE(0x1403115E0, 0x1403EDB10), &scr_set_thread_position_stub);
|
||||
process_script_hook.create(SELECT_VALUE(0x14031AB30, 0x1403F7300), &process_script_stub);
|
||||
sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, &sl_get_canonical_string_stub);
|
||||
|
||||
scheduler::loop([]
|
||||
{
|
||||
}, scheduler::pipeline::server);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
@ -33,6 +36,25 @@ namespace slowmotion
|
||||
game::SV_SetConfigstring(10, utils::string::va("%i %i %g %g", *game::mp::gameTime, duration, start, end));
|
||||
game::Com_SetSlowMotion(start, end, duration);
|
||||
}
|
||||
|
||||
void set_code_time_scale(float timescale)
|
||||
{
|
||||
assert(timescale > 0.0f);
|
||||
*game::com_codeTimeScale = timescale;
|
||||
}
|
||||
|
||||
void com_timescale_f(const command::params& params)
|
||||
{
|
||||
if (params.size() != 2)
|
||||
{
|
||||
console::info("timescale <rate>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto timescale = static_cast<float>(std::atof(params.get(1)));
|
||||
console::info("timescale set to %f\n", timescale);
|
||||
set_code_time_scale(timescale);
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@ -40,6 +62,13 @@ namespace slowmotion
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
command::add("timescale", com_timescale_f);
|
||||
|
||||
if (!game::environment::is_dedi())
|
||||
{
|
||||
return;
|
||||
|
@ -75,9 +75,9 @@ namespace stats
|
||||
else
|
||||
{
|
||||
// unlock all
|
||||
cg_unlock_all_items = game::Dvar_RegisterBool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED, "Unlock items that are level-locked by the player's stats.");
|
||||
cg_unlock_all_loot = game::Dvar_RegisterBool("cg_unlockall_loot", false, game::DVAR_FLAG_SAVED, "Unlock supply drop loot.");
|
||||
game::Dvar_RegisterBool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED, "Unlock extra class slots.");
|
||||
cg_unlock_all_items = game::Dvar_RegisterBool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED);
|
||||
cg_unlock_all_loot = game::Dvar_RegisterBool("cg_unlockall_loot", false, game::DVAR_FLAG_SAVED);
|
||||
game::Dvar_RegisterBool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED);
|
||||
|
||||
is_item_locked_hook1.create(0x1403BD790, is_item_locked_stub1); // LiveStorage_IsItemUnlockedFromTable_LocalClient
|
||||
is_item_locked_hook2.create(0x1403BD290, is_item_locked_stub2); // LiveStorage_IsItemUnlockedFromTable
|
||||
|
@ -82,8 +82,8 @@ namespace virtuallobby
|
||||
return;
|
||||
}
|
||||
|
||||
virtualLobby_fov = game::Dvar_RegisterFloat("virtualLobby_fov", 40.0f, 1.0f, 170.0f, game::DVAR_FLAG_SAVED, "Field of view for the virtual lobby");
|
||||
virtualLobby_fovscale = game::Dvar_RegisterFloat("virtualLobby_fovScale", 1.0f, 0.0f, 2.0f, game::DVAR_FLAG_SAVED, "Field of view scaled for the virtual lobby");
|
||||
virtualLobby_fov = game::Dvar_RegisterFloat("virtualLobby_fov", 40.0f, 1.0f, 170.0f, game::DVAR_FLAG_SAVED);
|
||||
virtualLobby_fovscale = game::Dvar_RegisterFloat("virtualLobby_fovScale", 1.0f, 0.0f, 2.0f, game::DVAR_FLAG_SAVED);
|
||||
|
||||
utils::hook::nop(0x1401D5AFB, 16);
|
||||
utils::hook::jump(0x1401D5AFB, get_fov_stub, true);
|
||||
|
@ -43,6 +43,28 @@ namespace dvars
|
||||
game::dvar_t* com_developer = nullptr;
|
||||
game::dvar_t* com_developer_script = nullptr;
|
||||
|
||||
std::string get_dvar_string(const std::string& dvar)
|
||||
{
|
||||
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
|
||||
if (dvar_value && dvar_value->current.string)
|
||||
{
|
||||
return { dvar_value->current.string };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool get_dvar_bool(const std::string& dvar)
|
||||
{
|
||||
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
|
||||
if (dvar_value && dvar_value->current.enabled)
|
||||
{
|
||||
return dvar_value->current.enabled;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain)
|
||||
{
|
||||
if (domain.vector.min == -FLT_MAX)
|
||||
@ -51,22 +73,16 @@ namespace dvars
|
||||
{
|
||||
return utils::string::va("Domain is any %iD vector", components);
|
||||
}
|
||||
else
|
||||
{
|
||||
return utils::string::va("Domain is any %iD vector with components %g or smaller", components,
|
||||
domain.vector.max);
|
||||
}
|
||||
|
||||
return utils::string::va("Domain is any %iD vector with components %g or smaller", components, domain.vector.max);
|
||||
}
|
||||
else if (domain.vector.max == FLT_MAX)
|
||||
|
||||
if (domain.vector.max == FLT_MAX)
|
||||
{
|
||||
return utils::string::va("Domain is any %iD vector with components %g or bigger", components,
|
||||
domain.vector.min);
|
||||
}
|
||||
else
|
||||
{
|
||||
return utils::string::va("Domain is any %iD vector with components from %g to %g", components,
|
||||
domain.vector.min, domain.vector.max);
|
||||
return utils::string::va("Domain is any %iD vector with components %g or bigger", components, domain.vector.min);
|
||||
}
|
||||
|
||||
return utils::string::va("Domain is any %iD vector with components from %g to %g", components, domain.vector.min, domain.vector.max);
|
||||
}
|
||||
|
||||
std::string dvar_get_domain(const game::dvar_type type, const game::dvar_limits& domain)
|
||||
|
@ -42,6 +42,9 @@ namespace dvars
|
||||
extern game::dvar_t* com_developer;
|
||||
extern game::dvar_t* com_developer_script;
|
||||
|
||||
std::string get_dvar_string(const std::string& dvar);
|
||||
bool get_dvar_bool(const std::string& dvar);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -60,6 +60,13 @@ namespace game
|
||||
VAR_TOTAL_COUNT = 0x1B,
|
||||
};
|
||||
|
||||
struct ScriptFunctions
|
||||
{
|
||||
int maxSize;
|
||||
int count;
|
||||
int* address;
|
||||
};
|
||||
|
||||
struct VariableStackBuffer
|
||||
{
|
||||
const char* pos;
|
||||
@ -214,7 +221,6 @@ namespace game
|
||||
unsigned __int16 childVariableBucket[65536];
|
||||
ChildVariableValue childVariableValue[384000];
|
||||
};
|
||||
// *
|
||||
|
||||
enum Sys_Folder
|
||||
{
|
||||
@ -1483,11 +1489,17 @@ namespace game
|
||||
char __pad2[8];
|
||||
int nextSnapshotTime; // 269024
|
||||
char __pad3[544];
|
||||
LiveClientDropType liveDropRequest; //269572
|
||||
LiveClientDropType liveDropRequest; // 269572
|
||||
char __pad4[24];
|
||||
TestClientType testClient; // 269600
|
||||
char __pad5[391700];
|
||||
}; // size = 661304
|
||||
|
||||
static_assert(offsetof(client_t, gentity) == 0x41AB0);
|
||||
static_assert(offsetof(client_t, nextSnapshotTime) == 0x41AE0);
|
||||
static_assert(offsetof(client_t, liveDropRequest) == 0x41D04);
|
||||
static_assert(offsetof(client_t, testClient) == 0x41D20);
|
||||
static_assert(sizeof(client_t) == 0xA1738);
|
||||
}
|
||||
|
||||
namespace sp
|
||||
|
@ -12,7 +12,7 @@ namespace game
|
||||
WEAK symbol<void(unsigned int id)> AddRefToObject{0, 0x1403F1F10};
|
||||
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0, 0x1403F2270};
|
||||
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x140317340, 0x1403F3A50};
|
||||
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0, 0x1403F3940};
|
||||
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x140317230, 0x1403F3940};
|
||||
|
||||
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x140001730};
|
||||
|
||||
@ -70,22 +70,14 @@ namespace game
|
||||
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x1403737D0, 0x1404C2E40};
|
||||
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x140374E10, 0x1404C47B0};
|
||||
|
||||
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned int flags, const char* description)>
|
||||
Dvar_RegisterBool{0x140371850, 0x1404C0BE0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char** valueList, int defaultIndex, unsigned int flags,
|
||||
const char* description)> Dvar_RegisterEnum{0x140371B30, 0x1404C0EC0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float value, float min, float max, unsigned int flags,
|
||||
const char* description)> Dvar_RegisterFloat{0x140371C20, 0x1404C0FB0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, int value, int min, int max, unsigned int flags, const char* desc)>
|
||||
Dvar_RegisterInt{0x140371CF0, 0x1404C1080};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char* value, unsigned int flags, const char* description)>
|
||||
Dvar_RegisterString{0x140372050, 0x1404C1450};
|
||||
WEAK symbol<dvar_t* (const char* dvarName, float x, float y, float min, float max,
|
||||
unsigned int flags, const char* description)> Dvar_RegisterVec2{0x140372120, 0x1404C1520};
|
||||
WEAK symbol<dvar_t* (const char* dvarName, float x, float y, float z, float min, float max,
|
||||
unsigned int flags, const char* description)> Dvar_RegisterVec3{0x140372230, 0x1404C1600};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float z, float w, float min, float max,
|
||||
unsigned int flags, const char* description)> Dvar_RegisterVec4{0x140372430, 0x1404C1800};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned int flags)> Dvar_RegisterBool{0x140371850, 0x1404C0BE0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char** valueList, int defaultIndex, unsigned int flags)> Dvar_RegisterEnum{0x140371B30, 0x1404C0EC0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float value, float min, float max, unsigned int flags)> Dvar_RegisterFloat{0x140371C20, 0x1404C0FB0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, int value, int min, int max, unsigned int flags)> Dvar_RegisterInt{0x140371CF0, 0x1404C1080};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char* value, unsigned int flags)> Dvar_RegisterString{0x140372050, 0x1404C1450};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float min, float max, unsigned int flags)> Dvar_RegisterVec2{0x140372120, 0x1404C1520};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float z, float min, float max, unsigned int flags)> Dvar_RegisterVec3{0x140372230, 0x1404C1600};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float z, float w, float min, float max, unsigned int flags)> Dvar_RegisterVec4{0x140372430, 0x1404C1800};
|
||||
|
||||
WEAK symbol<DWOnlineStatus()> dwGetLogOnStatus{0, 0x14053CCB0};
|
||||
|
||||
@ -96,7 +88,7 @@ namespace game
|
||||
WEAK symbol<void (const char *path, const char *dir)> FS_AddLocalizedGameDirectory{0x14035F5C0, 0x1404AD170};
|
||||
|
||||
WEAK symbol<void()> GScr_LoadConsts{0x140283970, 0x1403479C0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1403165D0, 0x1403F2DC0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1403166D0, 0x1403F2DC0};
|
||||
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x1403166D0, 0x1403F2CC0};
|
||||
WEAK symbol<scr_string_t(unsigned int parentId, unsigned int id)> GetVariableName{0x1403170E0, 0x1403F37F0};
|
||||
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x14031AAD0, 0x1403F72A0};
|
||||
@ -152,21 +144,23 @@ namespace game
|
||||
WEAK symbol<void(int value)> Scr_AddInt{0x0, 0x1403F7B30};
|
||||
WEAK symbol<int(unsigned int index)> Scr_GetInt{0x14031C1F0, 0x1403F88D0};
|
||||
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0x14031C090, 0x1403F8820};
|
||||
WEAK symbol<void(unsigned int index, float* vectorValue)> Scr_GetVector{0x14031C7E0, 0x1403F8EC0};
|
||||
WEAK symbol<unsigned int()> Scr_GetNumParam{0x14031C2A0, 0x1403F8980};
|
||||
WEAK symbol<void()> Scr_ClearOutParams{0x14031B7C0, 0x1403F8040};
|
||||
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x14031A0D0, 0x1403F68A0};
|
||||
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x14026B620, 0x140339450};
|
||||
WEAK symbol<void(void* ent, unsigned int stringValue, unsigned int paramcount)> Scr_Notify{0x0, 0x140339120};
|
||||
WEAK symbol<void(unsigned int id, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyId{0x14031CB80, 0x1403F92D0};
|
||||
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x0, 0x1403F4500};
|
||||
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x140317EA0, 0x1403F4500};
|
||||
|
||||
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x0, 0x1403F8120};
|
||||
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x0, 0x1403EE250};
|
||||
WEAK symbol<unsigned int(const char* script, unsigned int name)> Scr_GetFunctionHandle{0x0, 0x1403EE0D0};
|
||||
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x14031B8C0, 0x1403F8120};
|
||||
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x140311CD0, 0x1403EE250};
|
||||
WEAK symbol<unsigned int(const char* filename, unsigned int name)> Scr_GetFunctionHandle{0x140311B50, 0x1403EE0D0};
|
||||
WEAK symbol<unsigned int(void* func, int type, unsigned int name)> Scr_RegisterFunction{0x1403115B0, 0x1403EDAE0};
|
||||
WEAK symbol<unsigned int(const char** pName, int* type)> Scr_GetFunction{0x140263E90, 0x1403318B0};
|
||||
|
||||
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x0, 0x1403F9E40};
|
||||
WEAK symbol<void()> Scr_ErrorInternal{0x0, 0x1403F80A0};
|
||||
WEAK symbol<void()> Scr_ErrorInternal{0x14031B840, 0x1403F80A0};
|
||||
|
||||
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x140314850, 0x1403F0F10};
|
||||
WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x140314AF0, 0x1403F11C0};
|
||||
@ -202,8 +196,7 @@ namespace game
|
||||
|
||||
WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0, 0x14043FCA0};
|
||||
|
||||
WEAK symbol<void(char* path, int pathSize, Sys_Folder folder, const char* filename, const char* ext)>
|
||||
Sys_BuildAbsPath{0x14037BBE0, 0x1404CC7E0};
|
||||
WEAK symbol<void(char* path, int pathSize, Sys_Folder folder, const char* filename, const char* ext)> Sys_BuildAbsPath{0x14037BBE0, 0x1404CC7E0};
|
||||
WEAK symbol<HANDLE(int folder, const char* baseFileName)> Sys_CreateFile{0x14037BCA0, 0x1404CC8A0};
|
||||
WEAK symbol<void(const char* error, ...)> Sys_Error{0x14038C770, 0x1404D6260};
|
||||
WEAK symbol<bool(const char* path)> Sys_FileExists{0x14038C810, 0x1404D6310};
|
||||
@ -224,6 +217,8 @@ namespace game
|
||||
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, int source)> PMem_AllocFromSource_NoDebug{0x1403775F0, 0x1404C7BA0};
|
||||
WEAK symbol<void*(unsigned int size)> Hunk_AllocateTempMemoryHighInternal{0x140369D60, 0x1404B68B0};
|
||||
|
||||
WEAK symbol<void(unsigned __int64 markPos)> LargeLocalResetToMark{0x140369C40, 0x1404B6790};
|
||||
|
||||
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14059C5C0, 0x1406FD930};
|
||||
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x14059CD00, 0x1406FE070};
|
||||
|
||||
@ -246,6 +241,8 @@ namespace game
|
||||
WEAK symbol<jmp_buf> g_script_error{0x14A1917B0, 0x1487FA0C0};
|
||||
WEAK symbol<scr_classStruct_t> g_classMap{0x14080A840, 0x1409BE1B0};
|
||||
|
||||
WEAK symbol<float> com_codeTimeScale{0x0, 0x147B754EC};
|
||||
|
||||
WEAK symbol<scrVarGlob_t> scr_VarGlob{0x149B1D680, 0x148185F80};
|
||||
WEAK symbol<scrVmPub_t> scr_VmPub{0x14A1938C0, 0x1487FC1C0};
|
||||
WEAK symbol<function_stack_t> scr_function_stack{0x14A19DE40, 0x148806740};
|
||||
|
25
src/common/utils/properties.cpp
Normal file
25
src/common/utils/properties.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#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;
|
||||
}
|
||||
}
|
7
src/common/utils/properties.hpp
Normal file
7
src/common/utils/properties.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
|
||||
namespace utils::properties
|
||||
{
|
||||
std::filesystem::path get_appdata_path();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user