forked from alterware/iw6-mod
Compare commits
15
Commits
demo_system
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
534e9c096b | ||
|
|
479aef7082 | ||
|
|
09b121a763 | ||
|
|
365def1513 | ||
|
|
bf125f5671 | ||
|
|
5c7d1af309 | ||
|
|
157092c56d | ||
|
|
0885049ed0 | ||
|
|
b1e467d511 | ||
|
|
f1ec94f50b | ||
|
|
aa182f6edc | ||
|
|
38d8148c82 | ||
|
|
ea6da588c3 | ||
|
|
3dddcb6f0a | ||
|
|
45907301ee
|
Vendored
+1
-1
Submodule deps/GSL updated: f1a494cfd2...272463043e
Vendored
+1
-1
Submodule deps/WinToast updated: 821c4818ad...a78ce469b4
Vendored
+1
-1
Submodule deps/asmjit updated: bfa0bf690c...cfc9f813cc
Vendored
+1
-1
Submodule deps/gsc-tool updated: b8e30e6334...9ff3b871ef
Vendored
+1
-1
Submodule deps/libtomcrypt updated: 7e863d2142...c900951dab
Vendored
+1
-1
Submodule deps/libtommath updated: 8314bde5e5...5809141a3a
Vendored
+1
-1
Submodule deps/minhook updated: f5485b8454...c1a7c3843b
Vendored
+1
-1
Submodule deps/rapidjson updated: 6089180ecb...d621dc9e9c
Vendored
+1
-1
Submodule deps/zlib updated: 9f0f2d4f9f...ef24c4c750
@@ -0,0 +1,35 @@
|
||||
#include <std_include.hpp>
|
||||
#include <loader/component_loader.hpp>
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/memory.hpp>
|
||||
|
||||
namespace asset_restrict
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void reallocate_asset_pool(game::XAssetType type, const int size)
|
||||
{
|
||||
const auto asset_size = game::DB_GetXAssetTypeSize(type);
|
||||
const auto new_size = size * game::g_poolSize[type];
|
||||
|
||||
const auto new_allocation_size = static_cast<std::size_t>(new_size * asset_size);
|
||||
|
||||
const game::XAssetHeader pool_entry = { .data = utils::memory::allocate(new_allocation_size) };
|
||||
game::DB_XAssetPool[type] = pool_entry.data;
|
||||
game::g_poolSize[type] = new_size;
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
reallocate_asset_pool(game::ASSET_TYPE_XANIMPARTS, 2);
|
||||
reallocate_asset_pool(game::ASSET_TYPE_ATTACHMENT, 2);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(asset_restrict::component)
|
||||
@@ -8,11 +8,13 @@
|
||||
#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() / "iw6-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()
|
||||
|
||||
@@ -232,7 +232,10 @@ namespace command
|
||||
|
||||
void execute(std::string command, const bool sync)
|
||||
{
|
||||
command += "\n";
|
||||
if (!command.ends_with('\n'))
|
||||
{
|
||||
command += "\n";
|
||||
}
|
||||
|
||||
if (sync)
|
||||
{
|
||||
@@ -553,12 +556,12 @@ namespace command
|
||||
{
|
||||
if (!dvars::sv_cheats->current.enabled)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
||||
return;
|
||||
}
|
||||
|
||||
game::mp::g_entities[client_num].flags ^= game::FL_GODMODE;
|
||||
game::SV_GameSendServerCommand(client_num, 1,
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
|
||||
utils::string::va("f \"godmode %s\"",
|
||||
game::mp::g_entities[client_num].flags & game::FL_GODMODE
|
||||
? "^2on"
|
||||
@@ -569,12 +572,12 @@ namespace command
|
||||
{
|
||||
if (!dvars::sv_cheats->current.enabled)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
||||
return;
|
||||
}
|
||||
|
||||
game::mp::g_entities[client_num].flags ^= game::FL_NOTARGET;
|
||||
game::SV_GameSendServerCommand(client_num, 1,
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
|
||||
utils::string::va("f \"notarget %s\"",
|
||||
game::mp::g_entities[client_num].flags & game::FL_NOTARGET
|
||||
? "^2on"
|
||||
@@ -585,12 +588,12 @@ namespace command
|
||||
{
|
||||
if (!dvars::sv_cheats->current.enabled)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
||||
return;
|
||||
}
|
||||
|
||||
game::mp::g_entities[client_num].client->flags ^= 1;
|
||||
game::SV_GameSendServerCommand(client_num, 1,
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
|
||||
utils::string::va("f \"noclip %s\"",
|
||||
game::mp::g_entities[client_num].client->flags & 1
|
||||
? "^2on"
|
||||
@@ -601,12 +604,12 @@ namespace command
|
||||
{
|
||||
if (!dvars::sv_cheats->current.enabled)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
||||
return;
|
||||
}
|
||||
|
||||
game::mp::g_entities[client_num].client->flags ^= 2;
|
||||
game::SV_GameSendServerCommand(client_num, 1,
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
|
||||
utils::string::va("f \"ufo %s\"",
|
||||
game::mp::g_entities[client_num].client->flags & 2
|
||||
? "^2on"
|
||||
@@ -617,13 +620,13 @@ namespace command
|
||||
{
|
||||
if (!dvars::sv_cheats->current.enabled)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.size() < 4)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1,
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
|
||||
"f \"You did not specify the correct number of coordinates\"");
|
||||
return;
|
||||
}
|
||||
@@ -637,13 +640,13 @@ namespace command
|
||||
{
|
||||
if (!dvars::sv_cheats->current.enabled)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.size() < 4)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1,
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
|
||||
"f \"You did not specify the correct number of coordinates\"");
|
||||
return;
|
||||
}
|
||||
@@ -657,13 +660,13 @@ namespace command
|
||||
{
|
||||
if (!dvars::sv_cheats->current.enabled)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.size() < 2)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"You did not specify a weapon name\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -680,13 +683,13 @@ namespace command
|
||||
{
|
||||
if (!dvars::sv_cheats->current.enabled)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"Cheats are not enabled on this server\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.size() < 2)
|
||||
{
|
||||
game::SV_GameSendServerCommand(client_num, 1, "f \"You did not specify a weapon name\"");
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,28 +25,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("iw6-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 client_count = 0;
|
||||
auto bot_count = 0;
|
||||
|
||||
for (auto i = 0; i < sv_maxclients->current.integer; i++)
|
||||
for (auto i = 0; i < sv_maxclients->current.integer; ++i)
|
||||
{
|
||||
auto* client = &game::mp::svs->clients[i];
|
||||
auto* self = &game::mp::g_entities[i];
|
||||
|
||||
if (client->header.state > game::CS_FREE && self && self->client)
|
||||
{
|
||||
client_count++;
|
||||
++client_count;
|
||||
if (game::SV_BotIsBot(i))
|
||||
{
|
||||
++bot_count;
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace // demo class
|
||||
[[nodiscard]] bool rewind_demo(std::optional<std::uint32_t> msec);
|
||||
[[nodiscard]] bool start(std::filesystem::path& path, bool repeat);
|
||||
[[nodiscard]] std::optional<std::reference_wrapper<std::filesystem::path>> restart();
|
||||
template <typename Func, typename... Args> auto use_predicted_data(Func func, Args&&... args);
|
||||
template <typename Func, typename... Args> auto handle_fast_forward(Func func, Args&&... args);
|
||||
template <typename Func, typename... Args> [[nodiscard]] auto use_predicted_data(Func func, Args&&... args);
|
||||
template <typename Func, typename... Args> [[nodiscard]] auto handle_fast_forward(Func func, Args&&... args);
|
||||
|
||||
void parse_demo_wrapper();
|
||||
void stop();
|
||||
@@ -86,7 +86,7 @@ namespace // demo class
|
||||
return false;
|
||||
}
|
||||
|
||||
// trigger demo end-of-file which will repeat the demo
|
||||
// set demo end-of-file which will repeat the demo
|
||||
repeat_ = true;
|
||||
file_.seekg(0, std::ios::end);
|
||||
|
||||
@@ -157,11 +157,12 @@ namespace // demo class
|
||||
if (!parse_demo(file_, buffer_, post_map_header_file_offset_, pred_data_, pred_data_index_, gs_))
|
||||
{
|
||||
if (is_demo_repeating() && *game::mp::connstate != game::CA_DISCONNECTED)
|
||||
{ // don't repeat demo if it was stopped manually
|
||||
{
|
||||
repeat();
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't repeat demo if it was stopped manually
|
||||
stop();
|
||||
}
|
||||
}
|
||||
@@ -356,7 +357,7 @@ namespace // hooks
|
||||
{
|
||||
if (const auto delta_time = snap_svr_time - server_time; delta_time >= 1000)
|
||||
{
|
||||
// skip timeout between first and second snapshots (for user demos)
|
||||
// skip timeout between first and second snapshot (for user demos)
|
||||
fast_forward_demo(delta_time);
|
||||
skip_timeout = true;
|
||||
}
|
||||
@@ -671,7 +672,7 @@ namespace // command execution
|
||||
bool repeat{};
|
||||
};
|
||||
|
||||
auto parsed_arguments = [¶ms]() -> std::optional<args_t>
|
||||
const auto parsed_arguments = [¶ms]() -> std::optional<args_t>
|
||||
{
|
||||
if (params.size() == 3)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace // demo class
|
||||
|
||||
void shutdown_watcher();
|
||||
void process_network_data(auto old_connstate, auto new_connstate, std::span<const std::uint8_t> network_data);
|
||||
void process_predicted_data();
|
||||
void process_predicted_data(auto old_connstate, auto new_connstate);
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool is_auto_recording() const;
|
||||
@@ -326,7 +326,8 @@ namespace // demo class
|
||||
// store gamestate for user file
|
||||
write_network_data(crit_data_, network_data);
|
||||
|
||||
// reserve memory (low size if auto recording is enabled, because then the demo writes almost directly to disk)
|
||||
// reserve memory; use smaller buffer size when auto recording is enabled,
|
||||
// since the data is written to disk almost right away.
|
||||
const auto reserve_size = (demo_autorecord && demo_autorecord->current.enabled) ? 8192 : 1024 * 1024;
|
||||
auto_buffer_.reserve_memory(reserve_size);
|
||||
|
||||
@@ -419,38 +420,18 @@ namespace // demo class
|
||||
}
|
||||
}
|
||||
|
||||
void demo_recording_t::process_predicted_data()
|
||||
void demo_recording_t::process_predicted_data(auto old_connstate, auto new_connstate)
|
||||
{
|
||||
if (old_connstate < game::CA_ACTIVE || new_connstate < game::CA_ACTIVE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
process_helo_pilot_turret_fire();
|
||||
|
||||
if (is_recording())
|
||||
{
|
||||
// only one predicted data packet is needed per (1000 / sv_fps) msec
|
||||
// the best way to limit the amount of predicted data is to attempt to predict the next playerState_t::commandTime
|
||||
// because during demo playback the playerState_t::commandTime determines which predicted data is used
|
||||
|
||||
const auto& cl = *game::mp::cl;
|
||||
const auto cmd_number = static_cast<std::size_t>(cl.cmdNumber);
|
||||
const auto pred_svr_time_delta = cl.cmds[cmd_number % std::size(cl.cmds)].serverTime
|
||||
- cl.cmds[(cmd_number - 1) % std::size(cl.cmds)].serverTime;
|
||||
|
||||
const auto pred_svr_time = cl.cgamePredictedDataServerTime;
|
||||
const auto svr_time_delta = std::clamp(cl.snap.serverTime - cl.oldSnapServerTime, 50, 100);
|
||||
|
||||
const auto cur_cmd_time = cl.snap.ps.commandTime;
|
||||
const auto next_cmd_time = cur_cmd_time + svr_time_delta;
|
||||
const auto threshold = (3 * pred_svr_time_delta) / 2;
|
||||
|
||||
if (pred_svr_time + threshold >= next_cmd_time && next_cmd_time + threshold >= pred_svr_time)
|
||||
{
|
||||
times_.predicted_data_time = cl.serverTime;
|
||||
write_predicted_data(user_file_);
|
||||
}
|
||||
else if (cl.serverTime >= times_.predicted_data_time + svr_time_delta - 5)
|
||||
{
|
||||
times_.predicted_data_time = cl.serverTime;
|
||||
write_predicted_data(user_file_);
|
||||
}
|
||||
write_predicted_data(user_file_);
|
||||
}
|
||||
|
||||
if (is_auto_recording())
|
||||
@@ -494,21 +475,11 @@ namespace // hooks
|
||||
return;
|
||||
}
|
||||
|
||||
demo.process_predicted_data(old_connstate, new_connstate);
|
||||
demo.process_network_data(old_connstate, new_connstate,
|
||||
std::span<const std::uint8_t>(reinterpret_cast<const std::uint8_t*>(msg.data), msg.cursize));
|
||||
}
|
||||
|
||||
void capture_predicted_data(game::msg_t& msg, std::int32_t value, std::uint32_t bits)
|
||||
{
|
||||
if (!demo_playback::playing())
|
||||
{
|
||||
demo.process_predicted_data();
|
||||
}
|
||||
|
||||
// call MSG_WriteBits that this hook replaces
|
||||
game::MSG_WriteBits(&msg, value, bits);
|
||||
}
|
||||
|
||||
void request_non_delta_data(game::msg_t& msg, std::int32_t value, std::uint32_t bits)
|
||||
{
|
||||
if (!demo_playback::playing() && demo.non_delta_data_requested())
|
||||
@@ -715,9 +686,6 @@ namespace demo_recording
|
||||
// capture incoming packets
|
||||
CL_ParseServerMessage_hook.create(game::CL_ParseServerMessage, capture_data);
|
||||
|
||||
// capture client predicted data (in CL_WritePacket)
|
||||
utils::hook::call(0x1402C21B9, capture_predicted_data);
|
||||
|
||||
// request the server for no delta data (in CL_WritePacket)
|
||||
utils::hook::call(0x1402C2026, request_non_delta_data);
|
||||
|
||||
|
||||
@@ -83,15 +83,18 @@ namespace // checked client num class
|
||||
return std::isdigit(c);
|
||||
});
|
||||
|
||||
auto value = std::numeric_limits<std::size_t>::max();
|
||||
const auto ec = std::from_chars(pid.data(), pid.data() + pid.size(), value).ec;
|
||||
|
||||
if (all_digits && ec == std::errc{})
|
||||
if (all_digits)
|
||||
{
|
||||
const auto client_num = check_client_num(value);
|
||||
if (client_num)
|
||||
auto value = std::numeric_limits<std::size_t>::max();
|
||||
const auto ec = std::from_chars(pid.data(), pid.data() + pid.size(), value).ec;
|
||||
|
||||
if (ec == std::errc{})
|
||||
{
|
||||
return client_num;
|
||||
const auto client_num = check_client_num(value);
|
||||
if (client_num)
|
||||
{
|
||||
return client_num;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,9 +377,8 @@ namespace // server demos class
|
||||
// write general, mod and map headers to the buffer once
|
||||
if (!client.is_buffer_active())
|
||||
{
|
||||
// reserve memory (low size if auto recording is enabled, because then the demo writes almost directly to disk)
|
||||
const auto reserve_size = (sv_demo_autorecord && sv_demo_autorecord->current.enabled) ? 8192 : 512 * 1024;
|
||||
client.buffer.reserve_memory(reserve_size);
|
||||
// reserve memory up front to minimize further (re)allocations
|
||||
client.buffer.reserve_memory(1024 * 1024);
|
||||
|
||||
sv_write_general_header(client.buffer, game::mp::svs->clients[client_num.value()].name);
|
||||
write_mod_header(client.buffer);
|
||||
@@ -431,6 +433,8 @@ namespace // server demos class
|
||||
|
||||
auto& client = get_client(client_num);
|
||||
|
||||
// keep buffering data even when recording a demo so subsequent demos for the same client
|
||||
// can be recorded using the entire buffer; the entire buffer is required for demos to be valid
|
||||
if (client.is_buffer_active())
|
||||
{
|
||||
write_data_internal(client.buffer);
|
||||
@@ -633,8 +637,8 @@ namespace demo_sv_recording
|
||||
// capture outgoing packets to client
|
||||
SV_Netchan_Transmit_hook.create(game::SV_Netchan_Transmit, sv_capture_data);
|
||||
|
||||
// execute server demo code based on this value; if it's is enabled mid-match,
|
||||
// then the demos recorded during that match (if any) are likely corrupt!
|
||||
// execute server demo code based on this value
|
||||
// if this was enabled after a player joined the server, then that match cannot be recorded for that player
|
||||
sv_demos = game::Dvar_RegisterBool(
|
||||
"sv_demos", false, game::DVAR_FLAG_NONE, "Enable server demos");
|
||||
|
||||
|
||||
@@ -13,9 +13,13 @@ namespace demo_timescale
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour Com_TimeScaleMsec_hook;
|
||||
utils::hook::detour LUI_Layout_hook;
|
||||
utils::hook::detour Com_GetTimescaleForSnd_hook;
|
||||
|
||||
game::dvar_t* demo_timescale;
|
||||
|
||||
std::int32_t latest_delta_time;
|
||||
|
||||
void generate_pattern(std::array<std::uint8_t, 1000>& pattern, std::size_t ones_count, std::size_t zeros_count)
|
||||
{
|
||||
assert(ones_count + zeros_count == pattern.size());
|
||||
@@ -53,7 +57,7 @@ namespace demo_timescale
|
||||
generate_pattern(pattern, ones_count, zeros_count);
|
||||
}
|
||||
|
||||
std::int32_t Com_TimeScaleMsec_stub(std::int32_t msec)
|
||||
std::int32_t get_delta_time_internal(std::int32_t msec)
|
||||
{
|
||||
if (!demo_playback::playing() || !demo_timescale || demo_timescale->current.value == 1.0f)
|
||||
{
|
||||
@@ -116,6 +120,33 @@ namespace demo_timescale
|
||||
// advance (1ms) or pause (0ms) based on the pattern
|
||||
return pattern[pattern_index++ % pattern.size()];
|
||||
}
|
||||
|
||||
std::int32_t get_delta_time(std::int32_t msec)
|
||||
{
|
||||
latest_delta_time = get_delta_time_internal(msec);
|
||||
|
||||
return latest_delta_time;
|
||||
}
|
||||
|
||||
void set_delta_time_ui_elements(const std::int32_t client_num, const char* root_name, std::int32_t delta_time, void* lua_vm)
|
||||
{
|
||||
if (demo_playback::playing())
|
||||
{
|
||||
delta_time = latest_delta_time;
|
||||
}
|
||||
|
||||
LUI_Layout_hook.invoke<void>(client_num, root_name, delta_time, lua_vm);
|
||||
}
|
||||
|
||||
float get_timescale_for_sound()
|
||||
{
|
||||
if (!demo_playback::playing() || !demo_timescale || demo_timescale->current.value == 1.0f)
|
||||
{
|
||||
return Com_GetTimescaleForSnd_hook.invoke<float>();
|
||||
}
|
||||
|
||||
return demo_timescale->current.value;
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@@ -129,7 +160,15 @@ namespace demo_timescale
|
||||
}
|
||||
|
||||
// add timescale support for demo playback
|
||||
Com_TimeScaleMsec_hook.create(game::Com_TimeScaleMsec, Com_TimeScaleMsec_stub);
|
||||
Com_TimeScaleMsec_hook.create(game::Com_TimeScaleMsec, get_delta_time);
|
||||
|
||||
// add timescale support for lua ui elements during demo playback
|
||||
LUI_Layout_hook.create(game::LUI_Layout, set_delta_time_ui_elements);
|
||||
|
||||
// add timescale support for sounds during demo playback
|
||||
Com_GetTimescaleForSnd_hook.create(game::Com_GetTimescaleForSnd, get_timescale_for_sound);
|
||||
|
||||
// allow the user to control the timescale during demo playback
|
||||
demo_timescale = game::Dvar_RegisterFloat(
|
||||
"demotimescale", 1.0f, 0.0f, 1000.0f, game::DVAR_FLAG_NONE, "Set playback speed for demos");
|
||||
}
|
||||
|
||||
@@ -467,35 +467,35 @@ namespace demo_utils
|
||||
write_network_data_(output, network_data);
|
||||
}
|
||||
|
||||
void write_predicted_player_data_(auto& output, const game::ClientArchiveData& cad,
|
||||
const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id)
|
||||
void write_predicted_player_data_(auto& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
write_id_and_size(output, 50, demo_data_id::predicted_data);
|
||||
output.write(reinterpret_cast<const char*>(&id), 1);
|
||||
output.write(reinterpret_cast<const char*>(&cad_index), 1);
|
||||
|
||||
output.write(reinterpret_cast<const char*>(&cad.serverTime), 4);
|
||||
output.write(reinterpret_cast<const char*>(&cad.origin[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&cad.velocity[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&cad.bobCycle), 4);
|
||||
output.write(reinterpret_cast<const char*>(&cad.movementDir), 4);
|
||||
output.write(reinterpret_cast<const char*>(&viewangles[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&ps.commandTime), 4);
|
||||
output.write(reinterpret_cast<const char*>(&ps.origin[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&ps.velocity[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&ps.bobCycle), 4);
|
||||
output.write(reinterpret_cast<const char*>(&ps.movementDir), 4);
|
||||
output.write(reinterpret_cast<const char*>(&ps.viewangles[0]), 12);
|
||||
}
|
||||
|
||||
void write_predicted_player_data(buffer_t& output, const game::ClientArchiveData& cad,
|
||||
const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id)
|
||||
void write_predicted_player_data(buffer_t& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
write_predicted_player_data_(output, cad, viewangles, cad_index, id);
|
||||
write_predicted_player_data_(output, ps, cad_index, id);
|
||||
}
|
||||
|
||||
void write_predicted_player_data(std::ofstream& output, const game::ClientArchiveData& cad,
|
||||
const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id)
|
||||
void write_predicted_player_data(std::ofstream& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
write_predicted_player_data_(output, cad, viewangles, cad_index, id);
|
||||
write_predicted_player_data_(output, ps, cad_index, id);
|
||||
}
|
||||
|
||||
void write_predicted_vehicle_data_(auto& output, const game::ClientArchiveData& cad,
|
||||
const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id)
|
||||
void write_predicted_vehicle_data_(auto& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
const auto size = (id != predicted_data_id::vehicle_gryphon) ? 58 : 82;
|
||||
|
||||
@@ -503,10 +503,10 @@ namespace demo_utils
|
||||
output.write(reinterpret_cast<const char*>(&id), 1);
|
||||
output.write(reinterpret_cast<const char*>(&cad_index), 1);
|
||||
|
||||
const auto& vehicle = cad.playerVehStateClientArchive;
|
||||
output.write(reinterpret_cast<const char*>(&cad.serverTime), 4);
|
||||
output.write(reinterpret_cast<const char*>(&cad.origin[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&viewangles[0]), 12);
|
||||
const auto& vehicle = ps.vehicleState;
|
||||
output.write(reinterpret_cast<const char*>(&ps.commandTime), 4);
|
||||
output.write(reinterpret_cast<const char*>(&ps.origin[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&ps.viewangles[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&vehicle.flags), 4);
|
||||
output.write(reinterpret_cast<const char*>(&vehicle.origin[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&vehicle.angles[0]), 12);
|
||||
@@ -520,16 +520,16 @@ namespace demo_utils
|
||||
assert(!vehicle.splineId);
|
||||
}
|
||||
|
||||
void write_predicted_vehicle_data(buffer_t& output, const game::ClientArchiveData& cad,
|
||||
const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id)
|
||||
void write_predicted_vehicle_data(buffer_t& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
write_predicted_vehicle_data_(output, cad, viewangles, cad_index, id);
|
||||
write_predicted_vehicle_data_(output, ps, cad_index, id);
|
||||
}
|
||||
|
||||
void write_predicted_vehicle_data(std::ofstream& output, const game::ClientArchiveData& cad,
|
||||
const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id)
|
||||
void write_predicted_vehicle_data(std::ofstream& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
write_predicted_vehicle_data_(output, cad, viewangles, cad_index, id);
|
||||
write_predicted_vehicle_data_(output, ps, cad_index, id);
|
||||
}
|
||||
|
||||
void write_helo_pilot_turret_fire_(auto& output, std::uint8_t fire_count)
|
||||
@@ -675,23 +675,23 @@ namespace demo_utils
|
||||
|
||||
void write_predicted_data_(auto& output)
|
||||
{
|
||||
const auto& ps = game::mp::cg->ps;
|
||||
const auto& viewangles = game::mp::cg->refdefViewAngles;
|
||||
const auto cad_index = static_cast<std::uint8_t>(game::mp::cl->clientArchiveIndex - 1);
|
||||
const auto cad_array = std::span(game::mp::cl->clientArchive);
|
||||
auto& cl = *game::mp::cl;
|
||||
auto& ps = cl.snap.ps;
|
||||
const auto& cg = *game::mp::cg;
|
||||
|
||||
if (const auto& cad = cad_array[cad_index]; cad.serverTime)
|
||||
ps.viewangles[0] = cg.refdefViewAngles[0];
|
||||
ps.viewangles[1] = cg.refdefViewAngles[1];
|
||||
ps.viewangles[2] = cg.refdefViewAngles[2];
|
||||
|
||||
const auto vehicle_in_use = (ps.vehicleState.entity && ps.vehicleState.entity != 2047);
|
||||
if (!vehicle_in_use)
|
||||
{
|
||||
const auto vehicle_in_use = (ps.vehicleState.entity && ps.vehicleState.entity != 2047);
|
||||
if (!vehicle_in_use)
|
||||
{
|
||||
write_predicted_player_data(output, cad, viewangles, cad_index, predicted_data_id::player);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto data_type = get_predicted_vehicle_type(ps);
|
||||
write_predicted_vehicle_data(output, cad, viewangles, cad_index, data_type);
|
||||
}
|
||||
write_predicted_player_data(output, ps, static_cast<std::uint8_t>(cl.clientArchiveIndex), predicted_data_id::player);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto data_type = get_predicted_vehicle_type(ps);
|
||||
write_predicted_vehicle_data(output, ps, static_cast<std::uint8_t>(cl.clientArchiveIndex), data_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,9 +711,10 @@ namespace demo_utils
|
||||
const auto datetime = get_datetime();
|
||||
|
||||
const auto protocol = std::to_string(PROTOCOL);
|
||||
const auto* build_num_ptr = game::LiveStorage_FetchFFotD();
|
||||
const auto build_num = (build_num_ptr)
|
||||
? std::string_view(build_num_ptr)
|
||||
const auto* build_num_bptr = game::LiveStorage_FetchFFotD();
|
||||
const auto* build_num_eptr = build_num_bptr ? std::find(build_num_bptr, build_num_bptr + 128, '\0') : nullptr;
|
||||
const auto build_num = (build_num_bptr && std::distance(build_num_bptr, build_num_eptr) > 0 && std::distance(build_num_bptr, build_num_eptr) < 128)
|
||||
? std::string_view(build_num_bptr, build_num_eptr)
|
||||
: "unknown_build_number";
|
||||
|
||||
const auto svr_name = utils::string::strip(svr_name_sv, true);
|
||||
@@ -771,17 +772,17 @@ namespace demo_utils
|
||||
const auto success = game::CL_GetClientNameColorize(0, game::mp::cg->clientNum, buffer.data(), static_cast<int>(buffer.size()));
|
||||
if (success)
|
||||
{
|
||||
player_name = std::string_view(buffer.data());
|
||||
player_name = std::string_view(buffer.begin(), std::find(buffer.begin(), buffer.end(), '\0'));
|
||||
}
|
||||
|
||||
if (player_name.empty())
|
||||
if (player_name.empty() || player_name.size() == buffer.size())
|
||||
{
|
||||
player_name = get_dvar_string("name", true);
|
||||
}
|
||||
|
||||
if (player_name.empty())
|
||||
{
|
||||
player_name = "unknown_player_name";
|
||||
if (player_name.empty() || player_name.size() >= 64)
|
||||
{
|
||||
player_name = "unknown_player_name";
|
||||
}
|
||||
}
|
||||
|
||||
write_general_header_internal(output, description, host_name, player_name);
|
||||
@@ -842,77 +843,12 @@ namespace demo_utils
|
||||
|
||||
namespace serialization_server
|
||||
{
|
||||
void sv_write_predicted_player_data_(auto& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
write_id_and_size(output, 50, demo_data_id::predicted_data);
|
||||
output.write(reinterpret_cast<const char*>(&id), 1);
|
||||
output.write(reinterpret_cast<const char*>(&cad_index), 1);
|
||||
|
||||
output.write(reinterpret_cast<const char*>(&ps.commandTime), 4);
|
||||
output.write(reinterpret_cast<const char*>(&ps.origin[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&ps.velocity[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&ps.bobCycle), 4);
|
||||
output.write(reinterpret_cast<const char*>(&ps.movementDir), 4);
|
||||
output.write(reinterpret_cast<const char*>(&ps.viewangles[0]), 12);
|
||||
}
|
||||
|
||||
void sv_write_predicted_player_data(buffer_t& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
sv_write_predicted_player_data_(output, ps, cad_index, id);
|
||||
}
|
||||
|
||||
void sv_write_predicted_player_data(std::ofstream& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
sv_write_predicted_player_data_(output, ps, cad_index, id);
|
||||
}
|
||||
|
||||
void sv_write_predicted_vehicle_data_(auto& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
const auto size = (id != predicted_data_id::vehicle_gryphon) ? 58 : 82;
|
||||
|
||||
write_id_and_size(output, size, demo_data_id::predicted_data);
|
||||
output.write(reinterpret_cast<const char*>(&id), 1);
|
||||
output.write(reinterpret_cast<const char*>(&cad_index), 1);
|
||||
|
||||
const auto& vehicle = ps.vehicleState;
|
||||
output.write(reinterpret_cast<const char*>(&ps.commandTime), 4);
|
||||
output.write(reinterpret_cast<const char*>(&ps.origin[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&ps.viewangles[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&vehicle.flags), 4);
|
||||
output.write(reinterpret_cast<const char*>(&vehicle.origin[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&vehicle.angles[0]), 12);
|
||||
|
||||
if (id == predicted_data_id::vehicle_gryphon)
|
||||
{
|
||||
output.write(reinterpret_cast<const char*>(&vehicle.velocity[0]), 12);
|
||||
output.write(reinterpret_cast<const char*>(&vehicle.angVelocity[0]), 12);
|
||||
}
|
||||
|
||||
assert(!vehicle.splineId);
|
||||
}
|
||||
|
||||
void sv_write_predicted_vehicle_data(buffer_t& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
sv_write_predicted_vehicle_data_(output, ps, cad_index, id);
|
||||
}
|
||||
|
||||
void sv_write_predicted_vehicle_data(std::ofstream& output, const game::mp::playerState_t& ps,
|
||||
std::uint8_t cad_index, predicted_data_id id)
|
||||
{
|
||||
sv_write_predicted_vehicle_data_(output, ps, cad_index, id);
|
||||
}
|
||||
|
||||
void sv_write_predicted_data_(auto& output, const game::mp::playerState_t& ps, std::size_t send_msg_count)
|
||||
{
|
||||
const auto vehicle_in_use = (ps.vehicleState.entity && ps.vehicleState.entity != 2047);
|
||||
if (!vehicle_in_use)
|
||||
{
|
||||
sv_write_predicted_player_data(output, ps, static_cast<std::uint8_t>(send_msg_count), predicted_data_id::player);
|
||||
write_predicted_player_data(output, ps, static_cast<std::uint8_t>(send_msg_count), predicted_data_id::player);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -926,7 +862,7 @@ namespace demo_utils
|
||||
}
|
||||
}
|
||||
|
||||
sv_write_predicted_vehicle_data(output, ps, static_cast<std::uint8_t>(send_msg_count), data_type);
|
||||
write_predicted_vehicle_data(output, ps, static_cast<std::uint8_t>(send_msg_count), data_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -949,7 +885,7 @@ namespace demo_utils
|
||||
? sv_hostname
|
||||
: "unknown_server_host_name";
|
||||
|
||||
if (player_name.empty())
|
||||
if (player_name.empty() || player_name.size() >= 64)
|
||||
{
|
||||
player_name = "unknown_player_name";
|
||||
}
|
||||
|
||||
@@ -143,15 +143,15 @@ namespace demo_utils
|
||||
void write_network_data(buffer_t& output, std::span<const std::uint8_t> network_data);
|
||||
void write_network_data(std::ofstream& output, std::span<const std::uint8_t> network_data);
|
||||
|
||||
//void write_predicted_player_data(buffer_t& output, const game::ClientArchiveData& cad,
|
||||
//const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id);
|
||||
//void write_predicted_player_data(std::ofstream& output, const game::ClientArchiveData& cad,
|
||||
//const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id);
|
||||
//void write_predicted_player_data(buffer_t& output, const game::mp::playerState_t& ps,
|
||||
//std::uint8_t cad_index, predicted_data_id id);
|
||||
//void write_predicted_player_data(std::ofstream& output, const game::mp::playerState_t& ps,
|
||||
//std::uint8_t cad_index, predicted_data_id id);
|
||||
|
||||
//void write_predicted_vehicle_data(buffer_t& output, const game::ClientArchiveData& cad,
|
||||
//const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id);
|
||||
//void write_predicted_vehicle_data(std::ofstream& output, const game::ClientArchiveData& cad,
|
||||
//const game::vec3_t& viewangles, std::uint8_t cad_index, predicted_data_id id);
|
||||
//void write_predicted_vehicle_data(buffer_t& output, const game::mp::playerState_t& ps,
|
||||
//std::uint8_t cad_index, predicted_data_id id);
|
||||
//void write_predicted_vehicle_data(std::ofstream& output, const game::mp::playerState_t& ps,
|
||||
//std::uint8_t cad_index, predicted_data_id id);
|
||||
|
||||
void write_helo_pilot_turret_fire(buffer_t& output, std::uint8_t fire_count);
|
||||
void write_helo_pilot_turret_fire(std::ofstream& output, std::uint8_t fire_count);
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace dvar_cheats
|
||||
const auto* dvar = game::Scr_GetString(0); // grab the original dvar again since it's never stored on stack
|
||||
const auto* command = utils::string::va("q %s \"%s\"", dvar, value);
|
||||
|
||||
game::SV_GameSendServerCommand(entity_num, 1, command);
|
||||
game::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command);
|
||||
}
|
||||
|
||||
const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "fastfiles.hpp"
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
#include "fastfiles.hpp"
|
||||
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/memory.hpp>
|
||||
@@ -22,7 +22,7 @@ namespace fastfiles
|
||||
void db_try_load_x_file_internal(const char* zone_name, const int zone_flags, const int is_base_map)
|
||||
{
|
||||
console::info("Loading fastfile %s\n", zone_name);
|
||||
return db_try_load_x_file_internal_hook.invoke<void>(zone_name, zone_flags, is_base_map);
|
||||
db_try_load_x_file_internal_hook.invoke<void>(zone_name, zone_flags, is_base_map);
|
||||
}
|
||||
|
||||
void dump_gsc_script(const std::string& name, game::XAssetHeader header)
|
||||
@@ -114,21 +114,9 @@ namespace fastfiles
|
||||
utils::hook::call(SELECT_VALUE(0x1402752DF, 0x140156350), p_mem_free_stub);
|
||||
utils::hook::call(SELECT_VALUE(0x140276004, 0x140324259), p_mem_free_stub);
|
||||
|
||||
command::add("loadzone", [](const command::params& params)
|
||||
{
|
||||
if (params.size() < 2)
|
||||
{
|
||||
console::info("usage: loadzone <zone>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
game::XZoneInfo info;
|
||||
info.name = params.get(1);
|
||||
info.allocFlags = 1;
|
||||
info.freeFlags = 0;
|
||||
|
||||
game::DB_LoadXAssets(&info, 1, game::DBSyncMode::DB_LOAD_SYNC);
|
||||
});
|
||||
// Allow loading of unsigned fastfiles
|
||||
utils::hook::set<uint8_t>(0x1402FBF23, 0xEB); // DB_LoadXFile
|
||||
utils::hook::nop(0x1402FC445, 2); // DB_SetFileLoadCompressor
|
||||
|
||||
command::add("materiallist", [](const command::params& params)
|
||||
{
|
||||
|
||||
@@ -86,6 +86,11 @@ namespace filesystem
|
||||
register_path("iw6");
|
||||
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");
|
||||
|
||||
@@ -102,9 +102,14 @@ namespace game_log
|
||||
g_log_printf("InitGame\n");
|
||||
});
|
||||
|
||||
scripting::on_shutdown([](int free_scripts)
|
||||
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
|
||||
{
|
||||
console::info("==== ShutdownGame (%d) ====\n", free_scripts);
|
||||
if (post_shutdown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
console::info("==== ShutdownGame (%d) ====\n", clear_scripts);
|
||||
|
||||
g_log_printf("ShutdownGame:\n");
|
||||
g_log_printf("------------------------------------------------------------\n");
|
||||
|
||||
@@ -113,7 +113,6 @@ namespace gsc
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsigned int scr_get_object(unsigned int index)
|
||||
@@ -284,28 +283,23 @@ namespace gsc
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
scr_emit_function_hook.create(SELECT_VALUE(0x1403D3350, 0x14042E150), &scr_emit_function_stub);
|
||||
|
||||
scr_emit_function_hook.create(0x14042E150, &scr_emit_function_stub);
|
||||
|
||||
utils::hook::call(0x14042E0E4, compile_error_stub); // LinkFile
|
||||
utils::hook::call(0x14042E138, compile_error_stub); // LinkFile
|
||||
utils::hook::call(0x14042E22B, find_variable_stub); // Scr_EmitFunction
|
||||
utils::hook::call(SELECT_VALUE(0x1403D32E4, 0x14042E0E4), compile_error_stub); // LinkFile
|
||||
utils::hook::call(SELECT_VALUE(0x1403D3338, 0x14042E138), compile_error_stub); // LinkFile
|
||||
utils::hook::call(SELECT_VALUE(0x1403D342A, 0x14042E22B), find_variable_stub); // Scr_EmitFunction
|
||||
|
||||
// Restore basic error messages to scr functions
|
||||
utils::hook::jump(0x140438ED0, scr_get_object);
|
||||
utils::hook::jump(0x140438AD0, scr_get_const_string);
|
||||
utils::hook::jump(0x1404388B0, scr_get_const_istring);
|
||||
utils::hook::jump(0x1404393D0, scr_get_vector);
|
||||
utils::hook::jump(0x140438E10, scr_get_int);
|
||||
utils::hook::jump(0x140438D60, scr_get_float);
|
||||
utils::hook::jump(game::Scr_GetObject, scr_get_object);
|
||||
utils::hook::jump(game::Scr_GetConstString, scr_get_const_string);
|
||||
utils::hook::jump(game::Scr_GetConstIString, scr_get_const_istring);
|
||||
utils::hook::jump(game::Scr_GetVector, scr_get_vector);
|
||||
utils::hook::jump(game::Scr_GetInt, scr_get_int);
|
||||
utils::hook::jump(game::Scr_GetFloat, scr_get_float);
|
||||
|
||||
utils::hook::jump(0x1404390B0, scr_get_pointer_type);
|
||||
utils::hook::jump(0x140439280, scr_get_type);
|
||||
utils::hook::jump(0x1404392F0, scr_get_type_name);
|
||||
utils::hook::jump(SELECT_VALUE(0x1403DE150, 0x1404390B0), scr_get_pointer_type);
|
||||
utils::hook::jump(SELECT_VALUE(0x1403DE320, 0x140439280), scr_get_type);
|
||||
utils::hook::jump(SELECT_VALUE(0x1403DE390, 0x1404392F0), scr_get_type_name);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
|
||||
@@ -37,9 +37,60 @@ namespace gsc
|
||||
bool force_error_print = false;
|
||||
std::optional<std::string> gsc_error_msg;
|
||||
|
||||
std::vector<devmap_entry> devmap_entries{};
|
||||
|
||||
std::optional<devmap_entry> get_devmap_entry(const std::uint8_t* codepos)
|
||||
{
|
||||
const auto itr = std::ranges::find_if(devmap_entries, [codepos](const devmap_entry& entry) -> bool
|
||||
{
|
||||
return codepos >= entry.bytecode && codepos < entry.bytecode + entry.size;
|
||||
});
|
||||
|
||||
if (itr != devmap_entries.end())
|
||||
{
|
||||
return *itr;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::uint16_t, std::uint16_t>> get_line_and_col_for_codepos(const std::uint8_t* codepos)
|
||||
{
|
||||
const auto entry = get_devmap_entry(codepos);
|
||||
|
||||
if (!entry.has_value())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::uint16_t, std::uint16_t>> best_line_info{};
|
||||
std::uint32_t best_codepos = 0;
|
||||
|
||||
assert(codepos >= entry->bytecode);
|
||||
const std::uint32_t codepos_offset = static_cast<std::uint32_t>(codepos - entry->bytecode);
|
||||
|
||||
for (const auto& instruction : entry->devmap)
|
||||
{
|
||||
if (instruction.codepos > codepos_offset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (best_line_info.has_value() && codepos_offset - instruction.codepos > codepos_offset - best_codepos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
best_line_info = { { instruction.line, instruction.col } };
|
||||
best_codepos = instruction.codepos;
|
||||
}
|
||||
|
||||
return best_line_info;
|
||||
}
|
||||
|
||||
unsigned int scr_get_function_stub(const char** p_name, int* type)
|
||||
{
|
||||
const auto result = utils::hook::invoke<unsigned int>(0x1403CD9F0, p_name, type);
|
||||
const auto result = game::Scr_GetFunction(p_name, type);
|
||||
|
||||
for (const auto& [name, func] : functions)
|
||||
{
|
||||
@@ -120,27 +171,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(const unsigned __int64 mark_pos)
|
||||
{
|
||||
if (!dvars::com_developer_script->current.enabled && !force_error_print)
|
||||
{
|
||||
utils::hook::invoke<void>(0x1404E4D00, mark_pos);
|
||||
game::LargeLocalResetToMark(mark_pos);
|
||||
return;
|
||||
}
|
||||
|
||||
console::warn("******* script runtime error ********\n");
|
||||
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x144D57840);
|
||||
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(SELECT_VALUE(0x1455BE740, 0x144D57840));
|
||||
|
||||
const std::string error = gsc_error_msg.has_value() ? std::format(": {}", gsc_error_msg.value()) : std::string();
|
||||
|
||||
@@ -166,7 +226,7 @@ namespace gsc
|
||||
|
||||
print_callstack();
|
||||
console::warn("************************************\n");
|
||||
utils::hook::invoke<void>(0x1404E4D00, mark_pos);
|
||||
game::LargeLocalResetToMark(mark_pos);
|
||||
}
|
||||
|
||||
void inc_in_param()
|
||||
@@ -228,6 +288,11 @@ 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)
|
||||
@@ -248,6 +313,22 @@ namespace gsc
|
||||
}
|
||||
}
|
||||
|
||||
void add_devmap_entry(std::uint8_t* codepos, std::size_t size, const std::string& name, xsk::gsc::buffer devmap_buf)
|
||||
{
|
||||
std::vector<dev_map_instruction> devmap{};
|
||||
const auto* devmap_ptr = reinterpret_cast<const dev_map*>(devmap_buf.data);
|
||||
|
||||
devmap.resize(devmap_ptr->num_instructions);
|
||||
std::memcpy(devmap.data(), devmap_ptr->instructions, sizeof(dev_map_instruction) * devmap_ptr->num_instructions);
|
||||
|
||||
devmap_entries.emplace_back(codepos, size, name, std::move(devmap));
|
||||
}
|
||||
|
||||
void clear_devmap()
|
||||
{
|
||||
devmap_entries.clear();
|
||||
}
|
||||
|
||||
void add_function(const std::string& name, game::BuiltinFunction function)
|
||||
{
|
||||
++function_id_start;
|
||||
@@ -271,27 +352,18 @@ namespace gsc
|
||||
utils::hook::set<game::BuiltinFunction>(SELECT_VALUE(0x14086F468, 0x1409E6CE8), scr_print);
|
||||
utils::hook::set<game::BuiltinFunction>(SELECT_VALUE(0x14086F480, 0x1409E6D00), scr_print_ln);
|
||||
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353C, 0x14042E33C), 0x1000); // Scr_RegisterFunction
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D353B + 1, 0x14042E33B + 1), 0x1000); // Scr_RegisterFunction
|
||||
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403D3542 + 4, 0x14042E342 + 4), RVA(&func_table)); // Scr_RegisterFunction
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0
|
||||
// utils::hook::set<std::uint32_t>(SELECT_VALUE(0x1403E0BDD + 3, 0x14043BBBE + 3), RVA(&func_table)); // VM_Execute_0
|
||||
utils::hook::inject(SELECT_VALUE(0x1403D38E4 + 3, 0x14042E734 + 3), &func_table); // Scr_BeginLoadScripts
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
utils::hook::nop(SELECT_VALUE(0x1403E0BDD + 5, 0x14043BBBE + 5), 2);
|
||||
utils::hook::call(SELECT_VALUE(0x1403E0BDD, 0x14043BBBE), vm_call_builtin_function);
|
||||
|
||||
utils::hook::nop(0x14043BBBE + 5, 2);
|
||||
utils::hook::call(0x14043BBBE, vm_call_builtin_function);
|
||||
utils::hook::call(SELECT_VALUE(0x1403D391F, 0x14042E76F), scr_get_function_stub);
|
||||
|
||||
utils::hook::call(0x14043CEB1, vm_error_stub);
|
||||
|
||||
utils::hook::call(0x14042E76F, scr_get_function_stub);
|
||||
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd);
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd);
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E20, assert_cmd);
|
||||
utils::hook::call(SELECT_VALUE(0x1403E1ED0, 0x14043CEB1), vm_error_stub);
|
||||
|
||||
add_function("replacefunc", []
|
||||
{
|
||||
@@ -309,10 +381,16 @@ namespace gsc
|
||||
command::execute(cmd);
|
||||
});
|
||||
|
||||
add_function("isdedicated", []
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
game::Scr_AddInt(game::environment::is_dedi());
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E38, assert_ex_cmd);
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E50, assert_msg_cmd);
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E6E20, assert_cmd);
|
||||
|
||||
utils::hook::set<game::BuiltinFunction>(0x1409E94D0, scr_cmd_is_dedicated_server);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,36 @@
|
||||
#pragma once
|
||||
#include <xsk/gsc/engine/iw6_pc.hpp>
|
||||
|
||||
namespace gsc
|
||||
{
|
||||
extern void* func_table[0x1000];
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct dev_map_instruction
|
||||
{
|
||||
std::uint32_t codepos;
|
||||
std::uint16_t line;
|
||||
std::uint16_t col;
|
||||
};
|
||||
|
||||
struct dev_map
|
||||
{
|
||||
std::uint32_t num_instructions;
|
||||
dev_map_instruction instructions[1];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct devmap_entry
|
||||
{
|
||||
const std::uint8_t* bytecode;
|
||||
std::size_t size;
|
||||
std::string script_name;
|
||||
std::vector<dev_map_instruction> devmap;
|
||||
};
|
||||
|
||||
void add_devmap_entry(std::uint8_t*, std::size_t, const std::string&, xsk::gsc::buffer);
|
||||
void clear_devmap();
|
||||
|
||||
void add_function(const std::string& name, game::BuiltinFunction function);
|
||||
|
||||
void scr_error(const char* error);
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/memory.hpp>
|
||||
#include <utils/compression.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/memory.hpp>
|
||||
|
||||
#include "component/filesystem.hpp"
|
||||
#include "component/console.hpp"
|
||||
#include "component/filesystem.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)
|
||||
@@ -114,6 +116,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)
|
||||
@@ -180,13 +188,9 @@ 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)
|
||||
@@ -204,6 +208,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))
|
||||
@@ -218,12 +260,22 @@ namespace gsc
|
||||
{
|
||||
utils::hook::invoke<void>(0x1403CCB10);
|
||||
|
||||
clear();
|
||||
for (const auto& path : filesystem::get_search_paths())
|
||||
{
|
||||
load_scripts(path);
|
||||
}
|
||||
}
|
||||
|
||||
int g_scr_load_script_and_label_stub(game::ScriptFunctions* functions)
|
||||
{
|
||||
const auto result = utils::hook::invoke<int>(0x140349790, functions);
|
||||
|
||||
for (const auto& path : filesystem::get_search_paths())
|
||||
{
|
||||
load_scripts(path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void db_get_raw_buffer_stub(const game::RawFile* rawfile, char* buf, const int size)
|
||||
@@ -250,7 +302,26 @@ namespace gsc
|
||||
utils::hook::invoke<void>(0x1403D2CA0);
|
||||
}
|
||||
|
||||
void scr_load_level_stub()
|
||||
void scr_load_level_singleplayer_stub()
|
||||
{
|
||||
for (auto& function_handle : main_handles)
|
||||
{
|
||||
console::info("Executing '%s::main'\n", function_handle.first.data());
|
||||
const auto thread = game::Scr_ExecThread(static_cast<int>(function_handle.second), 0);
|
||||
game::RemoveRefToObject(thread);
|
||||
}
|
||||
|
||||
utils::hook::invoke<void>(0x1403401B0);
|
||||
|
||||
for (auto& function_handle : init_handles)
|
||||
{
|
||||
console::info("Executing '%s::init'\n", function_handle.first.data());
|
||||
const auto thread = game::Scr_ExecThread(static_cast<int>(function_handle.second), 0);
|
||||
game::RemoveRefToObject(thread);
|
||||
}
|
||||
}
|
||||
|
||||
void scr_load_level_multiplayer_stub()
|
||||
{
|
||||
utils::hook::invoke<void>(0x1403CDC60);
|
||||
|
||||
@@ -345,31 +416,35 @@ namespace gsc
|
||||
utils::hook::call(SELECT_VALUE(0x14032D1E0, 0x1403CCED9), scr_begin_load_scripts_stub); // GScr_LoadScripts
|
||||
utils::hook::call(SELECT_VALUE(0x14032D345, 0x1403CD08D), scr_end_load_scripts_stub); // GScr_LoadScripts
|
||||
|
||||
// ProcessScript
|
||||
utils::hook::call(SELECT_VALUE(0x1403DC887, 0x1404378D7), find_script);
|
||||
utils::hook::call(SELECT_VALUE(0x1403DC897, 0x1404378E7), db_is_x_asset_default);
|
||||
|
||||
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments");
|
||||
|
||||
if (game::environment::is_sp())
|
||||
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ProcessScript
|
||||
utils::hook::call(0x1404378D7, find_script);
|
||||
utils::hook::call(0x1404378E7, db_is_x_asset_default);
|
||||
|
||||
// GScr_LoadScripts
|
||||
utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub);
|
||||
|
||||
// Exec script handles
|
||||
utils::hook::call(0x14039F64E, g_load_structs_stub);
|
||||
utils::hook::call(0x14039F653, scr_load_level_stub);
|
||||
|
||||
scripting::on_shutdown([](int free_scripts)
|
||||
{
|
||||
if (free_scripts)
|
||||
if (clear_scripts && post_shutdown)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
});
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
utils::hook::call(0x14034996F, g_scr_load_script_and_label_stub);
|
||||
|
||||
utils::hook::call(0x140316591, scr_load_level_singleplayer_stub);
|
||||
}
|
||||
else
|
||||
{
|
||||
// GScr_LoadScripts
|
||||
utils::hook::call(0x1403CD009, gscr_load_game_type_script_stub);
|
||||
|
||||
// Exec script handles
|
||||
utils::hook::call(0x14039F64E, g_load_structs_stub);
|
||||
utils::hook::call(0x14039F653, scr_load_level_multiplayer_stub);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "mods.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace mods
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour sys_create_file_hook;
|
||||
|
||||
void db_build_os_path_from_source(const char* zone_name, game::FF_DIR source, unsigned int size, char* filename)
|
||||
{
|
||||
char user_map[MAX_PATH]{};
|
||||
|
||||
switch (source)
|
||||
{
|
||||
case game::FFD_DEFAULT:
|
||||
(void)sprintf_s(filename, size, "%s\\%s.ff", std::filesystem::current_path().string().c_str(), zone_name);
|
||||
break;
|
||||
case game::FFD_MOD_DIR:
|
||||
assert(mods::is_using_mods());
|
||||
|
||||
(void)sprintf_s(filename, size, "%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), (*dvars::fs_gameDirVar)->current.string, zone_name);
|
||||
break;
|
||||
case game::FFD_USER_MAP:
|
||||
strncpy_s(user_map, zone_name, _TRUNCATE);
|
||||
|
||||
(void)sprintf_s(filename, size, "%s\\%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), "usermaps", user_map, zone_name);
|
||||
break;
|
||||
default:
|
||||
assert(false && "inconceivable");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
game::Sys_File sys_create_file_stub(const char* dir, const char* filename)
|
||||
{
|
||||
auto result = sys_create_file_hook.invoke<game::Sys_File>(dir, filename);
|
||||
|
||||
if (result.handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!is_using_mods())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// .ff extension was added previously
|
||||
if (!std::strcmp(filename, "mod.ff") && mods::db_mod_file_exists())
|
||||
{
|
||||
char file_path[MAX_PATH]{};
|
||||
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(file_path), file_path);
|
||||
result.handle = game::Sys_OpenFileReliable(file_path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void db_load_x_assets_stub(game::XZoneInfo* zone_info, unsigned int zone_count, game::DBSyncMode sync_mode)
|
||||
{
|
||||
std::vector<game::XZoneInfo> zones(zone_info, zone_info + zone_count);
|
||||
|
||||
if (db_mod_file_exists())
|
||||
{
|
||||
zones.emplace_back("mod", game::DB_ZONE_COMMON | game::DB_ZONE_CUSTOM, 0);
|
||||
}
|
||||
|
||||
game::DB_LoadXAssets(zones.data(), static_cast<unsigned int>(zones.size()), sync_mode);
|
||||
}
|
||||
|
||||
const auto skip_extra_zones_stub = utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
const auto skip = a.newLabel();
|
||||
const auto original = a.newLabel();
|
||||
|
||||
a.pushad64();
|
||||
a.test(ebp, game::DB_ZONE_CUSTOM); // allocFlags
|
||||
a.jnz(skip);
|
||||
|
||||
a.bind(original);
|
||||
a.popad64();
|
||||
a.mov(rdx, 0x140835F28);
|
||||
a.mov(rcx, rsi);
|
||||
a.call_aligned(strcmp);
|
||||
a.jmp(0x1403217C0);
|
||||
|
||||
a.bind(skip);
|
||||
a.popad64();
|
||||
a.mov(r15d, 0x80);
|
||||
a.not_(r15d);
|
||||
a.and_(ebp, r15d);
|
||||
a.jmp(0x1403217F6);
|
||||
});
|
||||
}
|
||||
|
||||
bool is_using_mods()
|
||||
{
|
||||
return (*dvars::fs_gameDirVar) && *(*dvars::fs_gameDirVar)->current.string;
|
||||
}
|
||||
|
||||
bool db_mod_file_exists()
|
||||
{
|
||||
if (!*(*dvars::fs_gameDirVar)->current.string)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char filename[MAX_PATH]{};
|
||||
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(filename), filename);
|
||||
|
||||
if (auto zone_file = game::Sys_OpenFileReliable(filename); zone_file != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
::CloseHandle(zone_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
static_assert(sizeof(game::Sys_File) == 8);
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
dvars::fs_gameDirVar = reinterpret_cast<game::dvar_t**>(SELECT_VALUE(0x145856D38, 0x147876000));
|
||||
|
||||
// Remove DVAR_INIT from fs_game
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x14041C085 + 2, 0x1404DDA45 + 2), SELECT_VALUE(game::DVAR_FLAG_NONE, game::DVAR_FLAG_SERVERINFO));
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't load eng_ + patch_ with loadzone
|
||||
utils::hook::nop(0x1403217B1, 15);
|
||||
utils::hook::jump(0x1403217B1, skip_extra_zones_stub, true);
|
||||
|
||||
// Add custom zone paths
|
||||
sys_create_file_hook.create(game::Sys_CreateFile, sys_create_file_stub);
|
||||
|
||||
// Load mod.ff
|
||||
utils::hook::call(0x1405E7113, db_load_x_assets_stub); // R_LoadGraphicsAssets According to myself but I don't remember where I got it from
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(mods::component)
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace mods
|
||||
{
|
||||
bool is_using_mods();
|
||||
bool db_mod_file_exists();
|
||||
}
|
||||
@@ -126,7 +126,7 @@ namespace notifies
|
||||
a.lea(eax, dword_ptr(r15, -0x17));
|
||||
a.mov(dword_ptr(rbp, 0x60), r15d);
|
||||
|
||||
a.jmp(0x14043A593);
|
||||
a.jmp(SELECT_VALUE(0x1403DF5B3, 0x14043A593));
|
||||
|
||||
a.bind(replace);
|
||||
|
||||
@@ -174,22 +174,22 @@ namespace notifies
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
utils::hook::jump(SELECT_VALUE(0x1403DF5A4, 0x14043A584), utils::hook::assemble(vm_execute_stub), true);
|
||||
|
||||
scripting::on_shutdown([](const int clear_scripts, const int post_shutdown) -> void
|
||||
{
|
||||
if (clear_scripts && post_shutdown)
|
||||
{
|
||||
vm_execute_hooks.clear();
|
||||
}
|
||||
});
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::call(0x1404724DD, client_command_stub);
|
||||
|
||||
utils::hook::jump(0x14043A584, utils::hook::assemble(vm_execute_stub), true);
|
||||
|
||||
scripting::on_shutdown([](const bool free_scripts)
|
||||
{
|
||||
if (free_scripts)
|
||||
{
|
||||
vm_execute_hooks.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,6 +81,26 @@ namespace party
|
||||
a.mov(ecx, 2);
|
||||
a.jmp(0x1402C617D);
|
||||
});
|
||||
|
||||
utils::info_string get_info()
|
||||
{
|
||||
utils::info_string info;
|
||||
|
||||
info.set("gamename", "IW6");
|
||||
info.set("hostname", dvars::get_string("sv_hostname"));
|
||||
info.set("gametype", dvars::get_string("g_gametype"));
|
||||
info.set("sv_motd", dvars::get_string("sv_motd"));
|
||||
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
|
||||
info.set("mapname", dvars::get_string("mapname"));
|
||||
info.set("isPrivate", dvars::get_string("g_password").empty() ? "0" : "1");
|
||||
info.set("clients", std::to_string(get_client_count()));
|
||||
info.set("bots", std::to_string(get_bot_count()));
|
||||
info.set("sv_maxclients", std::to_string(game::mp::svs->clientCount));
|
||||
info.set("protocol", std::to_string(PROTOCOL));
|
||||
info.set("shortversion", SHORTVERSION);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
void connect_to_dummy_party(const std::string& mapname, const std::string& gametype)
|
||||
@@ -366,7 +386,7 @@ namespace party
|
||||
const auto message = params.join(2);
|
||||
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
|
||||
|
||||
game::SV_GameSendServerCommand(client_num, 0, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
|
||||
console::info("%s -> %i: %s\n", name, client_num, message.data());
|
||||
});
|
||||
|
||||
@@ -380,7 +400,7 @@ namespace party
|
||||
const auto client_num = atoi(params.get(1));
|
||||
const auto message = params.join(2);
|
||||
|
||||
game::SV_GameSendServerCommand(client_num, 0, utils::string::va("%c \"%s\"", 84, message.data()));
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data()));
|
||||
console::info("%i: %s\n", client_num, message.data());
|
||||
});
|
||||
|
||||
@@ -394,7 +414,7 @@ namespace party
|
||||
const auto message = params.join(1);
|
||||
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
|
||||
|
||||
game::SV_GameSendServerCommand(-1, 0, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
|
||||
game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
|
||||
console::info("%s: %s\n", name, message.data());
|
||||
});
|
||||
|
||||
@@ -407,30 +427,61 @@ namespace party
|
||||
|
||||
const auto message = params.join(1);
|
||||
|
||||
game::SV_GameSendServerCommand(-1, 0, utils::string::va("%c \"%s\"", 84, message.data()));
|
||||
game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.data()));
|
||||
console::info("%s\n", message.data());
|
||||
});
|
||||
|
||||
network::on("getInfo", [](const game::netadr_t& target, const std::string& data)
|
||||
{
|
||||
utils::info_string info;
|
||||
utils::info_string info = get_info();
|
||||
info.set("challenge", data);
|
||||
info.set("gamename", "IW6");
|
||||
info.set("hostname", dvars::get_string("sv_hostname"));
|
||||
info.set("gametype", dvars::get_string("g_gametype"));
|
||||
info.set("sv_motd", dvars::get_string("sv_motd"));
|
||||
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
|
||||
info.set("mapname", dvars::get_string("mapname"));
|
||||
info.set("isPrivate", dvars::get_string("g_password").empty() ? "0" : "1");
|
||||
info.set("clients", std::to_string(get_client_count()));
|
||||
info.set("bots", std::to_string(get_bot_count()));
|
||||
info.set("sv_maxclients", std::to_string(game::mp::svs->clientCount));
|
||||
info.set("protocol", std::to_string(PROTOCOL));
|
||||
info.set("shortversion", SHORTVERSION);
|
||||
|
||||
network::send(target, "infoResponse", info.build(), '\n');
|
||||
});
|
||||
|
||||
network::on("getStatus", [](const game::netadr_t& target, const std::string& data)
|
||||
{
|
||||
std::string player_list;
|
||||
|
||||
utils::info_string info = get_info();
|
||||
info.set("challenge", data);
|
||||
|
||||
const auto* sv_running = game::Dvar_FindVar("sv_running");
|
||||
if (!sv_running || !sv_running->current.enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < game::Dvar_FindVar("sv_maxclients")->current.integer; ++i)
|
||||
{
|
||||
auto* client = &game::mp::svs->clients[i];
|
||||
auto* self = &game::mp::g_entities[i];
|
||||
|
||||
if (client->header.state < game::CS_ACTIVE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!self || !self->client)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (game::SV_BotIsBot(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto score = game::G_GetClientScore(i);
|
||||
const auto ping = game::mp::svs->clients[i].ping;
|
||||
const std::string name = game::mp::svs->clients[i].name;
|
||||
|
||||
player_list.append(std::format("{} {} \"{}\"\n", score, ping, name));
|
||||
}
|
||||
|
||||
network::send(target, "statusResponse", info.build() + "\n"s + player_list + "\n"s, '\n');
|
||||
});
|
||||
|
||||
if (game::environment::is_dedi())
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace scripting
|
||||
|
||||
std::unordered_map<unsigned int, std::string> canonical_string_table;
|
||||
|
||||
std::vector<std::function<void(int)>> shutdown_callbacks;
|
||||
std::vector<std::function<void(int, int)>> shutdown_callbacks;
|
||||
std::vector<std::function<void()>> init_callbacks;
|
||||
|
||||
void scr_load_level_stub()
|
||||
@@ -47,9 +47,9 @@ namespace scripting
|
||||
}
|
||||
}
|
||||
|
||||
void g_shutdown_game_stub(const int 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();
|
||||
@@ -59,10 +59,15 @@ namespace scripting
|
||||
|
||||
for (const auto& callback : shutdown_callbacks)
|
||||
{
|
||||
callback(free_scripts);
|
||||
callback(clear_scripts ,false);
|
||||
}
|
||||
|
||||
return g_shutdown_game_hook.invoke<void>(free_scripts);
|
||||
g_shutdown_game_hook.invoke<void>(clear_scripts);
|
||||
|
||||
for (const auto& callback : shutdown_callbacks)
|
||||
{
|
||||
callback(clear_scripts, true);
|
||||
}
|
||||
}
|
||||
|
||||
void process_script_stub(const char* filename)
|
||||
@@ -165,7 +170,7 @@ namespace scripting
|
||||
return find_token(id);
|
||||
}
|
||||
|
||||
void on_shutdown(const std::function<void(int)>& callback)
|
||||
void on_shutdown(const std::function<void(int, int)>& callback)
|
||||
{
|
||||
shutdown_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace scripting
|
||||
|
||||
extern std::string current_file;
|
||||
|
||||
void on_shutdown(const std::function<void(int)>& callback);
|
||||
void on_shutdown(const std::function<void(int, int)>& callback);
|
||||
void on_init(const std::function<void()>& callback);
|
||||
|
||||
std::optional<std::string> get_canonical_string(unsigned int id);
|
||||
|
||||
@@ -46,7 +46,9 @@ namespace dvars
|
||||
game::dvar_t* cg_legacyCrashHandling = nullptr;
|
||||
|
||||
game::dvar_t* com_developer_script = nullptr;
|
||||
game::dvar_t** com_developer;
|
||||
game::dvar_t** com_developer = nullptr;
|
||||
|
||||
game::dvar_t** fs_gameDirVar = nullptr;
|
||||
|
||||
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain)
|
||||
{
|
||||
|
||||
@@ -47,6 +47,8 @@ namespace dvars
|
||||
extern game::dvar_t* com_developer_script;
|
||||
extern game::dvar_t** com_developer;
|
||||
|
||||
extern game::dvar_t** fs_gameDirVar;
|
||||
|
||||
std::string dvar_get_vector_domain(int components, const game::dvar_limits& domain);
|
||||
std::string dvar_get_domain(game::dvar_type type, const game::dvar_limits& domain);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,13 @@ namespace game
|
||||
return sv_cmd_args->argv[sv_cmd_args->nesting][index];
|
||||
}
|
||||
|
||||
HANDLE Sys_OpenFileReliable(const char* filename)
|
||||
{
|
||||
return ::CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr);
|
||||
}
|
||||
|
||||
namespace environment
|
||||
{
|
||||
launcher::mode mode = launcher::mode::none;
|
||||
|
||||
@@ -63,6 +63,8 @@ namespace game
|
||||
[[nodiscard]] int SV_Cmd_Argc();
|
||||
[[nodiscard]] const char* SV_Cmd_Argv(int index);
|
||||
|
||||
[[nodiscard]] HANDLE Sys_OpenFileReliable(const char* filename);
|
||||
|
||||
[[nodiscard]] bool is_headless();
|
||||
void show_error(const std::string& text, const std::string& title = "Error");
|
||||
|
||||
|
||||
@@ -47,6 +47,12 @@ namespace game
|
||||
PMF_UNK3 = 0x4000,
|
||||
};
|
||||
|
||||
enum svscmd_type
|
||||
{
|
||||
SV_CMD_CAN_IGNORE,
|
||||
SV_CMD_RELIABLE,
|
||||
};
|
||||
|
||||
enum XAssetType
|
||||
{
|
||||
ASSET_TYPE_PHYSPRESET = 0x0,
|
||||
@@ -127,6 +133,19 @@ namespace game
|
||||
DB_LOAD_SYNC_SKIP_ALWAYS_LOADED = 0x5,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
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 = 0x80,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
THREAD_CONTEXT_MAIN = 0x0,
|
||||
@@ -1123,6 +1142,7 @@ namespace game
|
||||
DVAR_FLAG_LATCHED = 0x2,
|
||||
DVAR_FLAG_CHEAT = 0x4,
|
||||
DVAR_FLAG_REPLICATED = 0x8,
|
||||
DVAR_FLAG_SERVERINFO = 0x400,
|
||||
DVAR_FLAG_WRITE = 0x800,
|
||||
DVAR_FLAG_READ = 0x2000,
|
||||
};
|
||||
@@ -1588,6 +1608,13 @@ namespace game
|
||||
VAR_TOTAL_COUNT = 0x1B,
|
||||
};
|
||||
|
||||
struct ScriptFunctions
|
||||
{
|
||||
int maxSize;
|
||||
int count;
|
||||
int* address;
|
||||
};
|
||||
|
||||
struct VariableStackBuffer
|
||||
{
|
||||
const char* pos;
|
||||
@@ -1987,8 +2014,8 @@ namespace game
|
||||
SurfaceFxTable *surfaceFx;*/
|
||||
RawFile* rawfile;
|
||||
ScriptFile* scriptfile;
|
||||
/*StringTable *stringTable;
|
||||
LeaderboardDef *leaderboardDef;
|
||||
StringTable* stringTable;
|
||||
/*LeaderboardDef *leaderboardDef;
|
||||
StructuredDataDefSet *structuredDataDefSet;
|
||||
TracerDef *tracerDef;
|
||||
VehicleDef *vehDef;
|
||||
@@ -2050,6 +2077,18 @@ namespace game
|
||||
const void* weapCompleteDef;
|
||||
};
|
||||
|
||||
enum FF_DIR
|
||||
{
|
||||
FFD_DEFAULT = 0x0,
|
||||
FFD_MOD_DIR = 0x1,
|
||||
FFD_USER_MAP = 0x2,
|
||||
};
|
||||
|
||||
struct Sys_File
|
||||
{
|
||||
HANDLE handle;
|
||||
};
|
||||
|
||||
namespace sp
|
||||
{
|
||||
// very shit structures for the moment cuz i cba mapping the whole thing out right now...
|
||||
|
||||
+26
-15
@@ -8,12 +8,12 @@ namespace game
|
||||
* Functions
|
||||
**************************************************************/
|
||||
|
||||
WEAK symbol<void(unsigned int id)> AddRefToObject{0, 0x1404326D0};
|
||||
WEAK symbol<void(unsigned int id)> AddRefToObject{0x1403D7A10, 0x1404326D0};
|
||||
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x1403D7740, 0x1404326E0};
|
||||
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0, 0x1404329B0};
|
||||
WEAK symbol<ObjectVariableValue*(unsigned int* index)> AllocVariable{0x1403D7A70, 0x140432A10};
|
||||
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x1403D90F0, 0x1404340C0};
|
||||
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0, 0x140433FB0};
|
||||
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x1403D8FE0, 0x140433FB0};
|
||||
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x1403D91C0, 0x140434190};
|
||||
|
||||
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x140139D80};
|
||||
@@ -58,6 +58,7 @@ namespace game
|
||||
|
||||
WEAK symbol<void(const char* cmdName, void (), cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{0x1403B3570, 0x1403F7070};
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x1403B3B10, 0x1403F7680};
|
||||
WEAK symbol<float()> Com_GetTimescaleForSnd{0, 0x140413860};
|
||||
WEAK symbol<int(int msec)> Com_TimeScaleMsec{0, 0x140415D50};
|
||||
|
||||
WEAK symbol<void(XAssetType type, void (__cdecl *func)(XAssetHeader, void*), void* inData, bool includeOverride)> DB_EnumXAssets_FastFile{0x140271F50, 0x14031EF90};
|
||||
@@ -74,7 +75,7 @@ namespace game
|
||||
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x140272D50, 0x14031FE50};
|
||||
WEAK symbol<int(const char* zoneName)> DB_IsLocalized{0x140273210, 0x140320360};
|
||||
|
||||
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, PMem_Source source)> PMem_AllocFromSource_NoDebug{0x140430B80, 0x001404F46C0};
|
||||
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, PMem_Source source)> PMem_AllocFromSource_NoDebug{0x140430B80, 0x1404F46C0};
|
||||
WEAK symbol<void(const char* name, PMem_Direction allocDir)> PMem_Free{0x140430EC0 , 0x1404F4A30};
|
||||
|
||||
WEAK symbol<void*(unsigned int size)> Hunk_AllocateTempMemoryHighInternal{0x140423C70, 0x1404E4E20};
|
||||
@@ -121,6 +122,7 @@ namespace game
|
||||
WEAK symbol<unsigned int (const char* name, /*ConfigString*/ unsigned int start, unsigned int max, int create,
|
||||
const char* errormsg)> G_FindConfigstringIndex{0, 0x140161F90};
|
||||
WEAK symbol<int(int server_time)> G_RunFrame{0, 0x1403A05E0};
|
||||
WEAK symbol<int(int clientNun)> G_GetClientScore{0, 0x14039EF60};
|
||||
|
||||
WEAK symbol<game_hudelem_t*(int clientNum, int teamNum)> HudElem_Alloc{0, 0x1403997E0};
|
||||
|
||||
@@ -134,7 +136,7 @@ namespace game
|
||||
|
||||
WEAK symbol<unsigned int (int)> Live_SyncOnlineDataFlags{0, 0x1405ABF70};
|
||||
|
||||
WEAK symbol<const char*()> LiveStorage_FetchFFotD{0x140415FD0, 0x1404D7C00};
|
||||
WEAK symbol<const char*()> LiveStorage_FetchFFotD{0x140415FD0, 0x1404D7C00}; // get build number
|
||||
WEAK symbol<bool(int controllerIndex, unsigned int name, int value, StatsGroup statsGroup)> LiveStorage_PlayerDataSetIntByName{0x1403B8C20, 0x140404730};
|
||||
WEAK symbol<bool(std::uint8_t* persistentData, const char* lookupString, int value, std::uint8_t* modifiedFlags, StatsGroup statsGroup)> LiveStorage_PlayerDataSetReservedInt{0x1403B8D00, 0x140404820};
|
||||
WEAK symbol<std::int64_t(std::uint8_t* persistentData, const char* lookupString, const StatsGroup statsGroup)> LiveStorage_PlayerDataGetReservedInt{0x1403B84F0, 0x140403CF0};
|
||||
@@ -151,11 +153,13 @@ namespace game
|
||||
|
||||
WEAK symbol<StructuredDataDef*(const char* filename, unsigned int maxSize)>StructuredDataDef_GetAsset{0, 0x1404E6560};
|
||||
WEAK symbol<StringTable*(const char* fileName, const StringTable** tablePtr)>StringTable_GetAsset{0, 0x1404E6170};
|
||||
WEAK symbol<const char*(const StringTable* table, const int row, const int column)> StringTable_GetColumnValueForRow{0, 0x1404E61A0};
|
||||
WEAK symbol<int(const StringTable* table, const int comparisonColumn, const char* value)> StringTable_LookupRowNumForValue{0, 0x1404E6260};
|
||||
WEAK symbol<const char*(const StringTable* table, int row, int column)> StringTable_GetColumnValueForRow{0, 0x1404E61A0};
|
||||
WEAK symbol<int(const StringTable* table, int comparisonColumn, const char* value)> StringTable_LookupRowNumForValue{0, 0x1404E6260};
|
||||
WEAK symbol<int(const char* string)> StringTable_HashString{0x1404259A0, 0x1404E6320};
|
||||
|
||||
WEAK symbol<bool(int localClientNum, void* lua_vm)> LUI_IntermissionBegan{0, 0x1401CEB40};
|
||||
WEAK symbol<bool(int localClientNum, void* luaVM)> LUI_IntermissionBegan{0, 0x1401CEB40};
|
||||
WEAK symbol<void(int localClientNum, const char* menuName, int isPopup, int isModal, unsigned int isExclusive)> LUI_OpenMenu{0x1403FD460, 0x1404B3610};
|
||||
WEAK symbol<void(int localClientNum, const char* rootName, int deltaTime, void* luaVM)> LUI_Layout{0, 0x1401CF060};
|
||||
// Made up name, replaced by ScopedCriticalSection on Black Ops 3
|
||||
WEAK symbol<void()> LUI_EnterCriticalSection{0x1401AE940, 0x1401CD040};
|
||||
WEAK symbol<void()> LUI_LeaveCriticalSection{0x1401B0AA0, 0x1401CF1A0};
|
||||
@@ -188,13 +192,17 @@ namespace game
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1403D84F0, 0x1404334A0};
|
||||
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0, 0x1404333A0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int id)> GetVariableName{0x1403D8E90, 0x140433E60};
|
||||
WEAK symbol<void (VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x1403DC810, 0x140437860};
|
||||
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x1403DC810, 0x140437860};
|
||||
WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x1403D9AF0, 0x140434A10};
|
||||
WEAK symbol<const char*(unsigned int index)> Scr_GetString{0, 0x140439160};
|
||||
WEAK symbol<const char*(unsigned int index)> Scr_GetString{0x1403DE200, 0x140439160};
|
||||
WEAK symbol<unsigned int(unsigned int index)> Scr_GetConstString{0x1403DDAC0, 0x140438AD0};
|
||||
WEAK symbol<unsigned int(unsigned int index)> Scr_GetConstIString{0x1403DD8A0, 0x1404388B0};
|
||||
WEAK symbol<void(int value)> Scr_AddInt{0, 0x140437E70};
|
||||
WEAK symbol<void(const char* value)> Scr_AddString{0, 0x1404381D0};
|
||||
WEAK symbol<int(unsigned int index)> Scr_GetInt{0, 0x140438E10};
|
||||
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0, 0x140438D60};
|
||||
WEAK symbol<int(unsigned int index)> Scr_GetInt{0x1403DDEB0, 0x140438E10};
|
||||
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0x1403DDD50, 0x140438D60};
|
||||
WEAK symbol<void(unsigned int index, float* vectorValue)> Scr_GetVector{0x1403DE470, 0x1404393D0};
|
||||
WEAK symbol<unsigned int(unsigned int index)> Scr_GetObject{0x1403DDF70, 0x140438ED0};
|
||||
WEAK symbol<unsigned int()> Scr_GetNumParam{0x1403DDF60, 0x140438EC0};
|
||||
WEAK symbol<void()> Scr_ClearOutParams{0x1403DD500, 0x140438600};
|
||||
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x1403DBDC0, 0x140436E10};
|
||||
@@ -203,15 +211,16 @@ namespace game
|
||||
WEAK symbol<void(unsigned int id, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyId{0x1403DE730, 0x140439700};
|
||||
WEAK symbol<void(unsigned int stringValue, unsigned int paramcount)> Scr_NotifyLevel{0, 0x1404397D0};
|
||||
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0, 0x140436D60};
|
||||
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0, 0x140434AC0};
|
||||
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x1403D9BA0, 0x140434AC0};
|
||||
|
||||
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x1403DD600, 0x1404386E0};
|
||||
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x1403D3C50, 0x14042EAA0};
|
||||
WEAK symbol<unsigned int(const char* script, unsigned int name)> Scr_GetFunctionHandle{0x1403D3AD0 , 0x14042E920};
|
||||
WEAK symbol<unsigned int(void* func, int type, unsigned int name)> Scr_RegisterFunction{0x1403D3530, 0x14042E330};
|
||||
WEAK symbol<void()> Scr_ErrorInternal{0, 0x140438660};
|
||||
WEAK symbol<unsigned int(const char** pName, int* type)> Scr_GetFunction{0x14034A950, 0x1403CD9F0};
|
||||
WEAK symbol<void()> Scr_ErrorInternal{0x1403DD580 , 0x140438660};
|
||||
|
||||
WEAK symbol<int(unsigned int)> GetObjectType{0, 0x140433CF0};
|
||||
WEAK symbol<int(unsigned int)> GetObjectType{0x1403D8D30, 0x140433CF0};
|
||||
|
||||
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0, 0x14043A280};
|
||||
|
||||
@@ -229,7 +238,6 @@ namespace game
|
||||
WEAK symbol<void()> SV_MatchEnd{0, 0x14047A090};
|
||||
|
||||
WEAK symbol<void(netadr_t* from)> SV_DirectConnect{0, 0x140471390};
|
||||
WEAK symbol<void(int, int, const char*)> SV_GameSendServerCommand{0x140490F40, 0x1404758C0};
|
||||
WEAK symbol<bool()> SV_Loaded{0x140491820, 0x1404770C0};
|
||||
WEAK symbol<void(int localClientNum, const char* map, bool mapIsPreloaded)> SV_StartMap{0, 0x140470170};
|
||||
WEAK symbol<void(int localClientNum, const char* map, bool mapIsPreloaded, bool migrate)> SV_StartMapForParty{0, 0x1404702F0};
|
||||
@@ -246,6 +254,7 @@ namespace game
|
||||
WEAK symbol<void(int clientNum, const char* reason)> SV_KickClientNum{0, 0x14046F730};
|
||||
WEAK symbol<bool(mp::client_t*, const char*, int)> SV_Netchan_Transmit{0, 0x14047CC60};
|
||||
WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0, 0x140477450};
|
||||
WEAK symbol<void(int clientNum, svscmd_type type, const char* text)> SV_GameSendServerCommand{0x140490F40, 0x1404758C0};
|
||||
|
||||
WEAK symbol<void(const char* error, ...)> Sys_Error{0x14043AC20, 0x1404FF510};
|
||||
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x1403C2D40, 0x140423920};
|
||||
@@ -275,6 +284,8 @@ namespace game
|
||||
|
||||
WEAK symbol<void(void* ps)> Jump_ClearState{0, 0x140213120};
|
||||
|
||||
WEAK symbol<void(unsigned __int64 markPos)> LargeLocalResetToMark{0x140423B50, 0x1404E4D00};
|
||||
|
||||
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14062E030, 0x140738060};
|
||||
WEAK symbol<int (jmp_buf* Buf)> _setjmp{0x14062F030, 0x140739060};
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#define BINARY_PAYLOAD_SIZE 0x0B500000
|
||||
|
||||
// Decide whether to load the game as lib or to inject it
|
||||
#if 1
|
||||
#define INJECT_HOST_AS_LIB
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4100)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
|
||||
namespace utils::properties
|
||||
{
|
||||
std::filesystem::path get_appdata_path();
|
||||
}
|
||||
Reference in New Issue
Block a user