Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
15ec6cc13f | |||
a55f5874af | |||
664a6b644a |
2
deps/GSL
vendored
2
deps/GSL
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 3325bbd33d24d1f8f5a0f69e782c92ad5a39a68e
|
Subproject commit 355982daf6c54ccb11bef8a1c511be2622dec402
|
2
deps/asmjit
vendored
2
deps/asmjit
vendored
@ -1 +1 @@
|
|||||||
Subproject commit e8c8e2e48a1a38154c8e8864eb3bc61db80a1e31
|
Subproject commit cfc9f813cc6ccda63cad872edb32b38e0662bedb
|
2
deps/gsc-tool
vendored
2
deps/gsc-tool
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 2d9781ce0ce9e8551eaf040d7761fd986f33cfdc
|
Subproject commit e4cb6a7819726d9ea33cd6a52f0dcd9382258b47
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
|||||||
Subproject commit a6b9aff7aab857fe1b491710a5c5b9e2be49cb08
|
Subproject commit 2e9f2b5e44dd498dba60e5175c6c7effa5ff3728
|
2
deps/libtommath
vendored
2
deps/libtommath
vendored
@ -1 +1 @@
|
|||||||
Subproject commit e823b0c34cea291bdb94d672731e1c1f08525557
|
Subproject commit 5809141a3a6ec1bf3443c927c02b955e19224016
|
2
deps/minhook
vendored
2
deps/minhook
vendored
@ -1 +1 @@
|
|||||||
Subproject commit c3fcafdc10146beb5919319d0683e44e3c30d537
|
Subproject commit c1a7c3843bd1a5fe3eb779b64c0d823bca3dc339
|
2
deps/rapidjson
vendored
2
deps/rapidjson
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 24b5e7a8b27f42fa16b96fc70aade9106cf7102f
|
Subproject commit d621dc9e9c77f81e5c8a35b8dcc16dcd63351321
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 5a82f71ed1dfc0bec044d9702463dbdf84ea3b71
|
Subproject commit ef24c4c7502169f016dcd2a26923dbaf3216748c
|
@ -259,7 +259,7 @@ filter "configurations:Release"
|
|||||||
buildoptions {"/GL"}
|
buildoptions {"/GL"}
|
||||||
linkoptions {"/IGNORE:4702", "/LTCG"}
|
linkoptions {"/IGNORE:4702", "/LTCG"}
|
||||||
defines {"NDEBUG"}
|
defines {"NDEBUG"}
|
||||||
fatalwarnings {"All"}
|
flags {"FatalCompileWarnings"}
|
||||||
filter {}
|
filter {}
|
||||||
|
|
||||||
filter "configurations:Debug"
|
filter "configurations:Debug"
|
||||||
|
@ -30,7 +30,8 @@ namespace binding
|
|||||||
|
|
||||||
if (value && value < get_num_keys())
|
if (value && value < get_num_keys())
|
||||||
{
|
{
|
||||||
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
|
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
|
||||||
|
"bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
|
||||||
|
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
{
|
{
|
||||||
@ -44,7 +45,8 @@ namespace binding
|
|||||||
value -= get_num_keys();
|
value -= get_num_keys();
|
||||||
if (static_cast<size_t>(value) < custom_binds.size() && !custom_binds[value].empty())
|
if (static_cast<size_t>(value) < custom_binds.size() && !custom_binds[value].empty())
|
||||||
{
|
{
|
||||||
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, custom_binds[value].data());
|
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
|
||||||
|
"bind %s \"%s\"\n", key_button, custom_binds[value].data());
|
||||||
|
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
{
|
{
|
||||||
|
@ -62,6 +62,7 @@ namespace bots
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SV_BotGetRandomName
|
||||||
const auto* const bot_name = game::SV_BotGetRandomName();
|
const auto* const bot_name = game::SV_BotGetRandomName();
|
||||||
const auto* bot_ent = game::SV_AddBot(bot_name);
|
const auto* bot_ent = game::SV_AddBot(bot_name);
|
||||||
if (bot_ent)
|
if (bot_ent)
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "game/engine/sv_game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
#include "fastfiles.hpp"
|
|
||||||
#include "game_console.hpp"
|
#include "game_console.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "fastfiles.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
@ -235,13 +236,15 @@ namespace command
|
|||||||
{
|
{
|
||||||
if (!dvars::sv_cheats->current.enabled)
|
if (!dvars::sv_cheats->current.enabled)
|
||||||
{
|
{
|
||||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||||
|
"f \"Cheats are not enabled on this server\"");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ent->health < 1)
|
if (ent->health < 1)
|
||||||
{
|
{
|
||||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You must be alive to use this command\"");
|
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||||
|
"f \"You must be alive to use this command\"");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,7 +582,8 @@ namespace command
|
|||||||
|
|
||||||
ent->flags ^= game::FL_GODMODE;
|
ent->flags ^= game::FL_GODMODE;
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"godmode %s\"", (ent->flags & game::FL_GODMODE) ? "^2on" : "^1off"));
|
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||||
|
utils::string::va("f \"godmode %s\"", (ent->flags & game::FL_GODMODE) ? "^2on" : "^1off"));
|
||||||
});
|
});
|
||||||
|
|
||||||
add_sv("demigod", [](game::mp::gentity_s* ent, const params_sv&)
|
add_sv("demigod", [](game::mp::gentity_s* ent, const params_sv&)
|
||||||
@ -589,7 +593,8 @@ namespace command
|
|||||||
|
|
||||||
ent->flags ^= game::FL_DEMI_GODMODE;
|
ent->flags ^= game::FL_DEMI_GODMODE;
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"demigod mode %s\"", (ent->flags & game::FL_DEMI_GODMODE) ? "^2on" : "^1off"));
|
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||||
|
utils::string::va("f \"demigod mode %s\"", (ent->flags & game::FL_DEMI_GODMODE) ? "^2on" : "^1off"));
|
||||||
});
|
});
|
||||||
|
|
||||||
add_sv("notarget", [](game::mp::gentity_s* ent, const params_sv&)
|
add_sv("notarget", [](game::mp::gentity_s* ent, const params_sv&)
|
||||||
@ -599,7 +604,8 @@ namespace command
|
|||||||
|
|
||||||
ent->flags ^= game::FL_NOTARGET;
|
ent->flags ^= game::FL_NOTARGET;
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"notarget %s\"", (ent->flags & game::FL_NOTARGET) ? "^2on" : "^1off"));
|
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||||
|
utils::string::va("f \"notarget %s\"", (ent->flags & game::FL_NOTARGET) ? "^2on" : "^1off"));
|
||||||
});
|
});
|
||||||
|
|
||||||
add_sv("noclip", [](game::mp::gentity_s* ent, const params_sv&)
|
add_sv("noclip", [](game::mp::gentity_s* ent, const params_sv&)
|
||||||
@ -609,7 +615,8 @@ namespace command
|
|||||||
|
|
||||||
ent->client->flags ^= 1;
|
ent->client->flags ^= 1;
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"noclip %s\"", ent->client->flags & 1 ? "^2on" : "^1off"));
|
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||||
|
utils::string::va("f \"noclip %s\"", ent->client->flags & 1 ? "^2on" : "^1off"));
|
||||||
});
|
});
|
||||||
|
|
||||||
add_sv("ufo", [](game::mp::gentity_s* ent, const params_sv&)
|
add_sv("ufo", [](game::mp::gentity_s* ent, const params_sv&)
|
||||||
@ -619,7 +626,8 @@ namespace command
|
|||||||
|
|
||||||
ent->client->flags ^= 2;
|
ent->client->flags ^= 2;
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"ufo %s\"", ent->client->flags & 2 ? "^2on" : "^1off"));
|
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||||
|
utils::string::va("f \"ufo %s\"", ent->client->flags & 2 ? "^2on" : "^1off"));
|
||||||
});
|
});
|
||||||
|
|
||||||
add_sv("give", [](game::mp::gentity_s* ent, const params_sv& params)
|
add_sv("give", [](game::mp::gentity_s* ent, const params_sv& params)
|
||||||
@ -629,7 +637,8 @@ namespace command
|
|||||||
|
|
||||||
if (params.size() < 2)
|
if (params.size() < 2)
|
||||||
{
|
{
|
||||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
|
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||||
|
"f \"You did not specify a weapon name\"");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,7 +661,8 @@ namespace command
|
|||||||
|
|
||||||
if (params.size() < 2)
|
if (params.size() < 2)
|
||||||
{
|
{
|
||||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
|
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||||
|
"f \"You did not specify a weapon name\"");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace console
|
|||||||
{
|
{
|
||||||
static thread_local char buffer[0x1000];
|
static thread_local char buffer[0x1000];
|
||||||
|
|
||||||
const auto count = vsnprintf(buffer, sizeof(buffer), message, *ap);
|
const auto count = vsnprintf_s(buffer, _TRUNCATE, message, *ap);
|
||||||
|
|
||||||
if (count < 0) return {};
|
if (count < 0) return {};
|
||||||
return {buffer, static_cast<size_t>(count)};
|
return {buffer, static_cast<size_t>(count)};
|
||||||
@ -84,14 +84,15 @@ namespace console
|
|||||||
|
|
||||||
void print_stub(const char* fmt, ...)
|
void print_stub(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
char buffer[4096]{};
|
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
[[maybe_unused]] const auto res = vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
|
char buffer[4096]{};
|
||||||
|
const auto res = vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
|
||||||
|
(void)res;
|
||||||
print_message(buffer);
|
print_message(buffer);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void append_text(const char* text)
|
void append_text(const char* text)
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "game/engine/sv_game.hpp"
|
|
||||||
|
|
||||||
#include "command.hpp"
|
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
#include "dvars.hpp"
|
|
||||||
#include "network.hpp"
|
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
#include "server_list.hpp"
|
#include "server_list.hpp"
|
||||||
|
#include "network.hpp"
|
||||||
#include "component/gsc/script_extension.hpp"
|
#include "command.hpp"
|
||||||
|
#include "dvars.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
@ -19,6 +16,9 @@ namespace dedicated
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
utils::hook::detour gscr_set_dynamic_dvar_hook;
|
||||||
|
utils::hook::detour com_quit_f_hook;
|
||||||
|
|
||||||
const game::dvar_t* sv_lanOnly;
|
const game::dvar_t* sv_lanOnly;
|
||||||
|
|
||||||
void init_dedicated_server()
|
void init_dedicated_server()
|
||||||
@ -99,7 +99,8 @@ namespace dedicated
|
|||||||
|
|
||||||
for (const auto& command : queue)
|
for (const auto& command : queue)
|
||||||
{
|
{
|
||||||
command::execute(command);
|
game::Cbuf_AddText(0, command.data());
|
||||||
|
game::Cbuf_AddText(0, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,53 +109,49 @@ namespace dedicated
|
|||||||
std::this_thread::sleep_for(1ms);
|
std::this_thread::sleep_for(1ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sv_kill_server_f()
|
void gscr_set_dynamic_dvar()
|
||||||
{
|
{
|
||||||
game::Com_Shutdown("EXE_SERVERKILLED");
|
auto s = game::Scr_GetString(0);
|
||||||
}
|
auto* dvar = game::Dvar_FindVar(s);
|
||||||
|
if (dvar && !std::strncmp("scr_", dvar->name, 4))
|
||||||
void start_map(const std::string& map_name)
|
|
||||||
{
|
{
|
||||||
if (game::Live_SyncOnlineDataFlags(0) > 32)
|
|
||||||
{
|
|
||||||
scheduler::once([map_name]()
|
|
||||||
{
|
|
||||||
command::execute(std::format("map {}", map_name), false);
|
|
||||||
}, scheduler::pipeline::main, 1s);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!game::SV_MapExists(map_name.c_str()))
|
gscr_set_dynamic_dvar_hook.invoke<void>();
|
||||||
{
|
|
||||||
console::info("Map '%s' doesn't exist.\n", map_name.c_str());
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* current_mapname = game::Dvar_FindVar("mapname");
|
void kill_server()
|
||||||
if (current_mapname && utils::string::to_lower(current_mapname->current.string) == utils::string::to_lower(map_name) && (game::SV_Loaded() && !game::VirtualLobby_Loaded()))
|
|
||||||
{
|
{
|
||||||
console::info("Restarting map: %s\n", map_name.c_str());
|
for (auto i = 0; i < *game::mp::svs_numclients; ++i)
|
||||||
command::execute("map_restart", false);
|
{
|
||||||
return;
|
if (game::mp::svs_clients[i].header.state >= 3)
|
||||||
|
{
|
||||||
|
game::SV_GameSendServerCommand(i, game::SV_CMD_CAN_IGNORE,
|
||||||
|
utils::string::va("r \"%s\"", "EXE_ENDOFGAME"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console::info("Starting map: %s\n", map_name.c_str());
|
com_quit_f_hook.invoke<void>();
|
||||||
|
|
||||||
auto* gametype = game::Dvar_FindVar("g_gametype");
|
|
||||||
if (gametype && gametype->current.string)
|
|
||||||
{
|
|
||||||
command::execute(utils::string::va("ui_gametype %s", gametype->current.string), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
command::execute(utils::string::va("ui_mapname %s", map_name.c_str()), true);
|
void sys_error_stub(const char* msg, ...)
|
||||||
|
|
||||||
game::SV_StartMapForParty(0, map_name.c_str(), false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gscr_is_using_match_rules_data_stub()
|
|
||||||
{
|
{
|
||||||
game::Scr_AddInt(0);
|
char buffer[2048]{};
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, msg);
|
||||||
|
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
scheduler::once([]
|
||||||
|
{
|
||||||
|
command::execute("map_rotate");
|
||||||
|
}, scheduler::main, 3s);
|
||||||
|
|
||||||
|
game::Com_Error(game::ERR_DROP, "%s", buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,12 +190,12 @@ namespace dedicated
|
|||||||
// Don't allow sv_hostname to be changed by the game
|
// Don't allow sv_hostname to be changed by the game
|
||||||
dvars::disable::set_string("sv_hostname");
|
dvars::disable::set_string("sv_hostname");
|
||||||
|
|
||||||
|
// Stop crashing from sys_errors
|
||||||
|
utils::hook::jump(0x1404D6260, sys_error_stub);
|
||||||
|
|
||||||
// Hook R_SyncGpu
|
// Hook R_SyncGpu
|
||||||
utils::hook::jump(0x1405A7630, sync_gpu_stub);
|
utils::hook::jump(0x1405A7630, sync_gpu_stub);
|
||||||
|
|
||||||
// Make GScr_IsUsingMatchRulesData return 0 so the game doesn't override the cfg
|
|
||||||
gsc::override_function("isusingmatchrulesdata", gscr_is_using_match_rules_data_stub);
|
|
||||||
|
|
||||||
utils::hook::jump(0x14020C6B0, init_dedicated_server);
|
utils::hook::jump(0x14020C6B0, init_dedicated_server);
|
||||||
|
|
||||||
// delay startup commands until the initialization is done
|
// delay startup commands until the initialization is done
|
||||||
@ -208,6 +205,9 @@ namespace dedicated
|
|||||||
utils::hook::call(0x1403CEC35, execute_console_command);
|
utils::hook::call(0x1403CEC35, execute_console_command);
|
||||||
utils::hook::nop(0x1403CEC4B, 5);
|
utils::hook::nop(0x1403CEC4B, 5);
|
||||||
|
|
||||||
|
// patch GScr_SetDynamicDvar to behave better
|
||||||
|
gscr_set_dynamic_dvar_hook.create(0x140312D00, &gscr_set_dynamic_dvar);
|
||||||
|
|
||||||
utils::hook::nop(0x1404AE6AE, 5); // don't load config file
|
utils::hook::nop(0x1404AE6AE, 5); // don't load config file
|
||||||
utils::hook::nop(0x1403AF719, 5); // ^
|
utils::hook::nop(0x1403AF719, 5); // ^
|
||||||
utils::hook::set<uint8_t>(0x1403D2490, 0xC3); // don't save config file
|
utils::hook::set<uint8_t>(0x1403D2490, 0xC3); // don't save config file
|
||||||
@ -306,7 +306,7 @@ namespace dedicated
|
|||||||
console::info("==================================\n");
|
console::info("==================================\n");
|
||||||
|
|
||||||
// remove disconnect command
|
// remove disconnect command
|
||||||
game::Cmd_RemoveCommand(751);
|
game::Cmd_RemoveCommand(reinterpret_cast<const char*>(751));
|
||||||
|
|
||||||
execute_startup_command_queue();
|
execute_startup_command_queue();
|
||||||
execute_console_command_queue();
|
execute_console_command_queue();
|
||||||
@ -317,38 +317,8 @@ namespace dedicated
|
|||||||
command::add("heartbeat", send_heartbeat);
|
command::add("heartbeat", send_heartbeat);
|
||||||
}, scheduler::pipeline::main, 1s);
|
}, scheduler::pipeline::main, 1s);
|
||||||
|
|
||||||
command::add("killserver", sv_kill_server_f);
|
command::add("killserver", kill_server);
|
||||||
|
com_quit_f_hook.create(0x1403D08C0, &kill_server);
|
||||||
command::add("map", [](const command::params& argument)
|
|
||||||
{
|
|
||||||
if (argument.size() != 2)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
start_map(argument[1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
command::add("map_restart", []()
|
|
||||||
{
|
|
||||||
if (!game::SV_Loaded() || game::VirtualLobby_Loaded())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*reinterpret_cast<int*>(0x1488692B0) = 1; // sv_map_restart
|
|
||||||
*reinterpret_cast<int*>(0x1488692B4) = 1; // sv_loadScripts
|
|
||||||
*reinterpret_cast<int*>(0x1488692B8) = 0; // sv_migrate
|
|
||||||
reinterpret_cast<void(*)(int)>(0x140437460)(0); // SV_CheckLoadGame
|
|
||||||
});
|
|
||||||
|
|
||||||
command::add("fast_restart", []()
|
|
||||||
{
|
|
||||||
if (game::SV_Loaded() && !game::VirtualLobby_Loaded())
|
|
||||||
{
|
|
||||||
game::SV_FastRestart(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -348,14 +348,15 @@ namespace demonware
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char buffer[2048]{};
|
char buffer[2048];
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
printf("%s: %s\n", function, buffer);
|
printf("%s: %s\n", function, buffer);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void startup_dw()
|
void startup_dw()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "game/engine/sv_game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
@ -130,7 +130,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* 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);
|
const auto* command = utils::string::va("q %s \"%s\"", dvar, value);
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(static_cast<char>(entity_num), game::SV_CMD_RELIABLE, 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)
|
const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a)
|
||||||
|
@ -2,18 +2,43 @@
|
|||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
#include "game/dvars.hpp"
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
#include "fastfiles.hpp"
|
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
|
#include "fastfiles.hpp"
|
||||||
|
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
#include <utils/io.hpp>
|
#include <utils/io.hpp>
|
||||||
#include <utils/concurrency.hpp>
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
namespace fastfiles
|
namespace fastfiles
|
||||||
{
|
{
|
||||||
static utils::concurrency::container<std::string> current_fastfile;
|
static utils::concurrency::container<std::string> current_fastfile;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
inline void merge(std::vector<T>* target, T* source, size_t length)
|
||||||
|
{
|
||||||
|
if (source)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
target->push_back(source[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void merge(std::vector<T>* target, std::vector<T> source)
|
||||||
|
{
|
||||||
|
for (auto& entry : source)
|
||||||
|
{
|
||||||
|
target->push_back(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
utils::hook::detour db_try_load_x_file_internal_hook;
|
utils::hook::detour db_try_load_x_file_internal_hook;
|
||||||
@ -21,11 +46,21 @@ namespace fastfiles
|
|||||||
|
|
||||||
void db_try_load_x_file_internal(const char* zone_name, const int flags)
|
void db_try_load_x_file_internal(const char* zone_name, const int flags)
|
||||||
{
|
{
|
||||||
|
static std::unordered_map<std::string, std::string> zone_overrides = {};
|
||||||
|
|
||||||
|
auto override_zone_name = zone_overrides.find(zone_name);
|
||||||
|
if (override_zone_name != zone_overrides.end())
|
||||||
|
{
|
||||||
|
console::info("Overriding fastfile %s with %s\n", zone_name, override_zone_name->second.c_str());
|
||||||
|
zone_name = override_zone_name->second.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
console::info("Loading fastfile %s\n", zone_name);
|
console::info("Loading fastfile %s\n", zone_name);
|
||||||
current_fastfile.access([&](std::string& fastfile)
|
current_fastfile.access([&](std::string& fastfile)
|
||||||
{
|
{
|
||||||
fastfile = zone_name;
|
fastfile = zone_name;
|
||||||
});
|
});
|
||||||
|
|
||||||
return db_try_load_x_file_internal_hook.invoke<void>(zone_name, flags);
|
return db_try_load_x_file_internal_hook.invoke<void>(zone_name, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,10 +91,12 @@ namespace fastfiles
|
|||||||
const auto result = db_find_x_asset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
|
const auto result = db_find_x_asset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
|
||||||
const auto diff = game::Sys_Milliseconds() - start;
|
const auto diff = game::Sys_Milliseconds() - start;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
if (type == game::ASSET_TYPE_SCRIPTFILE)
|
if (type == game::ASSET_TYPE_SCRIPTFILE)
|
||||||
{
|
{
|
||||||
dump_gsc_script(name, result);
|
dump_gsc_script(name, result);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (diff > 100)
|
if (diff > 100)
|
||||||
{
|
{
|
||||||
@ -73,6 +110,223 @@ namespace fastfiles
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::hook::detour db_link_xasset_entry1_hook;
|
||||||
|
game::XAssetEntry* db_link_xasset_entry1(game::XAssetType type, game::XAssetHeader* header)
|
||||||
|
{
|
||||||
|
if (type == game::ASSET_TYPE_SCRIPTFILE)
|
||||||
|
{
|
||||||
|
dump_gsc_script(header->scriptfile->name, *header);
|
||||||
|
}
|
||||||
|
|
||||||
|
return db_link_xasset_entry1_hook.invoke<game::XAssetEntry*>(type, header);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace mp
|
||||||
|
{
|
||||||
|
void skip_extra_zones_stub(utils::hook::assembler& a)
|
||||||
|
{
|
||||||
|
const auto skip = a.newLabel();
|
||||||
|
const auto original = a.newLabel();
|
||||||
|
|
||||||
|
a.pushad64();
|
||||||
|
a.test(esi, game::DB_ZONE_CUSTOM); // allocFlags
|
||||||
|
a.jnz(skip);
|
||||||
|
|
||||||
|
a.bind(original);
|
||||||
|
a.popad64();
|
||||||
|
a.mov(rdx, 0x140809D40);
|
||||||
|
a.mov(rcx, rbp);
|
||||||
|
a.call(0x1406FE120);
|
||||||
|
a.jmp(0x140271B63);
|
||||||
|
|
||||||
|
a.bind(skip);
|
||||||
|
a.popad64();
|
||||||
|
a.mov(r13d, game::DB_ZONE_CUSTOM);
|
||||||
|
a.not_(r13d);
|
||||||
|
a.and_(esi, r13d);
|
||||||
|
a.jmp(0x140271D02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sp
|
||||||
|
{
|
||||||
|
void skip_extra_zones_stub(utils::hook::assembler& a)
|
||||||
|
{
|
||||||
|
const auto skip = a.newLabel();
|
||||||
|
const auto original = a.newLabel();
|
||||||
|
|
||||||
|
a.pushad64();
|
||||||
|
a.test(edi, game::DB_ZONE_CUSTOM); // allocFlags
|
||||||
|
a.jnz(skip);
|
||||||
|
|
||||||
|
a.bind(original);
|
||||||
|
a.popad64();
|
||||||
|
a.call(0x140379360);
|
||||||
|
a.xor_(ecx, ecx);
|
||||||
|
a.test(eax, eax);
|
||||||
|
a.setz(cl);
|
||||||
|
a.jmp(0x1401802D6);
|
||||||
|
|
||||||
|
a.bind(skip);
|
||||||
|
a.popad64();
|
||||||
|
a.mov(r13d, game::DB_ZONE_CUSTOM);
|
||||||
|
a.not_(r13d);
|
||||||
|
a.and_(edi, r13d);
|
||||||
|
a.jmp(0x1401803EF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::hook::detour db_read_stream_file_hook;
|
||||||
|
void db_read_stream_file_stub(int a1, int a2)
|
||||||
|
{
|
||||||
|
// always use lz4 compressor type when reading stream files
|
||||||
|
*game::g_compressor = 4;
|
||||||
|
return db_read_stream_file_hook.invoke<void>(a1, a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::hook::detour sys_createfile_hook;
|
||||||
|
HANDLE sys_create_file(game::Sys_Folder folder, const char* base_filename)
|
||||||
|
{
|
||||||
|
const auto* fs_basepath = game::Dvar_FindVar("fs_basepath");
|
||||||
|
const auto* fs_game = game::Dvar_FindVar("fs_game");
|
||||||
|
|
||||||
|
const std::string dir = fs_basepath ? fs_basepath->current.string : "";
|
||||||
|
const std::string mod_dir = fs_game ? fs_game->current.string : "";
|
||||||
|
const std::string name = base_filename;
|
||||||
|
|
||||||
|
if (name == "mod.ff")
|
||||||
|
{
|
||||||
|
if (!mod_dir.empty())
|
||||||
|
{
|
||||||
|
const auto path = utils::string::va("%s\\%s\\%s",
|
||||||
|
dir.data(), mod_dir.data(), base_filename);
|
||||||
|
|
||||||
|
if (utils::io::file_exists(path))
|
||||||
|
{
|
||||||
|
return CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
||||||
|
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sys_createfile_hook.invoke<HANDLE>(folder, base_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE sys_create_file_stub(game::Sys_Folder folder, const char* base_filename)
|
||||||
|
{
|
||||||
|
return sys_create_file(folder, base_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_load_zone(std::string name, bool localized, bool game = false)
|
||||||
|
{
|
||||||
|
if (localized)
|
||||||
|
{
|
||||||
|
const auto language = game::SEH_GetCurrentLanguageCode();
|
||||||
|
try_load_zone(language + "_"s + name, false);
|
||||||
|
if (game::environment::is_mp())
|
||||||
|
{
|
||||||
|
try_load_zone(language + "_"s + name + "_mp"s, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fastfiles::exists(name))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::XZoneInfo info{};
|
||||||
|
info.name = name.data();
|
||||||
|
info.allocFlags = (game ? game::DB_ZONE_GAME : game::DB_ZONE_COMMON) | game::DB_ZONE_CUSTOM;
|
||||||
|
info.freeFlags = 0;
|
||||||
|
game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_ASYNC);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_pre_gfx_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode)
|
||||||
|
{
|
||||||
|
//imagefiles::close_custom_handles();
|
||||||
|
|
||||||
|
std::vector<game::XZoneInfo> data;
|
||||||
|
merge(&data, zoneInfo, zoneCount);
|
||||||
|
|
||||||
|
// code_pre_gfx
|
||||||
|
|
||||||
|
//weapon::clear_modifed_enums();
|
||||||
|
try_load_zone("mod_pre_gfx", true);
|
||||||
|
try_load_zone("s1_mod_pre_gfx", true);
|
||||||
|
|
||||||
|
game::DB_LoadXAssets(data.data(), static_cast<std::uint32_t>(data.size()), syncMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_post_gfx_and_ui_and_common_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode)
|
||||||
|
{
|
||||||
|
std::vector<game::XZoneInfo> data;
|
||||||
|
merge(&data, zoneInfo, zoneCount);
|
||||||
|
|
||||||
|
// code_post_gfx
|
||||||
|
// ui
|
||||||
|
// common
|
||||||
|
|
||||||
|
try_load_zone("s1_mod_common", true);
|
||||||
|
|
||||||
|
game::DB_LoadXAssets(data.data(), static_cast<std::uint32_t>(data.size()), syncMode);
|
||||||
|
|
||||||
|
try_load_zone("mod_common", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_ui_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode)
|
||||||
|
{
|
||||||
|
std::vector<game::XZoneInfo> data;
|
||||||
|
merge(&data, zoneInfo, zoneCount);
|
||||||
|
|
||||||
|
// ui
|
||||||
|
|
||||||
|
game::DB_LoadXAssets(data.data(), static_cast<std::uint32_t>(data.size()), syncMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_lua_file_asset_stub(void* a1)
|
||||||
|
{
|
||||||
|
const auto fastfile = fastfiles::get_current_fastfile();
|
||||||
|
if (fastfile == "mod")
|
||||||
|
{
|
||||||
|
console::error("Mod tried to load a lua file!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add usermap LUI loading checks
|
||||||
|
/*
|
||||||
|
const auto usermap = fastfiles::get_current_usermap();
|
||||||
|
if (usermap.has_value())
|
||||||
|
{
|
||||||
|
const auto& usermap_value = usermap.value();
|
||||||
|
const auto usermap_load = usermap_value + "_load";
|
||||||
|
|
||||||
|
if (fastfile == usermap_value || fastfile == usermap_load)
|
||||||
|
{
|
||||||
|
console::error("Usermap tried to load a lua file!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
utils::hook::invoke<void>(0x140276480, a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::hook::detour db_level_load_add_zone_hook;
|
||||||
|
void db_level_load_add_zone_stub(void* load, const char* name, const unsigned int alloc_flags,
|
||||||
|
const size_t size_est)
|
||||||
|
{
|
||||||
|
if (!strcmp(name, "mp_terminal_cls"))
|
||||||
|
{
|
||||||
|
db_level_load_add_zone_hook.invoke<void>(load, name, alloc_flags | game::DB_ZONE_CUSTOM, size_est);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db_level_load_add_zone_hook.invoke<void>(load, name, alloc_flags, size_est);
|
||||||
|
; }
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_current_fastfile()
|
std::string get_current_fastfile()
|
||||||
@ -85,37 +339,35 @@ namespace fastfiles
|
|||||||
return fastfile_copy;
|
return fastfile_copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int get_asset_type_size(const game::XAssetType type)
|
bool exists(const std::string& zone)
|
||||||
{
|
{
|
||||||
constexpr int asset_type_sizes[] =
|
const auto is_localized = game::DB_IsLocalized(zone.data());
|
||||||
{
|
const auto handle = sys_create_file((is_localized ? game::SF_ZONE_LOC : game::SF_ZONE),
|
||||||
96, 88, 128, 56, 40, 216, 56, 680,
|
utils::string::va("%s.ff", zone.data()));
|
||||||
480, 32, 32, 32, 32, 32, 352, 1456,
|
|
||||||
104, 32, 24, 152, 152, 152, 16, 64,
|
|
||||||
640, 40, 16, 408, 24, 288, 176, 2800,
|
|
||||||
48, -1, 40, 24, 200, 88, 16, 120,
|
|
||||||
3560, 32, 64, 16, 16, -1, -1, -1,
|
|
||||||
-1, 24, 40, 24, 40, 24, 128, 2256,
|
|
||||||
136, 32, 72, 24, 64, 88, 48, 32,
|
|
||||||
96, 152, 64, 32,
|
|
||||||
};
|
|
||||||
|
|
||||||
return asset_type_sizes[type];
|
if (handle != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
CloseHandle(handle);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <game::XAssetType Type, size_t Size>
|
return false;
|
||||||
char* reallocate_asset_pool()
|
}
|
||||||
{
|
|
||||||
constexpr auto element_size = get_asset_type_size(Type);
|
|
||||||
static char new_pool[element_size * Size] = {0};
|
|
||||||
assert(get_asset_type_size(Type) == game::DB_GetXAssetTypeSize(Type));
|
|
||||||
|
|
||||||
|
void reallocate_asset_pool(game::XAssetType Type, size_t Size)
|
||||||
|
{
|
||||||
|
const size_t element_size = game::DB_GetXAssetTypeSize(Type);
|
||||||
|
auto* new_pool = utils::memory::get_allocator()->allocate(Size * element_size);
|
||||||
std::memmove(new_pool, game::DB_XAssetPool[Type], game::g_poolSize[Type] * element_size);
|
std::memmove(new_pool, game::DB_XAssetPool[Type], game::g_poolSize[Type] * element_size);
|
||||||
|
|
||||||
game::DB_XAssetPool[Type] = new_pool;
|
game::DB_XAssetPool[Type] = new_pool;
|
||||||
game::g_poolSize[Type] = Size;
|
game::g_poolSize[Type] = static_cast<int>(Size);
|
||||||
|
}
|
||||||
|
|
||||||
return new_pool;
|
template <game::XAssetType Type, size_t Multiplier>
|
||||||
|
void reallocate_asset_pool_multiplier()
|
||||||
|
{
|
||||||
|
reallocate_asset_pool(Type, Multiplier * game::g_poolSize[Type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void enum_assets(const game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, const bool include_override)
|
void enum_assets(const game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, const bool include_override)
|
||||||
@ -127,6 +379,75 @@ namespace fastfiles
|
|||||||
}), &callback, include_override);
|
}), &callback, include_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* get_zone_name(const unsigned int index)
|
||||||
|
{
|
||||||
|
if (game::environment::is_sp())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return game::g_zones[index].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reallocate_asset_pools()
|
||||||
|
{
|
||||||
|
reallocate_asset_pool(game::ASSET_TYPE_FONT, 48);
|
||||||
|
|
||||||
|
if (!game::environment::is_sp())
|
||||||
|
{
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LUA_FILE, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_WEAPON, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LOCALIZE_ENTRY, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_XANIMPARTS, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_ATTACHMENT, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_FONT, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_SNDDRIVER_GLOBALS, 4>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_EQUIPMENT_SND_TABLE, 4>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_SOUND, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LOADED_SOUND, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LEADERBOARD, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_VERTEXDECL, 6>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_COMPUTESHADER, 4>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_REVERB_PRESET, 2>();
|
||||||
|
reallocate_asset_pool_multiplier<game::ASSET_TYPE_IMPACT_FX, 10>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enum_asset_entries(const game::XAssetType type, const std::function<void(game::XAssetEntry*)>& callback, bool include_override)
|
||||||
|
{
|
||||||
|
constexpr auto max_asset_count = 0x1D8A8;
|
||||||
|
auto hash = &game::db_hashTable[0];
|
||||||
|
for (auto c = 0; c < max_asset_count; c++)
|
||||||
|
{
|
||||||
|
for (auto i = *hash; i; )
|
||||||
|
{
|
||||||
|
const auto entry = &game::g_assetEntryPool[i];
|
||||||
|
|
||||||
|
if (entry->asset.type == type)
|
||||||
|
{
|
||||||
|
callback(entry);
|
||||||
|
|
||||||
|
if (include_override && entry->nextOverride)
|
||||||
|
{
|
||||||
|
auto next_ovveride = entry->nextOverride;
|
||||||
|
while (next_ovveride)
|
||||||
|
{
|
||||||
|
const auto override = &game::g_assetEntryPool[next_ovveride];
|
||||||
|
callback(override);
|
||||||
|
next_ovveride = override->nextOverride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = entry->nextHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
++hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -135,6 +456,7 @@ namespace fastfiles
|
|||||||
db_try_load_x_file_internal_hook.create(SELECT_VALUE(0x1401816F0, 0x1402741C0), &db_try_load_x_file_internal);
|
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);
|
db_find_x_asset_header_hook.create(game::DB_FindXAssetHeader, db_find_x_asset_header_stub);
|
||||||
|
db_link_xasset_entry1_hook.create(SELECT_VALUE(0x14017F390, 0x1402708F0), db_link_xasset_entry1);
|
||||||
dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE);
|
dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE);
|
||||||
|
|
||||||
command::add("loadzone", [](const command::params& params)
|
command::add("loadzone", [](const command::params& params)
|
||||||
@ -147,7 +469,7 @@ namespace fastfiles
|
|||||||
|
|
||||||
game::XZoneInfo info{};
|
game::XZoneInfo info{};
|
||||||
info.name = params.get(1);
|
info.name = params.get(1);
|
||||||
info.allocFlags = 1;
|
info.allocFlags = game::DB_ZONE_COMMON | game::DB_ZONE_CUSTOM;
|
||||||
info.freeFlags = 0;
|
info.freeFlags = 0;
|
||||||
game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_SYNC);
|
game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_SYNC);
|
||||||
});
|
});
|
||||||
@ -160,16 +482,50 @@ namespace fastfiles
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
reallocate_asset_pool<game::ASSET_TYPE_FONT, 48>();
|
#ifdef DEBUG
|
||||||
|
//reallocate_asset_pools();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Allow loading of unsigned fastfiles
|
||||||
if (!game::environment::is_sp())
|
if (!game::environment::is_sp())
|
||||||
{
|
{
|
||||||
const auto* xmodel_pool = reallocate_asset_pool<game::ASSET_TYPE_XMODEL, 8832>();
|
utils::hook::nop(0x1402427A5, 2); // DB_InflateInit
|
||||||
utils::hook::inject(0x14026FD63, xmodel_pool + 8);
|
}
|
||||||
utils::hook::inject(0x14026FDB3, xmodel_pool + 8);
|
|
||||||
utils::hook::inject(0x14026FFAC, xmodel_pool + 8);
|
// Don't load extra zones with loadzone
|
||||||
utils::hook::inject(0x14027463C, xmodel_pool + 8);
|
if (game::environment::is_sp())
|
||||||
utils::hook::inject(0x140274689, xmodel_pool + 8);
|
{
|
||||||
|
utils::hook::nop(0x1401802CA, 12);
|
||||||
|
utils::hook::jump(0x1401802CA, utils::hook::assemble(sp::skip_extra_zones_stub), true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
utils::hook::nop(0x140271B54, 15);
|
||||||
|
utils::hook::jump(0x140271B54, utils::hook::assemble(mp::skip_extra_zones_stub), true);
|
||||||
|
|
||||||
|
// dont load localized zone for custom maps (proper patch for this is here: https://github.com/auroramod/h1-mod/blob/develop/src/client/component/fastfiles.cpp#L442)
|
||||||
|
db_level_load_add_zone_hook.create(0x1402705C0, db_level_load_add_zone_stub);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow loading of mixed compressor types
|
||||||
|
utils::hook::nop(SELECT_VALUE(0x1401536D7, 0x140242DF7), 2);
|
||||||
|
|
||||||
|
// Fix compressor type on streamed file load
|
||||||
|
db_read_stream_file_hook.create(SELECT_VALUE(0x140187450, 0x14027AA70), db_read_stream_file_stub);
|
||||||
|
|
||||||
|
// Add custom zone paths
|
||||||
|
sys_createfile_hook.create(game::Sys_CreateFile, sys_create_file_stub);
|
||||||
|
|
||||||
|
// load our custom ui and common zones
|
||||||
|
utils::hook::call(SELECT_VALUE(0x140487CF8, 0x1405A562A), load_post_gfx_and_ui_and_common_zones);
|
||||||
|
|
||||||
|
// load our custom ui zones
|
||||||
|
utils::hook::call(SELECT_VALUE(0x1402F91D4, 0x1403D06FC), load_ui_zones);
|
||||||
|
|
||||||
|
// prevent mod.ff from loading lua files
|
||||||
|
if (game::environment::is_mp())
|
||||||
|
{
|
||||||
|
utils::hook::call(0x14024F1B4, load_lua_file_asset_stub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -6,5 +6,11 @@ namespace fastfiles
|
|||||||
{
|
{
|
||||||
std::string get_current_fastfile();
|
std::string get_current_fastfile();
|
||||||
|
|
||||||
|
bool exists(const std::string& zone);
|
||||||
|
|
||||||
void enum_assets(game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, bool include_override);
|
void enum_assets(game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, bool include_override);
|
||||||
|
|
||||||
|
const char* get_zone_name(const unsigned int index);
|
||||||
|
|
||||||
|
void enum_asset_entries(const game::XAssetType type, const std::function<void(game::XAssetEntry*)>& callback, bool include_override);
|
||||||
}
|
}
|
||||||
|
@ -64,22 +64,20 @@ namespace filesystem
|
|||||||
|
|
||||||
void startup()
|
void startup()
|
||||||
{
|
{
|
||||||
const auto base = std::filesystem::current_path();
|
register_path("s1");
|
||||||
|
|
||||||
register_path(base / "s1");
|
|
||||||
register_path(get_binary_directory() + "\\data");
|
register_path(get_binary_directory() + "\\data");
|
||||||
|
|
||||||
if (get_binary_directory() != base)
|
if (get_binary_directory() != std::filesystem::current_path())
|
||||||
{
|
{
|
||||||
register_path(base / "data");
|
register_path(std::filesystem::current_path() / "data");
|
||||||
}
|
}
|
||||||
|
|
||||||
// game's search paths
|
// game's search paths
|
||||||
register_path(base / "devraw");
|
register_path("devraw");
|
||||||
register_path(base / "devraw_shared");
|
register_path("devraw_shared");
|
||||||
register_path(base / "raw_shared");
|
register_path("raw_shared");
|
||||||
register_path(base / "raw");
|
register_path("raw");
|
||||||
register_path(base / "main");
|
register_path("main");
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_for_startup()
|
void check_for_startup()
|
||||||
|
@ -63,7 +63,7 @@ namespace game_console
|
|||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
|
strncpy_s(con.buffer, "", sizeof(con.buffer));
|
||||||
con.cursor = 0;
|
con.cursor = 0;
|
||||||
|
|
||||||
fixed_input = "";
|
fixed_input = "";
|
||||||
@ -249,7 +249,7 @@ namespace game_console
|
|||||||
dvars::con_inputDvarInactiveValueColor->current.vector, offset);
|
dvars::con_inputDvarInactiveValueColor->current.vector, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), 64);
|
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
|
||||||
con.globals.may_auto_complete = true;
|
con.globals.may_auto_complete = true;
|
||||||
}
|
}
|
||||||
else if (matches.size() > 1)
|
else if (matches.size() > 1)
|
||||||
@ -274,7 +274,7 @@ namespace game_console
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), 64);
|
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
|
||||||
con.globals.may_auto_complete = true;
|
con.globals.may_auto_complete = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -365,11 +365,11 @@ namespace game_console
|
|||||||
|
|
||||||
void print_internal(const char* fmt, ...)
|
void print_internal(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
char va_buffer[1024]{};
|
char va_buffer[0x200]{};
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vsnprintf(va_buffer, sizeof(va_buffer), fmt, ap);
|
vsprintf_s(va_buffer, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
const auto formatted = std::string(va_buffer);
|
const auto formatted = std::string(va_buffer);
|
||||||
@ -425,7 +425,7 @@ namespace game_console
|
|||||||
con.buffer[1] = '\0';
|
con.buffer[1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
game::I_strncat(con.buffer, sizeof(con.buffer), con.globals.auto_complete_choice);
|
strncat_s(con.buffer, con.globals.auto_complete_choice, 64);
|
||||||
con.cursor = static_cast<int>(std::string(con.buffer).length());
|
con.cursor = static_cast<int>(std::string(con.buffer).length());
|
||||||
|
|
||||||
if (con.cursor != 254)
|
if (con.cursor != 254)
|
||||||
@ -550,7 +550,7 @@ namespace game_console
|
|||||||
|
|
||||||
if (history_index != -1)
|
if (history_index != -1)
|
||||||
{
|
{
|
||||||
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
|
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
|
||||||
con.cursor = static_cast<int>(strlen(con.buffer));
|
con.cursor = static_cast<int>(strlen(con.buffer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -565,7 +565,7 @@ namespace game_console
|
|||||||
|
|
||||||
if (history_index != -1)
|
if (history_index != -1)
|
||||||
{
|
{
|
||||||
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
|
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
|
||||||
con.cursor = static_cast<int>(strlen(con.buffer));
|
con.cursor = static_cast<int>(strlen(con.buffer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -720,7 +720,7 @@ namespace game_console
|
|||||||
con.output_visible = false;
|
con.output_visible = false;
|
||||||
con.display_line_offset = 0;
|
con.display_line_offset = 0;
|
||||||
con.line_count = 0;
|
con.line_count = 0;
|
||||||
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
|
strncpy_s(con.buffer, "", sizeof(con.buffer));
|
||||||
|
|
||||||
con.globals.x = 0.0f;
|
con.globals.x = 0.0f;
|
||||||
con.globals.y = 0.0f;
|
con.globals.y = 0.0f;
|
||||||
@ -728,7 +728,7 @@ namespace game_console
|
|||||||
con.globals.font_height = 0.0f;
|
con.globals.font_height = 0.0f;
|
||||||
con.globals.may_auto_complete = false;
|
con.globals.may_auto_complete = false;
|
||||||
con.globals.info_line_count = 0;
|
con.globals.info_line_count = 0;
|
||||||
game::I_strncpyz(con.globals.auto_complete_choice, "", 64);
|
strncpy_s(con.globals.auto_complete_choice, "", 64);
|
||||||
|
|
||||||
// add clear command
|
// add clear command
|
||||||
command::add("clear", [&]()
|
command::add("clear", [&]()
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "gsc/script_extension.hpp"
|
#include "gsc/script_extension.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
#include <utils/io.hpp>
|
#include <utils/io.hpp>
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ namespace game_log
|
|||||||
char buf[1024]{};
|
char buf[1024]{};
|
||||||
std::size_t out_chars = 0;
|
std::size_t out_chars = 0;
|
||||||
|
|
||||||
for (std::uint32_t i = 0; i < game::Scr_GetNumParam(); ++i)
|
for (auto i = 0u; i < game::Scr_GetNumParam(); ++i)
|
||||||
{
|
{
|
||||||
const auto* value = game::Scr_GetString(i);
|
const auto* value = game::Scr_GetString(i);
|
||||||
const auto len = std::strlen(value);
|
const auto len = std::strlen(value);
|
||||||
@ -33,7 +34,7 @@ namespace game_log
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
game::I_strncat(buf, sizeof(buf), value);
|
strncat_s(buf, value, _TRUNCATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_log_printf("%s", buf);
|
g_log_printf("%s", buf);
|
||||||
@ -52,11 +53,18 @@ namespace game_log
|
|||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
|
||||||
|
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
const auto time = *game::level_time / 1000;
|
const auto time = *game::level_time / 1000;
|
||||||
utils::io::write_file(log, utils::string::va("%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, buffer), true);
|
utils::io::write_file(log, utils::string::va("%3i:%i%i %s",
|
||||||
|
time / 60,
|
||||||
|
time % 60 / 10,
|
||||||
|
time % 60 % 10,
|
||||||
|
buffer
|
||||||
|
), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
|
@ -419,7 +419,7 @@ namespace gsc
|
|||||||
public:
|
public:
|
||||||
void post_load() override
|
void post_load() override
|
||||||
{
|
{
|
||||||
gsc_ctx = std::make_unique<xsk::gsc::s1_pc::context>(xsk::gsc::instance::server);
|
gsc_ctx = std::make_unique<xsk::gsc::s1_pc::context>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
|
239
src/client/component/imagefiles.cpp
Normal file
239
src/client/component/imagefiles.cpp
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "console.hpp"
|
||||||
|
#include "filesystem.hpp"
|
||||||
|
#include "fastfiles.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "imagefiles.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
|
#define CUSTOM_IMAGE_FILE_INDEX 96
|
||||||
|
|
||||||
|
namespace imagefiles
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
utils::memory::allocator image_file_allocator;
|
||||||
|
std::unordered_map<std::string, HANDLE> image_file_handles;
|
||||||
|
|
||||||
|
std::string get_image_file_name()
|
||||||
|
{
|
||||||
|
return fastfiles::get_current_fastfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace mp
|
||||||
|
{
|
||||||
|
struct image_file_unk
|
||||||
|
{
|
||||||
|
char __pad0[112]; // 120 in H1
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, image_file_unk*> image_file_unk_map;
|
||||||
|
|
||||||
|
void* get_image_file_unk_mp(unsigned int index)
|
||||||
|
{
|
||||||
|
if (index != CUSTOM_IMAGE_FILE_INDEX)
|
||||||
|
{
|
||||||
|
return &reinterpret_cast<image_file_unk*>(
|
||||||
|
SELECT_VALUE(0x0, 0x143DC4E90))[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto name = get_image_file_name();
|
||||||
|
if (image_file_unk_map.find(name) == image_file_unk_map.end())
|
||||||
|
{
|
||||||
|
const auto unk = image_file_allocator.allocate<image_file_unk>();
|
||||||
|
image_file_unk_map[name] = unk;
|
||||||
|
return unk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return image_file_unk_map[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
namespace sp
|
||||||
|
{
|
||||||
|
struct image_file_unk
|
||||||
|
{
|
||||||
|
char __pad0[96];
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, image_file_unk*> image_file_unk_map;
|
||||||
|
|
||||||
|
void* get_image_file_unk_mp(unsigned int index)
|
||||||
|
{
|
||||||
|
if (index != CUSTOM_IMAGE_FILE_INDEX)
|
||||||
|
{
|
||||||
|
return &reinterpret_cast<image_file_unk*>(
|
||||||
|
SELECT_VALUE(0x0, 0x143DC4E90))[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto name = get_image_file_name();
|
||||||
|
if (image_file_unk_map.find(name) == image_file_unk_map.end())
|
||||||
|
{
|
||||||
|
const auto unk = image_file_allocator.allocate<image_file_unk>();
|
||||||
|
image_file_unk_map[name] = unk;
|
||||||
|
return unk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return image_file_unk_map[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
HANDLE get_image_file_handle(unsigned int index)
|
||||||
|
{
|
||||||
|
if (index != CUSTOM_IMAGE_FILE_INDEX)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<HANDLE*>(
|
||||||
|
SELECT_VALUE(0x0, 0x143B74E80))[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto name = get_image_file_name();
|
||||||
|
return image_file_handles[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
void db_create_gfx_image_stream_stub(utils::hook::assembler& a)
|
||||||
|
{
|
||||||
|
const auto handle_is_open = a.newLabel();
|
||||||
|
|
||||||
|
a.movzx(eax, cx);
|
||||||
|
a.mov(esi, eax);
|
||||||
|
a.imul(rsi, 0x70);
|
||||||
|
a.add(rsi, r15);
|
||||||
|
|
||||||
|
a.push(rax);
|
||||||
|
a.push(rax);
|
||||||
|
a.pushad64();
|
||||||
|
a.mov(rcx, rax);
|
||||||
|
a.call_aligned(mp::get_image_file_unk_mp);
|
||||||
|
a.mov(qword_ptr(rsp, 0x80), rax);
|
||||||
|
a.popad64();
|
||||||
|
a.pop(rax);
|
||||||
|
a.mov(rsi, rax);
|
||||||
|
a.pop(rax);
|
||||||
|
|
||||||
|
a.push(rax);
|
||||||
|
a.push(rax);
|
||||||
|
a.pushad64();
|
||||||
|
a.mov(rcx, rax);
|
||||||
|
a.call_aligned(get_image_file_handle);
|
||||||
|
a.mov(qword_ptr(rsp, 0x80), rax);
|
||||||
|
a.popad64();
|
||||||
|
a.pop(rax);
|
||||||
|
a.mov(r12, rax);
|
||||||
|
a.pop(rax);
|
||||||
|
|
||||||
|
a.cmp(r12, r13);
|
||||||
|
a.jnz(handle_is_open);
|
||||||
|
a.jmp(SELECT_VALUE(0x0, 0x140279C8B));
|
||||||
|
|
||||||
|
a.bind(handle_is_open);
|
||||||
|
a.jmp(SELECT_VALUE(0x0, 0x140279CDB));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* pakfile_open_stub(void* /*handles*/, unsigned int count, int is_imagefile,
|
||||||
|
unsigned int index, short is_localized)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
console::info("Opening %s%d.pak (localized:%d)\n", is_imagefile ? "imagefile" : "soundfile", index, is_localized);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (index != CUSTOM_IMAGE_FILE_INDEX)
|
||||||
|
{
|
||||||
|
return utils::hook::invoke<void*>(
|
||||||
|
SELECT_VALUE(0x0, 0x1404CC180),
|
||||||
|
SELECT_VALUE(0x0, 0x143B74E80),
|
||||||
|
count, is_imagefile, index, is_localized
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto name = get_image_file_name();
|
||||||
|
const auto handle = game::Sys_CreateFile(is_localized ? game::SF_PAKFILE_LOC : game::SF_PAKFILE, utils::string::va("%s.pak", name.data()));
|
||||||
|
if (handle != nullptr)
|
||||||
|
{
|
||||||
|
image_file_handles[name] = handle;
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
int com_sprintf_stub(char* buffer, const int len, const char* fmt, unsigned int index)
|
||||||
|
{
|
||||||
|
if (index != CUSTOM_IMAGE_FILE_INDEX)
|
||||||
|
{
|
||||||
|
return game::Com_sprintf(buffer, len, fmt, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto name = get_image_file_name();
|
||||||
|
return game::Com_sprintf(buffer, len, "%s.pak", name.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_custom_handles()
|
||||||
|
{
|
||||||
|
for (const auto& handle : image_file_handles)
|
||||||
|
{
|
||||||
|
if (handle.second != nullptr)
|
||||||
|
{
|
||||||
|
utils::hook::invoke<void>(0x140608A20, handle.second); // Sys_CloseFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
image_file_handles.clear();
|
||||||
|
//sp::image_file_unk_map.clear();
|
||||||
|
mp::image_file_unk_map.clear();
|
||||||
|
image_file_allocator.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_handle(const std::string& fastfile)
|
||||||
|
{
|
||||||
|
if (!image_file_handles.contains(fastfile))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto handle = image_file_handles[fastfile];
|
||||||
|
if (handle != nullptr)
|
||||||
|
{
|
||||||
|
utils::hook::invoke<void>(0x140608A20, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
image_file_handles.erase(fastfile);
|
||||||
|
if (game::environment::is_sp())
|
||||||
|
{
|
||||||
|
//image_file_allocator.free(sp::image_file_unk_map[fastfile]);
|
||||||
|
//sp::image_file_unk_map.erase(fastfile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image_file_allocator.free(mp::image_file_unk_map[fastfile]);
|
||||||
|
mp::image_file_unk_map.erase(fastfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
if (!game::environment::is_mp())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::hook::jump(SELECT_VALUE(0x0, 0x140279C79),
|
||||||
|
utils::hook::assemble(db_create_gfx_image_stream_stub), true);
|
||||||
|
utils::hook::call(SELECT_VALUE(0x0, 0x140279CBD), pakfile_open_stub);
|
||||||
|
utils::hook::call(SELECT_VALUE(0x0, 0x140279C9F), com_sprintf_stub);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(imagefiles::component)
|
7
src/client/component/imagefiles.hpp
Normal file
7
src/client/component/imagefiles.hpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace imagefiles
|
||||||
|
{
|
||||||
|
void close_custom_handles();
|
||||||
|
void close_handle(const std::string& fastfile);
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include "party.hpp"
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
@ -10,15 +11,57 @@ namespace logger
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
utils::hook::detour com_error_hook;
|
||||||
|
|
||||||
game::dvar_t* logger_dev = nullptr;
|
game::dvar_t* logger_dev = nullptr;
|
||||||
|
|
||||||
|
void print_error(const char* msg, ...)
|
||||||
|
{
|
||||||
|
char buffer[2048]{};
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, msg);
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
console::error("%s", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_com_error(int, const char* msg, ...)
|
||||||
|
{
|
||||||
|
char buffer[2048]{};
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, msg);
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
console::error("%s", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void com_error_stub(const int error, const char* msg, ...)
|
||||||
|
{
|
||||||
|
char buffer[2048]{};
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, msg);
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
console::error("Error: %s\n", buffer);
|
||||||
|
|
||||||
|
party::clear_sv_motd(); // clear sv_motd on error if it exists
|
||||||
|
|
||||||
|
com_error_hook.invoke<void>(error, "%s", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
void print_warning(const char* msg, ...)
|
void print_warning(const char* msg, ...)
|
||||||
{
|
{
|
||||||
char buffer[2048]{};
|
char buffer[2048]{};
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
console::warn("%s", buffer);
|
console::warn("%s", buffer);
|
||||||
@ -30,7 +73,7 @@ namespace logger
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
console::info("%s", buffer);
|
console::info("%s", buffer);
|
||||||
@ -47,7 +90,7 @@ namespace logger
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
console::info("%s", buffer);
|
console::info("%s", buffer);
|
||||||
@ -114,6 +157,13 @@ namespace logger
|
|||||||
utils::hook::jump(0x140701A1C, print);
|
utils::hook::jump(0x140701A1C, print);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!game::environment::is_sp())
|
||||||
|
{
|
||||||
|
utils::hook::call(0x1404D8543, print_com_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
com_error_hook.create(game::Com_Error, com_error_stub);
|
||||||
|
|
||||||
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED);
|
logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -29,7 +29,7 @@ namespace lui
|
|||||||
{
|
{
|
||||||
if (params.size() <= 1)
|
if (params.size() <= 1)
|
||||||
{
|
{
|
||||||
console::info("USAGE: %s <name>\n", params.get(0));
|
console::info("usage: lui_open <name>\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ namespace lui
|
|||||||
{
|
{
|
||||||
if (params.size() <= 1)
|
if (params.size() <= 1)
|
||||||
{
|
{
|
||||||
console::info("USAGE: %s <name>\n", params.get(0));
|
console::info("usage: lui_open_popup <name>\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
212
src/client/component/mapents.cpp
Normal file
212
src/client/component/mapents.cpp
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "console.hpp"
|
||||||
|
#include "filesystem.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
#include "gsc/script_loading.hpp"
|
||||||
|
|
||||||
|
namespace mapents
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::optional<std::string> parse_mapents(const std::string& source)
|
||||||
|
{
|
||||||
|
std::string out_buffer{};
|
||||||
|
|
||||||
|
const auto lines = utils::string::split(source, '\n');
|
||||||
|
auto in_map_ent = false;
|
||||||
|
auto empty = false;
|
||||||
|
auto in_comment = false;
|
||||||
|
|
||||||
|
for (auto i = 0; i < lines.size(); i++)
|
||||||
|
{
|
||||||
|
auto line_num = i+1;
|
||||||
|
auto line = lines[i];
|
||||||
|
if (line.ends_with('\r'))
|
||||||
|
{
|
||||||
|
line.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.starts_with("/*"))
|
||||||
|
{
|
||||||
|
in_comment = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.ends_with("*/"))
|
||||||
|
{
|
||||||
|
in_comment = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_comment)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.starts_with("//"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line[0] == '{' && !in_map_ent)
|
||||||
|
{
|
||||||
|
in_map_ent = true;
|
||||||
|
out_buffer.append("{\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line[0] == '{' && in_map_ent)
|
||||||
|
{
|
||||||
|
console::error("[map_ents parser] Unexpected '{' on line %i\n", line_num);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line[0] == '}' && in_map_ent)
|
||||||
|
{
|
||||||
|
if (empty)
|
||||||
|
{
|
||||||
|
out_buffer.append("\n}\n");
|
||||||
|
}
|
||||||
|
else if (i < static_cast<int>(lines.size()) - 1)
|
||||||
|
{
|
||||||
|
out_buffer.append("}\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out_buffer.append("}\0");
|
||||||
|
}
|
||||||
|
|
||||||
|
in_map_ent = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line[0] == '}' && !in_map_ent)
|
||||||
|
{
|
||||||
|
console::error("[map_ents parser] Unexpected '}' on line %i\n", line_num);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::regex expr(R"~((.+) "(.*)")~");
|
||||||
|
std::smatch match{};
|
||||||
|
if (!std::regex_search(line, match, expr) && !line.empty())
|
||||||
|
{
|
||||||
|
console::warn("[map_ents parser] Failed to parse line %i (%s)\n", line_num, line.data());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto key = utils::string::to_lower(match[1].str());
|
||||||
|
const auto value = match[2].str();
|
||||||
|
|
||||||
|
if (key.size() <= 0)
|
||||||
|
{
|
||||||
|
console::warn("[map_ents parser] Invalid key ('%s') on line %i (%s)\n", key.data(), line_num, line.data());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.size() <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
empty = false;
|
||||||
|
|
||||||
|
if (utils::string::is_numeric(key) || key.size() < 3 || !key.starts_with("\"") || !key.ends_with("\""))
|
||||||
|
{
|
||||||
|
out_buffer.append(line);
|
||||||
|
out_buffer.append("\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key_ = key.substr(1, key.size() - 2);
|
||||||
|
const auto id = gsc::gsc_ctx->token_id(key_);
|
||||||
|
if (id == 0)
|
||||||
|
{
|
||||||
|
console::warn("[map_ents parser] Key '%s' not found, on line %i (%s)\n", key_.data(), line_num, line.data());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_buffer.append(utils::string::va("%i \"%s\"\n", id, value.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {out_buffer};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string raw_ents;
|
||||||
|
bool load_raw_mapents()
|
||||||
|
{
|
||||||
|
auto mapents_name = utils::string::va("%s.ents", **reinterpret_cast<const char***>(SELECT_VALUE(0x0, 0x1479E6940)));
|
||||||
|
if (filesystem::exists(mapents_name))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
console::info("Reading raw ents file \"%s\"\n", mapents_name);
|
||||||
|
raw_ents = filesystem::read_file(mapents_name);
|
||||||
|
if (!raw_ents.empty())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
console::error("Failed to read raw ents file \"%s\"\n%s\n", mapents_name, ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string entity_string;
|
||||||
|
const char* cm_entity_string_stub()
|
||||||
|
{
|
||||||
|
const char* ents = nullptr;
|
||||||
|
if (load_raw_mapents())
|
||||||
|
{
|
||||||
|
ents = raw_ents.data();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!entity_string.empty())
|
||||||
|
{
|
||||||
|
return entity_string.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
ents = utils::hook::invoke<const char*>(SELECT_VALUE(0x0, 0x1403A1F30));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto parsed = parse_mapents(ents);
|
||||||
|
if (parsed.has_value())
|
||||||
|
{
|
||||||
|
entity_string = parsed.value();
|
||||||
|
return entity_string.data();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cm_unload_stub(void* clip_map)
|
||||||
|
{
|
||||||
|
entity_string.clear();
|
||||||
|
raw_ents.clear();
|
||||||
|
utils::hook::invoke<void>(SELECT_VALUE(0x0, 0x1403A1ED0), clip_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
utils::hook::call(SELECT_VALUE(0x0, 0x1402F5A54), cm_entity_string_stub);
|
||||||
|
utils::hook::call(SELECT_VALUE(0x0, 0x14026BF54), cm_unload_stub);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(mapents::component)
|
@ -1,7 +1,6 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "game/engine/sv_game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
#include "party.hpp"
|
#include "party.hpp"
|
||||||
@ -160,7 +159,7 @@ namespace party
|
|||||||
if (game::mp::g_entities[i].client)
|
if (game::mp::g_entities[i].client)
|
||||||
{
|
{
|
||||||
char client_name[16] = {0};
|
char client_name[16] = {0};
|
||||||
game::I_strncpyz(client_name, game::mp::g_entities[i].client->name, sizeof(client_name));
|
strncpy_s(client_name, game::mp::g_entities[i].client->name, sizeof(client_name));
|
||||||
game::I_CleanStr(client_name);
|
game::I_CleanStr(client_name);
|
||||||
|
|
||||||
if (client_name == name)
|
if (client_name == name)
|
||||||
@ -227,6 +226,64 @@ namespace party
|
|||||||
network::send(target, "getInfo", connect_state.challenge);
|
network::send(target, "getInfo", connect_state.challenge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void start_map(const std::string& mapname)
|
||||||
|
{
|
||||||
|
if (game::Live_SyncOnlineDataFlags(0) > 32)
|
||||||
|
{
|
||||||
|
scheduler::once([=]()
|
||||||
|
{
|
||||||
|
command::execute("map " + mapname, false);
|
||||||
|
}, scheduler::pipeline::main, 1s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!game::SV_MapExists(mapname.data()))
|
||||||
|
{
|
||||||
|
console::info("Map '%s' doesn't exist.\n", mapname.data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* current_mapname = game::Dvar_FindVar("mapname");
|
||||||
|
if (current_mapname && utils::string::to_lower(current_mapname->current.string) ==
|
||||||
|
utils::string::to_lower(mapname) && (game::SV_Loaded() && !game::VirtualLobby_Loaded()))
|
||||||
|
{
|
||||||
|
console::info("Restarting map: %s\n", mapname.data());
|
||||||
|
command::execute("map_restart", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!game::environment::is_dedi())
|
||||||
|
{
|
||||||
|
if (game::SV_Loaded())
|
||||||
|
{
|
||||||
|
const auto* args = "Leave";
|
||||||
|
game::UI_RunMenuScript(0, &args);
|
||||||
|
}
|
||||||
|
|
||||||
|
perform_game_initialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
console::info("Starting map: %s\n", mapname.data());
|
||||||
|
|
||||||
|
auto* gametype = game::Dvar_FindVar("g_gametype");
|
||||||
|
if (gametype && gametype->current.string)
|
||||||
|
{
|
||||||
|
command::execute(utils::string::va("ui_gametype %s", gametype->current.string), true);
|
||||||
|
}
|
||||||
|
command::execute(utils::string::va("ui_mapname %s", mapname.data()), true);
|
||||||
|
|
||||||
|
/*auto* maxclients = game::Dvar_FindVar("sv_maxclients");
|
||||||
|
if (maxclients)
|
||||||
|
{
|
||||||
|
command::execute(utils::string::va("ui_maxclients %i", maxclients->current.integer), true);
|
||||||
|
command::execute(utils::string::va("party_maxplayers %i", maxclients->current.integer), true);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
const auto* args = "StartServer";
|
||||||
|
game::UI_RunMenuScript(0, &args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int server_client_count()
|
int server_client_count()
|
||||||
{
|
{
|
||||||
return sv_maxclients;
|
return sv_maxclients;
|
||||||
@ -257,6 +314,36 @@ namespace party
|
|||||||
// enable custom kick reason in GScr_KickPlayer
|
// enable custom kick reason in GScr_KickPlayer
|
||||||
utils::hook::set<uint8_t>(0x14032ED80, 0xEB);
|
utils::hook::set<uint8_t>(0x14032ED80, 0xEB);
|
||||||
|
|
||||||
|
command::add("map", [](const command::params& argument)
|
||||||
|
{
|
||||||
|
if (argument.size() != 2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_map(argument[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
command::add("map_restart", []()
|
||||||
|
{
|
||||||
|
if (!game::SV_Loaded() || game::VirtualLobby_Loaded())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*reinterpret_cast<int*>(0x1488692B0) = 1; // sv_map_restart
|
||||||
|
*reinterpret_cast<int*>(0x1488692B4) = 1; // sv_loadScripts
|
||||||
|
*reinterpret_cast<int*>(0x1488692B8) = 0; // sv_migrate
|
||||||
|
reinterpret_cast<void(*)(int)>(0x140437460)(0); // SV_CheckLoadGame
|
||||||
|
});
|
||||||
|
|
||||||
|
command::add("fast_restart", []()
|
||||||
|
{
|
||||||
|
if (game::SV_Loaded() && !game::VirtualLobby_Loaded())
|
||||||
|
{
|
||||||
|
game::SV_FastRestart(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
command::add("reconnect", [](const command::params& argument)
|
command::add("reconnect", [](const command::params& argument)
|
||||||
{
|
{
|
||||||
if (!connect_state.hostDefined)
|
if (!connect_state.hostDefined)
|
||||||
@ -355,7 +442,7 @@ namespace party
|
|||||||
{
|
{
|
||||||
scheduler::once([i, reason]
|
scheduler::once([i, reason]
|
||||||
{
|
{
|
||||||
game::SV_KickClientNum(i, reason.c_str());
|
game::SV_KickClientNum(i, reason.data());
|
||||||
}, scheduler::pipeline::server);
|
}, scheduler::pipeline::server);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -369,7 +456,7 @@ namespace party
|
|||||||
|
|
||||||
scheduler::once([client_num, reason]()
|
scheduler::once([client_num, reason]()
|
||||||
{
|
{
|
||||||
game::SV_KickClientNum(client_num, reason.c_str());
|
game::SV_KickClientNum(client_num, reason.data());
|
||||||
}, scheduler::pipeline::server);
|
}, scheduler::pipeline::server);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -389,8 +476,9 @@ namespace party
|
|||||||
const auto message = params.join(2);
|
const auto message = params.join(2);
|
||||||
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
|
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(static_cast<char>(client_num), game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.c_str()));
|
game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE,
|
||||||
printf("%s -> %i: %s\n", name, client_num, message.c_str());
|
utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
|
||||||
|
printf("%s -> %i: %s\n", name, client_num, message.data());
|
||||||
});
|
});
|
||||||
|
|
||||||
command::add("tellraw", [](const command::params& params)
|
command::add("tellraw", [](const command::params& params)
|
||||||
@ -403,8 +491,9 @@ namespace party
|
|||||||
const auto client_num = atoi(params.get(1));
|
const auto client_num = atoi(params.get(1));
|
||||||
const auto message = params.join(2);
|
const auto message = params.join(2);
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(static_cast<char>(client_num), game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.c_str()));
|
game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE,
|
||||||
printf("%i: %s\n", client_num, message.c_str());
|
utils::string::va("%c \"%s\"", 84, message.data()));
|
||||||
|
printf("%i: %s\n", client_num, message.data());
|
||||||
});
|
});
|
||||||
|
|
||||||
command::add("say", [](const command::params& params)
|
command::add("say", [](const command::params& params)
|
||||||
@ -417,8 +506,9 @@ namespace party
|
|||||||
const auto message = params.join(1);
|
const auto message = params.join(1);
|
||||||
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
|
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.c_str()));
|
game::SV_GameSendServerCommand(
|
||||||
printf("%s: %s\n", name, message.c_str());
|
-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
|
||||||
|
printf("%s: %s\n", name, message.data());
|
||||||
});
|
});
|
||||||
|
|
||||||
command::add("sayraw", [](const command::params& params)
|
command::add("sayraw", [](const command::params& params)
|
||||||
@ -430,7 +520,8 @@ namespace party
|
|||||||
|
|
||||||
const auto message = params.join(1);
|
const auto message = params.join(1);
|
||||||
|
|
||||||
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.c_str()));
|
game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE,
|
||||||
|
utils::string::va("%c \"%s\"", 84, message.data()));
|
||||||
printf("%s\n", message.data());
|
printf("%s\n", message.data());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ namespace party
|
|||||||
void reset_connect_state();
|
void reset_connect_state();
|
||||||
|
|
||||||
void connect(const game::netadr_s& target);
|
void connect(const game::netadr_s& target);
|
||||||
|
void start_map(const std::string& mapname);
|
||||||
|
|
||||||
void clear_sv_motd();
|
void clear_sv_motd();
|
||||||
int server_client_count();
|
int server_client_count();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "game/engine/sv_game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
@ -20,8 +19,6 @@ namespace patches
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
utils::hook::detour com_quit_f_hook;
|
|
||||||
utils::hook::detour sv_shutdown_hook;
|
|
||||||
utils::hook::detour live_get_local_client_name_hook;
|
utils::hook::detour live_get_local_client_name_hook;
|
||||||
|
|
||||||
const char* live_get_local_client_name()
|
const char* live_get_local_client_name()
|
||||||
@ -118,7 +115,7 @@ namespace patches
|
|||||||
char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
|
char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
|
||||||
{
|
{
|
||||||
std::string file_name = filename;
|
std::string file_name = filename;
|
||||||
if (!file_name.ends_with(".cfg"))
|
if (file_name.find(".cfg") == std::string::npos)
|
||||||
{
|
{
|
||||||
file_name.append(".cfg");
|
file_name.append(".cfg");
|
||||||
}
|
}
|
||||||
@ -127,7 +124,6 @@ namespace patches
|
|||||||
if (file.exists())
|
if (file.exists())
|
||||||
{
|
{
|
||||||
snprintf(buf, size, "%s\n", file.get_buffer().data());
|
snprintf(buf, size, "%s\n", file.get_buffer().data());
|
||||||
buf[size - 1] = '\0';
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,27 +178,13 @@ namespace patches
|
|||||||
const auto client = &game::mp::svs_clients[ent->s.number];
|
const auto client = &game::mp::svs_clients[ent->s.number];
|
||||||
|
|
||||||
// 22 => "end_game"
|
// 22 => "end_game"
|
||||||
if (menu_id == 22 && client->header.netchan.remoteAddress.type != game::NA_LOOPBACK)
|
if (menu_id == 22 && client->header.remoteAddress.type != game::NA_LOOPBACK)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_lui_notify_server_hook.invoke<void>(ent);
|
cmd_lui_notify_server_hook.invoke<void>(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sv_shutdown_stub(const char* finalmsg)
|
|
||||||
{
|
|
||||||
console::info("----- Server Shutdown -----\n");
|
|
||||||
|
|
||||||
sv_shutdown_hook.invoke<void>(finalmsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void com_quit_f_stub()
|
|
||||||
{
|
|
||||||
console::info("quitting...\n");
|
|
||||||
|
|
||||||
com_quit_f_hook.invoke<void>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -257,10 +239,6 @@ namespace patches
|
|||||||
|
|
||||||
static void patch_mp()
|
static void patch_mp()
|
||||||
{
|
{
|
||||||
// Bypass Arxan function
|
|
||||||
utils::hook::nop(0x14043E120, 9);
|
|
||||||
utils::hook::jump(0x14043E120, game::engine::SV_GameSendServerCommand);
|
|
||||||
|
|
||||||
// Use name dvar
|
// Use name dvar
|
||||||
live_get_local_client_name_hook.create(0x1404D47F0, &live_get_local_client_name);
|
live_get_local_client_name_hook.create(0x1404D47F0, &live_get_local_client_name);
|
||||||
|
|
||||||
@ -328,14 +306,8 @@ namespace patches
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
// Disable Com_Error in NET_SendPacket
|
|
||||||
utils::hook::nop(0x1404D8543, 5);
|
|
||||||
|
|
||||||
// Prevent clients from ending the game as non host by sending 'end_game' lui notification
|
// 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);
|
cmd_lui_notify_server_hook.create(0x1402E9390, cmd_lui_notify_server_stub);
|
||||||
|
|
||||||
com_quit_f_hook.create(0x1403D08C0, com_quit_f_stub);
|
|
||||||
sv_shutdown_hook.create(0x140440170, sv_shutdown_stub);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void patch_sp()
|
static void patch_sp()
|
||||||
|
@ -86,7 +86,7 @@ namespace rcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
char clean_name[32]{};
|
char clean_name[32]{};
|
||||||
game::I_strncpyz(clean_name, client->name, sizeof(clean_name));
|
strncpy_s(clean_name, client->name, _TRUNCATE);
|
||||||
game::I_CleanStr(clean_name);
|
game::I_CleanStr(clean_name);
|
||||||
|
|
||||||
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
|
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
|
||||||
@ -96,8 +96,8 @@ namespace rcon
|
|||||||
(client->header.state == 2) ? "CNCT" : (client->header.state == 1) ? "ZMBI" : utils::string::va("%4i", game::SV_GetClientPing(i)),
|
(client->header.state == 2) ? "CNCT" : (client->header.state == 1) ? "ZMBI" : utils::string::va("%4i", game::SV_GetClientPing(i)),
|
||||||
game::SV_GetGuid(i),
|
game::SV_GetGuid(i),
|
||||||
clean_name,
|
clean_name,
|
||||||
network::net_adr_to_string(client->header.netchan.remoteAddress),
|
network::net_adr_to_string(client->header.remoteAddress),
|
||||||
client->header.netchan.remoteAddress.port)
|
client->header.remoteAddress.port)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,36 +84,6 @@ namespace security
|
|||||||
|
|
||||||
ui_replace_directive_hook.invoke<void>(local_client_num, src_string, dst_string, dst_buffer_size);
|
ui_replace_directive_hook.invoke<void>(local_client_num, src_string, dst_string, dst_buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int hud_elem_set_enum_string_stub(char* string, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
const auto len = vsnprintf(string, 0x800, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sv_add_bot_stub(char* string, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
const auto len = vsnprintf(string, 0x400, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sv_add_test_client_stub(char* string, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
const auto len = vsnprintf(string, 0x400, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -121,18 +91,11 @@ namespace security
|
|||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
{
|
{
|
||||||
// sprinf
|
|
||||||
utils::hook::call(SELECT_VALUE(0x1402203DF, 0x1402F0BAF), hud_elem_set_enum_string_stub);
|
|
||||||
|
|
||||||
if (game::environment::is_sp()) return;
|
if (game::environment::is_sp()) return;
|
||||||
|
|
||||||
// Patch vulnerability in PlayerCards_SetCachedPlayerData
|
// Patch vulnerability in PlayerCards_SetCachedPlayerData
|
||||||
utils::hook::call(0x1401BB909, set_cached_playerdata_stub);
|
utils::hook::call(0x1401BB909, set_cached_playerdata_stub);
|
||||||
|
|
||||||
// sprinf
|
|
||||||
utils::hook::call(0x140439075, sv_add_bot_stub);
|
|
||||||
utils::hook::call(0x14043932A, sv_add_test_client_stub);
|
|
||||||
|
|
||||||
// Patch entity overflow
|
// Patch entity overflow
|
||||||
utils::hook::jump(0x14044DE3A, assemble(remap_cached_entities_stub), true);
|
utils::hook::jump(0x14044DE3A, assemble(remap_cached_entities_stub), true);
|
||||||
|
|
||||||
|
@ -26,8 +26,6 @@ namespace steam_proxy
|
|||||||
ownership_state state_;
|
ownership_state state_;
|
||||||
|
|
||||||
utils::binary_resource runner_file(RUNNER, "s1-mod-runner.exe");
|
utils::binary_resource runner_file(RUNNER, "s1-mod-runner.exe");
|
||||||
|
|
||||||
bool is_disabled() { return true; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -35,7 +33,7 @@ namespace steam_proxy
|
|||||||
public:
|
public:
|
||||||
void post_load() override
|
void post_load() override
|
||||||
{
|
{
|
||||||
if (game::environment::is_dedi() || is_disabled())
|
if (game::environment::is_dedi())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -206,27 +206,6 @@ namespace ui_scripting
|
|||||||
setup_functions();
|
setup_functions();
|
||||||
|
|
||||||
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x14007CB70));
|
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x14007CB70));
|
||||||
|
|
||||||
lua["directoryexists"] = [](const std::string& string)
|
|
||||||
{
|
|
||||||
return utils::io::directory_exists(string);
|
|
||||||
};
|
|
||||||
|
|
||||||
lua["listfiles"] = [](const std::string& string)
|
|
||||||
{
|
|
||||||
return utils::io::list_files(string);
|
|
||||||
};
|
|
||||||
|
|
||||||
lua["directoryisempty"] = [](const std::string& string)
|
|
||||||
{
|
|
||||||
return utils::io::directory_is_empty(string);
|
|
||||||
};
|
|
||||||
|
|
||||||
lua["fileexists"] = [](const std::string& string)
|
|
||||||
{
|
|
||||||
return utils::io::file_exists(string);
|
|
||||||
};
|
|
||||||
|
|
||||||
lua["table"]["unpack"] = lua["unpack"];
|
lua["table"]["unpack"] = lua["unpack"];
|
||||||
lua["luiglobals"] = lua;
|
lua["luiglobals"] = lua;
|
||||||
|
|
||||||
|
113
src/client/component/weapon.cpp
Normal file
113
src/client/component/weapon.cpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "console.hpp"
|
||||||
|
#include "fastfiles.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/memory.hpp>
|
||||||
|
|
||||||
|
namespace weapon
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
utils::hook::detour g_setup_level_weapon_def_hook;
|
||||||
|
void g_setup_level_weapon_def_stub()
|
||||||
|
{
|
||||||
|
// precache level weapons first
|
||||||
|
g_setup_level_weapon_def_hook.invoke<void>();
|
||||||
|
|
||||||
|
std::vector<game::WeaponDef*> weapons;
|
||||||
|
|
||||||
|
// find all weapons in asset pools
|
||||||
|
fastfiles::enum_assets(game::ASSET_TYPE_WEAPON, [&weapons](game::XAssetHeader header)
|
||||||
|
{
|
||||||
|
weapons.push_back(reinterpret_cast<game::WeaponDef*>(header.data));
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
// sort weapons
|
||||||
|
std::sort(weapons.begin(), weapons.end(), [](game::WeaponDef* weapon1, game::WeaponDef* weapon2)
|
||||||
|
{
|
||||||
|
return std::string_view(weapon1->name) <
|
||||||
|
std::string_view(weapon2->name);
|
||||||
|
});
|
||||||
|
|
||||||
|
// precache items
|
||||||
|
for (std::size_t i = 0; i < weapons.size(); i++)
|
||||||
|
{
|
||||||
|
//console::info("precaching weapon \"%s\"\n", weapons[i]->name);
|
||||||
|
game::G_GetWeaponForName(weapons[i]->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::hook::detour xmodel_get_bone_index_hook;
|
||||||
|
int xmodel_get_bone_index_stub(game::XModel* model, game::scr_string_t name, unsigned int offset, char* index)
|
||||||
|
{
|
||||||
|
auto result = xmodel_get_bone_index_hook.invoke<int>(model, name, offset, index);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto original_index = *index;
|
||||||
|
const auto original_result = result;
|
||||||
|
|
||||||
|
if (name == game::SL_FindString("tag_weapon_right") ||
|
||||||
|
name == game::SL_FindString("tag_knife_attach"))
|
||||||
|
{
|
||||||
|
const auto tag_weapon = game::SL_FindString("tag_weapon");
|
||||||
|
result = xmodel_get_bone_index_hook.invoke<int>(model, tag_weapon, offset, index);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
console::info("using tag_weapon instead of %s (%s, %d, %d)\n", game::SL_ConvertToString(name), model->name, offset, *index);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*index = original_index;
|
||||||
|
result = original_result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cw_mismatch_error_stub(int, const char* msg, ...)
|
||||||
|
{
|
||||||
|
char buffer[0x100];
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, msg);
|
||||||
|
|
||||||
|
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
console::error(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
if (!game::environment::is_mp())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// precache all weapons that are loaded in zones
|
||||||
|
g_setup_level_weapon_def_hook.create(0x140340DE0, g_setup_level_weapon_def_stub);
|
||||||
|
|
||||||
|
// use tag_weapon if tag_weapon_right or tag_knife_attach are not found on model
|
||||||
|
xmodel_get_bone_index_hook.create(0x1404E2A50, xmodel_get_bone_index_stub);
|
||||||
|
|
||||||
|
// make custom weapon index mismatch not drop in CG_SetupCustomWeapon
|
||||||
|
utils::hook::call(0x1401E973D, cw_mismatch_error_stub);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(weapon::component)
|
@ -1,181 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include <game/game.hpp>
|
|
||||||
|
|
||||||
#include "sv_game.hpp"
|
|
||||||
|
|
||||||
#include <component/console.hpp>
|
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
|
|
||||||
namespace game::engine
|
|
||||||
{
|
|
||||||
char* SV_ExpandNewlines(char* in)
|
|
||||||
{
|
|
||||||
static char string[1024];
|
|
||||||
|
|
||||||
unsigned int l = 0;
|
|
||||||
while (*in && l < sizeof(string) - 3)
|
|
||||||
{
|
|
||||||
if (*in == '\n')
|
|
||||||
{
|
|
||||||
string[l++] = '\\';
|
|
||||||
string[l++] = 'n';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (*in != '\x14' && *in != '\x15')
|
|
||||||
{
|
|
||||||
string[l++] = *in;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
++in;
|
|
||||||
}
|
|
||||||
|
|
||||||
string[l] = '\0';
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SV_CullIgnorableServerCommands(mp::client_t* client)
|
|
||||||
{
|
|
||||||
int to = client->reliableSent + 1;
|
|
||||||
for (int from = to; from <= client->reliableSequence; ++from)
|
|
||||||
{
|
|
||||||
int from_index = from & 0x7F;
|
|
||||||
assert(client->netBuf.reliableCommandInfo[from_index].time >= 0);
|
|
||||||
if (client->netBuf.reliableCommandInfo[from_index].type)
|
|
||||||
{
|
|
||||||
int to_index = to & 0x7F;
|
|
||||||
if (to_index != from_index)
|
|
||||||
{
|
|
||||||
client->netBuf.reliableCommandInfo[to_index] = client->netBuf.reliableCommandInfo[from_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
++to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client->reliableSequence = to - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SV_DelayDropClient(mp::client_t* drop, const char* reason)
|
|
||||||
{
|
|
||||||
assert(drop);
|
|
||||||
assert(reason);
|
|
||||||
assert(drop->header.state != mp::CS_FREE);
|
|
||||||
if (drop->header.state == mp::CS_ZOMBIE)
|
|
||||||
{
|
|
||||||
#ifdef _DEBUG
|
|
||||||
console::info("(drop->dropReason) = %s", drop->dropReason);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (!drop->dropReason)
|
|
||||||
{
|
|
||||||
drop->dropReason = reason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SV_AddServerCommand(mp::client_t* client, svscmd_type type, const char* cmd)
|
|
||||||
{
|
|
||||||
static_assert(offsetof(mp::client_t, netBuf.reliableCommandInfo[0].cmd) == 0xC44);
|
|
||||||
|
|
||||||
if (client->testClient == TC_BOT)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client->reliableSequence - client->reliableAcknowledge < 64 && client->header.state == mp::CS_ACTIVE || (SV_CullIgnorableServerCommands(client), type))
|
|
||||||
{
|
|
||||||
int len = static_cast<int>(std::strlen(cmd)) + 1;
|
|
||||||
int to = SV_CanReplaceServerCommand(client, reinterpret_cast<const unsigned char*>(cmd), len);
|
|
||||||
if (to < 0)
|
|
||||||
{
|
|
||||||
++client->reliableSequence;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int from = to + 1;
|
|
||||||
while (from <= client->reliableSequence)
|
|
||||||
{
|
|
||||||
client->netBuf.reliableCommandInfo[to & 0x7F] = client->netBuf.reliableCommandInfo[from & 0x7F];
|
|
||||||
++from;
|
|
||||||
++to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client->reliableSequence - client->reliableAcknowledge == 129)
|
|
||||||
{
|
|
||||||
#ifdef _DEBUG
|
|
||||||
console::info("===== pending server commands =====\n");
|
|
||||||
int i = 0;
|
|
||||||
for (i = client->reliableAcknowledge + 1; i <= client->reliableSequence; ++i)
|
|
||||||
{
|
|
||||||
console::info("cmd %5d: %8d: %s\n", i, client->netBuf.reliableCommandInfo[i & 0x7F].time, client->netBuf.reliableCommandInfo[i & 0x7F].cmd);
|
|
||||||
}
|
|
||||||
console::info("cmd %5d: %8d: %s\n", i, *game::mp::serverTime, cmd);
|
|
||||||
#endif
|
|
||||||
NET_OutOfBandPrint(NS_SERVER, &client->header.netchan.remoteAddress, "disconnect");
|
|
||||||
SV_DelayDropClient(client, "EXE_SERVERCOMMANDOVERFLOW");
|
|
||||||
type = SV_CMD_RELIABLE;
|
|
||||||
cmd = utils::string::va("%c \"EXE_SERVERCOMMANDOVERFLOW\"", 'r');
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = client->reliableSequence & 0x7F;
|
|
||||||
MSG_WriteReliableCommandToBuffer(cmd, client->netBuf.reliableCommandInfo[index].cmd, sizeof(client->netBuf.reliableCommandInfo[index].cmd));
|
|
||||||
client->netBuf.reliableCommandInfo[index].time = *game::mp::serverTime;
|
|
||||||
client->netBuf.reliableCommandInfo[index].type = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...)
|
|
||||||
{
|
|
||||||
mp::client_t* client;
|
|
||||||
int j, len;
|
|
||||||
va_list va;
|
|
||||||
|
|
||||||
const auto server_command_buf_large = std::make_unique<char[]>(0x20000);
|
|
||||||
|
|
||||||
va_start(va, fmt);
|
|
||||||
len = vsnprintf(server_command_buf_large.get(), 0x20000, fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
|
|
||||||
assert(len >= 0);
|
|
||||||
|
|
||||||
if (cl)
|
|
||||||
{
|
|
||||||
SV_AddServerCommand(cl, type, server_command_buf_large.get());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (environment::is_dedi() && !std::strncmp(server_command_buf_large.get(), "print", 5))
|
|
||||||
{
|
|
||||||
console::info("broadcast: %s\n", SV_ExpandNewlines(server_command_buf_large.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
|
|
||||||
for (j = 0, client = mp::svs_clients; j < sv_maxclients->current.integer; j++, client++)
|
|
||||||
{
|
|
||||||
if (client->header.state < mp::CS_CLIENTLOADING)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SV_AddServerCommand(client, type, server_command_buf_large.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SV_GameSendServerCommand(char clientNum, svscmd_type type, const char* text)
|
|
||||||
{
|
|
||||||
[[maybe_unused]] const auto* sv_maxclients = Dvar_FindVar("sv_maxclients");
|
|
||||||
|
|
||||||
if (clientNum == -1)
|
|
||||||
{
|
|
||||||
SV_SendServerCommand(nullptr, type, "%s", text);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(sv_maxclients->current.integer >= 1 && sv_maxclients->current.integer <= 18);
|
|
||||||
assert(static_cast<unsigned>(clientNum) < sv_maxclients->current.unsignedInt);
|
|
||||||
SV_SendServerCommand(&mp::svs_clients[clientNum], type, "%s", text);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace game::engine
|
|
||||||
{
|
|
||||||
void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...);
|
|
||||||
void SV_GameSendServerCommand(char clientNum, svscmd_type type, const char* text);
|
|
||||||
}
|
|
@ -855,11 +855,10 @@ namespace game
|
|||||||
int readcount;
|
int readcount;
|
||||||
int bit;
|
int bit;
|
||||||
int lastEntityRef;
|
int lastEntityRef;
|
||||||
|
netsrc_t targetLocalNetID;
|
||||||
int useZlib;
|
int useZlib;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(msg_t) == 0x38);
|
|
||||||
|
|
||||||
enum errorParm
|
enum errorParm
|
||||||
{
|
{
|
||||||
ERR_FATAL = 0,
|
ERR_FATAL = 0,
|
||||||
@ -1417,44 +1416,8 @@ namespace game
|
|||||||
|
|
||||||
static_assert(sizeof(pml_t) == 0x130);
|
static_assert(sizeof(pml_t) == 0x130);
|
||||||
|
|
||||||
struct netProfilePacket_t
|
|
||||||
{
|
|
||||||
int iTime;
|
|
||||||
int iSize;
|
|
||||||
int bFragment;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct netProfileStream_t
|
|
||||||
{
|
|
||||||
netProfilePacket_t packets[60];
|
|
||||||
int iCurrPacket;
|
|
||||||
int iBytesPerSecond;
|
|
||||||
int iLastBPSCalcTime;
|
|
||||||
int iCountedPackets;
|
|
||||||
int iCountedFragments;
|
|
||||||
int iFragmentPercentage;
|
|
||||||
int iLargestPacket;
|
|
||||||
int iSmallestPacket;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct netProfileInfo_t
|
|
||||||
{
|
|
||||||
netProfileStream_t send;
|
|
||||||
netProfileStream_t recieve;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace mp
|
namespace mp
|
||||||
{
|
{
|
||||||
enum
|
|
||||||
{
|
|
||||||
CS_FREE = 0x0,
|
|
||||||
CS_ZOMBIE = 0x1,
|
|
||||||
CS_RECONNECTING = 0x2,
|
|
||||||
CS_CONNECTED = 0x3,
|
|
||||||
CS_CLIENTLOADING = 0x4,
|
|
||||||
CS_ACTIVE = 0x5,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cachedSnapshot_t
|
struct cachedSnapshot_t
|
||||||
{
|
{
|
||||||
int archivedFrame;
|
int archivedFrame;
|
||||||
@ -1507,141 +1470,33 @@ namespace game
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
struct netchan_t
|
|
||||||
{
|
|
||||||
int outgoingSequence;
|
|
||||||
netsrc_t sock;
|
|
||||||
int dropped;
|
|
||||||
int incomingSequence;
|
|
||||||
netadr_s remoteAddress;
|
|
||||||
int fragmentSequence;
|
|
||||||
int fragmentLength;
|
|
||||||
char* fragmentBuffer;
|
|
||||||
int fragmentBufferSize;
|
|
||||||
int unsentFragments;
|
|
||||||
int unsentFragmentStart;
|
|
||||||
int unsentLength;
|
|
||||||
char* unsentBuffer;
|
|
||||||
int unsentBufferSize;
|
|
||||||
netProfileInfo_t prof;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct clientHeader_t
|
struct clientHeader_t
|
||||||
{
|
{
|
||||||
int state;
|
int state;
|
||||||
int sendAsActive;
|
char __pad0[36];
|
||||||
int deltaMessage;
|
netadr_s remoteAddress;
|
||||||
int rateDelayed;
|
}; // size = ?
|
||||||
int hasAckedBaselineData;
|
|
||||||
int hugeSnapshotSent;
|
|
||||||
netchan_t netchan;
|
|
||||||
float predictedOrigin[3];
|
|
||||||
int predictedOriginServerTime;
|
|
||||||
int migrationState;
|
|
||||||
unsigned int predictedVehicleSplineId;
|
|
||||||
int predictedVehicleTargetEntity;
|
|
||||||
float predictedVehicleOrigin[3];
|
|
||||||
int predictedVehicleServerTime;
|
|
||||||
int ackedMessage[32];
|
|
||||||
unsigned int ackedMessageCount;
|
|
||||||
int sentMessage[32];
|
|
||||||
int wasKillcam[32];
|
|
||||||
unsigned int sendMessageCount;
|
|
||||||
bool overrideDeltaMessage;
|
|
||||||
int overrideSequenceNumber;
|
|
||||||
int sequenceResume;
|
|
||||||
int isInKillcam;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct svscmd_info_t
|
|
||||||
{
|
|
||||||
int time;
|
|
||||||
int type;
|
|
||||||
char cmd[1024];
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(sizeof(svscmd_info_t) == 0x408);
|
|
||||||
|
|
||||||
struct client_net_buffers_t
|
|
||||||
{
|
|
||||||
svscmd_info_t reliableCommandInfo[128];
|
|
||||||
char netchanOutgoingBuffer[131072];
|
|
||||||
char netchanIncomingBuffer[2048];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct usercmd_s
|
|
||||||
{
|
|
||||||
int serverTime;
|
|
||||||
unsigned int buttons;
|
|
||||||
int angles[3];
|
|
||||||
Weapon weapon;
|
|
||||||
Weapon offHand;
|
|
||||||
char forwardmove;
|
|
||||||
char rightmove;
|
|
||||||
unsigned __int16 airburstMarkDistance;
|
|
||||||
__int16 meleeChargeEnt;
|
|
||||||
unsigned __int8 meleeChargeDist;
|
|
||||||
char selectedLoc[2];
|
|
||||||
unsigned __int8 selectedLocAngle;
|
|
||||||
char remoteControlAngles[2];
|
|
||||||
char remoteControlMove[3];
|
|
||||||
unsigned int sightedClientsMask;
|
|
||||||
unsigned __int16 spawnTraceEntIndex;
|
|
||||||
unsigned int sightedSpawnsMask[2];
|
|
||||||
unsigned int partialSightedSpawnsMask[2];
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(sizeof(usercmd_s) == 0x44);
|
|
||||||
|
|
||||||
struct client_t
|
struct client_t
|
||||||
{
|
{
|
||||||
clientHeader_t header;
|
clientHeader_t header;
|
||||||
const char* dropReason;
|
char __pad0[3044];
|
||||||
char userinfo[0x400];
|
|
||||||
int reliableSequence;
|
int reliableSequence;
|
||||||
int reliableAcknowledge;
|
int reliableAcknowledge;
|
||||||
int reliableSent;
|
char __pad1[265864];
|
||||||
int messageAcknowledge;
|
|
||||||
int largeCommandSequence;
|
|
||||||
int gamestateMessageNum;
|
|
||||||
int challenge;
|
|
||||||
client_net_buffers_t netBuf;
|
|
||||||
int cumulThinkTime;
|
|
||||||
int beginCmdIndex;
|
|
||||||
int currCmdIndex;
|
|
||||||
usercmd_s lastUsercmd;
|
|
||||||
usercmd_s cmds[8];
|
|
||||||
int lastClientCommand;
|
|
||||||
gentity_s* gentity; // 268976
|
gentity_s* gentity; // 268976
|
||||||
char name[32];
|
char name[32]; // 268984
|
||||||
int lastPacketTime;
|
char __pad2[8];
|
||||||
int lastConnectTime;
|
|
||||||
int nextSnapshotTime; // 269024
|
int nextSnapshotTime; // 269024
|
||||||
char __pad3[532];
|
char __pad3[544];
|
||||||
int pureAuthentic;
|
|
||||||
unsigned int streamSyncWaitBits;
|
|
||||||
unsigned int streamSyncWaitTimeout;
|
|
||||||
LiveClientDropType liveDropRequest; // 269572
|
LiveClientDropType liveDropRequest; // 269572
|
||||||
char __pad4[24];
|
char __pad4[24];
|
||||||
TestClientType testClient; // 269600
|
TestClientType testClient; // 269600
|
||||||
char __pad5[391700];
|
char __pad5[391700];
|
||||||
}; // size = 661304
|
}; // size = 661304
|
||||||
|
|
||||||
static_assert(offsetof(client_t, header.netchan.unsentFragments) == 0x54);
|
|
||||||
static_assert(offsetof(client_t, header.migrationState) == 0x660);
|
|
||||||
static_assert(offsetof(client_t, userinfo) == 0x820);
|
|
||||||
static_assert(offsetof(client_t, reliableSequence) == 0xC20);
|
|
||||||
static_assert(offsetof(client_t, reliableAcknowledge) == 0xC24);
|
|
||||||
static_assert(offsetof(client_t, reliableSent) == 0xC28);
|
|
||||||
static_assert(offsetof(client_t, messageAcknowledge) == 0xC2C);
|
|
||||||
static_assert(offsetof(client_t, largeCommandSequence) == 0xC30);
|
|
||||||
static_assert(offsetof(client_t, lastUsercmd) == 0x41848);
|
|
||||||
static_assert(offsetof(client_t, lastClientCommand) == 0x41AAC);
|
|
||||||
static_assert(offsetof(client_t, gentity) == 0x41AB0);
|
static_assert(offsetof(client_t, gentity) == 0x41AB0);
|
||||||
static_assert(offsetof(client_t, name) == 0x41AB8);
|
|
||||||
static_assert(offsetof(client_t, lastPacketTime) == 0x41AD8);
|
|
||||||
static_assert(offsetof(client_t, nextSnapshotTime) == 0x41AE0);
|
static_assert(offsetof(client_t, nextSnapshotTime) == 0x41AE0);
|
||||||
static_assert(offsetof(client_t, pureAuthentic) == 0x41CF8);
|
|
||||||
static_assert(offsetof(client_t, liveDropRequest) == 0x41D04);
|
static_assert(offsetof(client_t, liveDropRequest) == 0x41D04);
|
||||||
static_assert(offsetof(client_t, testClient) == 0x41D20);
|
static_assert(offsetof(client_t, testClient) == 0x41D20);
|
||||||
static_assert(sizeof(client_t) == 0xA1738);
|
static_assert(sizeof(client_t) == 0xA1738);
|
||||||
@ -1653,9 +1508,7 @@ namespace game
|
|||||||
{
|
{
|
||||||
char __pad[56135];
|
char __pad[56135];
|
||||||
int flags; // 56136
|
int flags; // 56136
|
||||||
}; // Incomplete
|
};
|
||||||
|
|
||||||
static_assert(offsetof(gclient_s, flags) == 56136);
|
|
||||||
|
|
||||||
struct gentity_s
|
struct gentity_s
|
||||||
{
|
{
|
||||||
@ -2098,4 +1951,39 @@ namespace game
|
|||||||
HksError m_error;
|
HksError m_error;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum DBAllocFlags : std::int32_t
|
||||||
|
{
|
||||||
|
DB_ZONE_NONE = 0x0,
|
||||||
|
DB_ZONE_COMMON = 0x1,
|
||||||
|
DB_ZONE_UI = 0x2,
|
||||||
|
DB_ZONE_GAME = 0x4,
|
||||||
|
DB_ZONE_LOAD = 0x8,
|
||||||
|
DB_ZONE_DEV = 0x10,
|
||||||
|
DB_ZONE_BASEMAP = 0x20,
|
||||||
|
DB_ZONE_TRANSIENT_POOL = 0x40,
|
||||||
|
DB_ZONE_TRANSIENT_MASK = 0x40,
|
||||||
|
DB_ZONE_CUSTOM = 0x1000 // added for custom zone loading
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XZone
|
||||||
|
{
|
||||||
|
char name[64];
|
||||||
|
char pad_0040[432];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(XZone) == 496);
|
||||||
|
|
||||||
|
struct WeaponDef
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
const char* szInternalName;
|
||||||
|
const char* name;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XModel
|
||||||
|
{
|
||||||
|
const char* name; // 0
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -23,14 +23,13 @@ namespace game
|
|||||||
WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0, 0x1404C9690};
|
WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0, 0x1404C9690};
|
||||||
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1403D19B0};
|
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1403D19B0};
|
||||||
WEAK symbol<void()> Com_Quit_f{0x1402F9390, 0x1403D08C0};
|
WEAK symbol<void()> Com_Quit_f{0x1402F9390, 0x1403D08C0};
|
||||||
WEAK symbol<void(const char* finalmsg)> Com_Shutdown{0x0, 0x1403D1BF0};
|
WEAK symbol<int(char* dest, int size, const char* fmt, ...)> Com_sprintf{0x0, 0x1404C97B0};
|
||||||
|
|
||||||
WEAK symbol<void(const char* cmdName, void (), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x1402EDDB0, 0x1403AF2C0};
|
WEAK symbol<void(const char* cmdName, void (), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x1402EDDB0, 0x1403AF2C0};
|
||||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x1402EE350, 0x1403AF900};
|
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x1402EE350, 0x1403AF900};
|
||||||
WEAK symbol<void(int index)> Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0};
|
WEAK symbol<void(const char*)> Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0};
|
||||||
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{0x1402EEA30, 0x1403B0020};
|
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{0x1402EEA30, 0x1403B0020};
|
||||||
WEAK symbol<void()> Cmd_EndTokenizeString{0x1402EE000, 0x1403AF5B0};
|
WEAK symbol<void()> Cmd_EndTokenizeString{0x1402EE000, 0x1403AF5B0};
|
||||||
WEAK symbol<int(char* dest, int size, const char* fmt, ...)> Com_sprintf{0x140378E30, 0x1404C97B0};
|
|
||||||
|
|
||||||
WEAK symbol<void(const char* message)> Conbuf_AppendText{0x14038F220, 0x1404D9040};
|
WEAK symbol<void(const char* message)> Conbuf_AppendText{0x14038F220, 0x1404D9040};
|
||||||
|
|
||||||
@ -60,6 +59,7 @@ namespace game
|
|||||||
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x14017E890, 0x14026FCC0};
|
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x14017E890, 0x14026FCC0};
|
||||||
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x14017E750, 0x14026FB90};
|
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x14017E750, 0x14026FB90};
|
||||||
WEAK symbol<char*(const char* filename, char* buf, int size)> DB_ReadRawFile{0x140180E30, 0x140273080};
|
WEAK symbol<char*(const char* filename, char* buf, int size)> DB_ReadRawFile{0x140180E30, 0x140273080};
|
||||||
|
WEAK symbol<bool(const char* filename)> DB_IsLocalized{0x14017EC80, 0x140270190};
|
||||||
|
|
||||||
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140370860, 0x1404BF8B0};
|
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140370860, 0x1404BF8B0};
|
||||||
WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x140370700, 0x1404BF690};
|
WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x140370700, 0x1404BF690};
|
||||||
@ -186,7 +186,7 @@ namespace game
|
|||||||
WEAK symbol<void(mp::client_t* client)> SV_DropClient{0, 0x140438A30};
|
WEAK symbol<void(mp::client_t* client)> SV_DropClient{0, 0x140438A30};
|
||||||
WEAK symbol<void(mp::client_t*, const char*, int)> SV_ExecuteClientCommand{0, 0x15121D8E6};
|
WEAK symbol<void(mp::client_t*, const char*, int)> SV_ExecuteClientCommand{0, 0x15121D8E6};
|
||||||
WEAK symbol<void(int localClientNum)> SV_FastRestart{0, 0x1404374E0};
|
WEAK symbol<void(int localClientNum)> SV_FastRestart{0, 0x1404374E0};
|
||||||
WEAK symbol<int(mp::client_t* client, const unsigned char* cmd, int cmdSize)> SV_CanReplaceServerCommand{0x0, 0x140441A00};
|
WEAK symbol<void(int clientNum, svscmd_type type, const char* text)> SV_GameSendServerCommand{0x1403F3A70, 0x14043E120};
|
||||||
WEAK symbol<const char*(int clientNum)> SV_GetGuid{0, 0x14043E1E0};
|
WEAK symbol<const char*(int clientNum)> SV_GetGuid{0, 0x14043E1E0};
|
||||||
WEAK symbol<int(int clientNum)> SV_GetClientPing{0, 0x14043E1C0};
|
WEAK symbol<int(int clientNum)> SV_GetClientPing{0, 0x14043E1C0};
|
||||||
WEAK symbol<playerState_s*(int num)> SV_GetPlayerstateForClientNum{0x1403F3AB0, 0x14043E260};
|
WEAK symbol<playerState_s*(int num)> SV_GetPlayerstateForClientNum{0x1403F3AB0, 0x14043E260};
|
||||||
@ -214,6 +214,7 @@ namespace game
|
|||||||
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0, 0x140490060};
|
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0, 0x140490060};
|
||||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0, 0x140492380};
|
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0, 0x140492380};
|
||||||
|
|
||||||
|
WEAK symbol<const char* ()> SEH_GetCurrentLanguageCode{0x140339280, 0x140474560};
|
||||||
WEAK symbol<const char*()> SEH_GetCurrentLanguageName{0x140339300, 0x1404745C0};
|
WEAK symbol<const char*()> SEH_GetCurrentLanguageName{0x140339300, 0x1404745C0};
|
||||||
|
|
||||||
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, unsigned int alignment, unsigned int type, int source)> PMem_AllocFromSource_NoDebug{0x1403775F0, 0x1404C7BA0};
|
||||||
@ -222,9 +223,6 @@ namespace game
|
|||||||
WEAK symbol<void(unsigned __int64 markPos)> LargeLocalResetToMark{0x140369C40, 0x1404B6790};
|
WEAK symbol<void(unsigned __int64 markPos)> LargeLocalResetToMark{0x140369C40, 0x1404B6790};
|
||||||
|
|
||||||
WEAK symbol<void(char* dest, const char* src, int destsize)> I_strncpyz{0x1403793B0, 0x1404C9E60};
|
WEAK symbol<void(char* dest, const char* src, int destsize)> I_strncpyz{0x1403793B0, 0x1404C9E60};
|
||||||
WEAK symbol<void(char* dest, int size, const char* src)> I_strncat{0x1403792E0, 0x1404C9D90};
|
|
||||||
|
|
||||||
WEAK symbol<void(const char* pszCommand, char* pszBuffer, int iBufferSize)> MSG_WriteReliableCommandToBuffer{0x0, 0x1403E1090};
|
|
||||||
|
|
||||||
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14059C5C0, 0x1406FD930};
|
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14059C5C0, 0x1406FD930};
|
||||||
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x14059CD00, 0x1406FE070};
|
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x14059CD00, 0x1406FE070};
|
||||||
@ -265,6 +263,7 @@ namespace game
|
|||||||
WEAK symbol<XAssetEntry> g_assetEntryPool{0x142CC2400, 0x14379F100};
|
WEAK symbol<XAssetEntry> g_assetEntryPool{0x142CC2400, 0x14379F100};
|
||||||
WEAK symbol<int> g_poolSize{0x140804140, 0x1409B4B90};
|
WEAK symbol<int> g_poolSize{0x140804140, 0x1409B4B90};
|
||||||
WEAK symbol<const char*> g_assetNames{0x140803C90, 0x1409B3180};
|
WEAK symbol<const char*> g_assetNames{0x140803C90, 0x1409B3180};
|
||||||
|
WEAK symbol<int> g_compressor{0x141598580, 0x141E0B080};
|
||||||
|
|
||||||
WEAK symbol<DWORD> threadIds{0x149632EC0, 0x147DCEA30};
|
WEAK symbol<DWORD> threadIds{0x149632EC0, 0x147DCEA30};
|
||||||
|
|
||||||
@ -272,6 +271,9 @@ namespace game
|
|||||||
|
|
||||||
WEAK symbol<unsigned int> tls_index{0x14F65DAF0, 0x150085C44};
|
WEAK symbol<unsigned int> tls_index{0x14F65DAF0, 0x150085C44};
|
||||||
|
|
||||||
|
WEAK symbol<unsigned int> g_zoneCount{0x0, 0x14379DBCC};
|
||||||
|
WEAK symbol<XZone> g_zones{0x0, 0x143B50618};
|
||||||
|
|
||||||
namespace mp
|
namespace mp
|
||||||
{
|
{
|
||||||
WEAK symbol<gentity_s> g_entities{0, 0x144758C70};
|
WEAK symbol<gentity_s> g_entities{0, 0x144758C70};
|
||||||
|
@ -64,7 +64,9 @@ LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info)
|
|||||||
&exception_information, nullptr, nullptr))
|
&exception_information, nullptr, nullptr))
|
||||||
{
|
{
|
||||||
char buf[4096]{};
|
char buf[4096]{};
|
||||||
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n", exception_info->ExceptionRecord->ExceptionCode, exception_info->ExceptionRecord->ExceptionAddress);
|
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n",
|
||||||
|
exception_info->ExceptionRecord->ExceptionCode,
|
||||||
|
exception_info->ExceptionRecord->ExceptionAddress);
|
||||||
game::show_error(buf);
|
game::show_error(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,6 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
@ -176,4 +176,52 @@ namespace utils::string
|
|||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_numeric(const std::string& text)
|
||||||
|
{
|
||||||
|
return std::to_string(atoi(text.data())) == text;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_lower(const std::string& a, const std::string& b)
|
||||||
|
{
|
||||||
|
return to_lower(a).find(to_lower(b)) != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool strstr_lower(const char* a, const char* b)
|
||||||
|
{
|
||||||
|
const char* a_ = a;
|
||||||
|
const char* b_ = b;
|
||||||
|
|
||||||
|
while (*a_ != '\0' && *b_ != '\0')
|
||||||
|
{
|
||||||
|
if (*b_ == '*' || std::tolower(*a_) == std::tolower(*b_))
|
||||||
|
{
|
||||||
|
b_++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b_ = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
a_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *b_ == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_clipboard_data(const std::string& text)
|
||||||
|
{
|
||||||
|
const auto len = text.size() + 1;
|
||||||
|
const auto mem = GlobalAlloc(GMEM_MOVEABLE, len);
|
||||||
|
|
||||||
|
memcpy(GlobalLock(mem), text.data(), len);
|
||||||
|
GlobalUnlock(mem);
|
||||||
|
|
||||||
|
if (OpenClipboard(nullptr))
|
||||||
|
{
|
||||||
|
EmptyClipboard();
|
||||||
|
SetClipboardData(CF_TEXT, mem);
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,4 +95,12 @@ namespace utils::string
|
|||||||
std::wstring convert(const std::string& str);
|
std::wstring convert(const std::string& str);
|
||||||
|
|
||||||
std::string replace(std::string str, const std::string& from, const std::string& to);
|
std::string replace(std::string str, const std::string& from, const std::string& to);
|
||||||
|
|
||||||
|
bool is_numeric(const std::string& text);
|
||||||
|
|
||||||
|
bool find_lower(const std::string& a, const std::string& b);
|
||||||
|
|
||||||
|
bool strstr_lower(const char* a, const char* b);
|
||||||
|
|
||||||
|
void set_clipboard_data(const std::string& text);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user