forked from mjkzy/s1-mod
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
c99fb7c6a0
|
|||
d6c93e6966
|
|||
84d7702ba2 | |||
b25aeb38a5 | |||
6239fb8a10
|
|||
5a4f180986
|
|||
547948a324
|
|||
6217a48a0d
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -29,9 +29,6 @@
|
||||
[submodule "deps/udis86"]
|
||||
path = deps/udis86
|
||||
url = https://github.com/vmt/udis86.git
|
||||
[submodule "deps/WinToast"]
|
||||
path = deps/WinToast
|
||||
url = https://github.com/mohabouje/WinToast.git
|
||||
[submodule "deps/zlib"]
|
||||
path = deps/zlib
|
||||
url = https://github.com/madler/zlib.git
|
||||
|
2
deps/GSL
vendored
2
deps/GSL
vendored
Submodule deps/GSL updated: 355982daf6...466e4ebaa5
1
deps/WinToast
vendored
1
deps/WinToast
vendored
Submodule deps/WinToast deleted from a78ce469b4
2
deps/asmjit
vendored
2
deps/asmjit
vendored
Submodule deps/asmjit updated: cfc9f813cc...cecc73f297
2
deps/gsc-tool
vendored
2
deps/gsc-tool
vendored
Submodule deps/gsc-tool updated: e4cb6a7819...c508e5b10f
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
Submodule deps/libtomcrypt updated: 2e9f2b5e44...d448df1938
2
deps/libtommath
vendored
2
deps/libtommath
vendored
Submodule deps/libtommath updated: 5809141a3a...e823b0c34c
2
deps/minhook
vendored
2
deps/minhook
vendored
Submodule deps/minhook updated: c1a7c3843b...565968b285
2
deps/rapidjson
vendored
2
deps/rapidjson
vendored
Submodule deps/rapidjson updated: d621dc9e9c...24b5e7a8b2
2
deps/zlib
vendored
2
deps/zlib
vendored
Submodule deps/zlib updated: ef24c4c750...5a82f71ed1
@@ -259,7 +259,7 @@ filter "configurations:Release"
|
||||
buildoptions {"/GL"}
|
||||
linkoptions {"/IGNORE:4702", "/LTCG"}
|
||||
defines {"NDEBUG"}
|
||||
flags {"FatalCompileWarnings"}
|
||||
fatalwarnings {"All"}
|
||||
filter {}
|
||||
|
||||
filter "configurations:Debug"
|
||||
|
61
src/client/component/assets/weapons.cpp
Normal file
61
src/client/component/assets/weapons.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "component/console.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace weapons
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void g_setup_level_weapon_def_stub()
|
||||
{
|
||||
game::G_SetupLevelWeaponDef();
|
||||
|
||||
// The count on this game seems pretty high
|
||||
std::array<game::WeaponCompleteDef*, 2048> weapons{};
|
||||
const auto count = game::DB_GetAllXAssetOfType_FastFile(game::ASSET_TYPE_WEAPON, (void**)weapons.data(), static_cast<int>(weapons.max_size()));
|
||||
|
||||
std::sort(weapons.begin(), weapons.begin() + count, [](game::WeaponCompleteDef* weapon1, game::WeaponCompleteDef* weapon2)
|
||||
{
|
||||
assert(weapon1->szInternalName);
|
||||
assert(weapon2->szInternalName);
|
||||
|
||||
return std::strcmp(weapon1->szInternalName, weapon2->szInternalName) < 0;
|
||||
});
|
||||
|
||||
#ifdef _DEBUG
|
||||
console::info("Found %i weapons to precache\n", count);
|
||||
#endif
|
||||
|
||||
for (auto i = 0; i < count; ++i)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
console::info("Precaching weapon \"%s\"\n", weapons[i]->szInternalName);
|
||||
#endif
|
||||
(void)game::G_GetWeaponForName(weapons[i]->szInternalName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (game::environment::is_sp()) return;
|
||||
|
||||
// Kill Scr_PrecacheItem (We are going to do this from code)
|
||||
utils::hook::nop(0x1403101D0, 4);
|
||||
utils::hook::set<std::uint8_t>(0x1403101D0, 0xC3);
|
||||
|
||||
// Load weapons from the DB
|
||||
utils::hook::call(0x1402F6EF4, g_setup_level_weapon_def_stub);
|
||||
utils::hook::call(0x140307401, g_setup_level_weapon_def_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(weapons::component)
|
@@ -30,8 +30,7 @@ namespace binding
|
||||
|
||||
if (value && value < get_num_keys())
|
||||
{
|
||||
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
|
||||
"bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
|
||||
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, game::command_whitelist[value]);
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
@@ -45,8 +44,7 @@ namespace binding
|
||||
value -= get_num_keys();
|
||||
if (static_cast<size_t>(value) < custom_binds.size() && !custom_binds[value].empty())
|
||||
{
|
||||
const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used),
|
||||
"bind %s \"%s\"\n", key_button, custom_binds[value].data());
|
||||
const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, custom_binds[value].data());
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
|
@@ -13,6 +13,8 @@
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#define ALLOW_CUSTOM_BOT_NAMES
|
||||
|
||||
namespace bots
|
||||
{
|
||||
namespace
|
||||
@@ -62,7 +64,6 @@ namespace bots
|
||||
return;
|
||||
}
|
||||
|
||||
// SV_BotGetRandomName
|
||||
const auto* const bot_name = game::SV_BotGetRandomName();
|
||||
const auto* bot_ent = game::SV_AddBot(bot_name);
|
||||
if (bot_ent)
|
||||
|
@@ -1,14 +1,13 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/engine/sv_game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
#include "game_console.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "fastfiles.hpp"
|
||||
#include "game_console.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
@@ -236,15 +235,13 @@ namespace command
|
||||
{
|
||||
if (!dvars::sv_cheats->current.enabled)
|
||||
{
|
||||
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||
"f \"Cheats are not enabled on this server\"");
|
||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ent->health < 1)
|
||||
{
|
||||
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||
"f \"You must be alive to use this command\"");
|
||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You must be alive to use this command\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -582,8 +579,7 @@ namespace command
|
||||
|
||||
ent->flags ^= game::FL_GODMODE;
|
||||
|
||||
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||
utils::string::va("f \"godmode %s\"", (ent->flags & game::FL_GODMODE) ? "^2on" : "^1off"));
|
||||
game::engine::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&)
|
||||
@@ -593,8 +589,7 @@ namespace command
|
||||
|
||||
ent->flags ^= game::FL_DEMI_GODMODE;
|
||||
|
||||
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"));
|
||||
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"));
|
||||
});
|
||||
|
||||
add_sv("notarget", [](game::mp::gentity_s* ent, const params_sv&)
|
||||
@@ -604,8 +599,7 @@ namespace command
|
||||
|
||||
ent->flags ^= game::FL_NOTARGET;
|
||||
|
||||
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||
utils::string::va("f \"notarget %s\"", (ent->flags & game::FL_NOTARGET) ? "^2on" : "^1off"));
|
||||
game::engine::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&)
|
||||
@@ -615,8 +609,7 @@ namespace command
|
||||
|
||||
ent->client->flags ^= 1;
|
||||
|
||||
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||
utils::string::va("f \"noclip %s\"", ent->client->flags & 1 ? "^2on" : "^1off"));
|
||||
game::engine::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&)
|
||||
@@ -626,8 +619,7 @@ namespace command
|
||||
|
||||
ent->client->flags ^= 2;
|
||||
|
||||
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||
utils::string::va("f \"ufo %s\"", ent->client->flags & 2 ? "^2on" : "^1off"));
|
||||
game::engine::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)
|
||||
@@ -637,8 +629,7 @@ namespace command
|
||||
|
||||
if (params.size() < 2)
|
||||
{
|
||||
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||
"f \"You did not specify a weapon name\"");
|
||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -661,8 +652,7 @@ namespace command
|
||||
|
||||
if (params.size() < 2)
|
||||
{
|
||||
game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE,
|
||||
"f \"You did not specify a weapon name\"");
|
||||
game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\"");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -42,7 +42,7 @@ namespace console
|
||||
{
|
||||
static thread_local char buffer[0x1000];
|
||||
|
||||
const auto count = vsnprintf_s(buffer, _TRUNCATE, message, *ap);
|
||||
const auto count = vsnprintf(buffer, sizeof(buffer), message, *ap);
|
||||
|
||||
if (count < 0) return {};
|
||||
return {buffer, static_cast<size_t>(count)};
|
||||
@@ -84,15 +84,14 @@ namespace console
|
||||
|
||||
void print_stub(const char* fmt, ...)
|
||||
{
|
||||
char buffer[4096]{};
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
char buffer[4096]{};
|
||||
const auto res = vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
|
||||
(void)res;
|
||||
print_message(buffer);
|
||||
|
||||
[[maybe_unused]] const auto res = vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
print_message(buffer);
|
||||
}
|
||||
|
||||
void append_text(const char* text)
|
||||
|
@@ -1,13 +1,16 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "game/engine/sv_game.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
#include "dvars.hpp"
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "server_list.hpp"
|
||||
#include "network.hpp"
|
||||
#include "command.hpp"
|
||||
#include "dvars.hpp"
|
||||
|
||||
#include "component/gsc/script_extension.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
@@ -16,9 +19,6 @@ namespace dedicated
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour gscr_set_dynamic_dvar_hook;
|
||||
utils::hook::detour com_quit_f_hook;
|
||||
|
||||
const game::dvar_t* sv_lanOnly;
|
||||
|
||||
void init_dedicated_server()
|
||||
@@ -99,8 +99,7 @@ namespace dedicated
|
||||
|
||||
for (const auto& command : queue)
|
||||
{
|
||||
game::Cbuf_AddText(0, command.data());
|
||||
game::Cbuf_AddText(0, "\n");
|
||||
command::execute(command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,49 +108,53 @@ namespace dedicated
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
void gscr_set_dynamic_dvar()
|
||||
void sv_kill_server_f()
|
||||
{
|
||||
auto s = game::Scr_GetString(0);
|
||||
auto* dvar = game::Dvar_FindVar(s);
|
||||
if (dvar && !std::strncmp("scr_", dvar->name, 4))
|
||||
game::Com_Shutdown("EXE_SERVERKILLED");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
gscr_set_dynamic_dvar_hook.invoke<void>();
|
||||
}
|
||||
|
||||
void kill_server()
|
||||
{
|
||||
for (auto i = 0; i < *game::mp::svs_numclients; ++i)
|
||||
if (!game::SV_MapExists(map_name.c_str()))
|
||||
{
|
||||
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("Map '%s' doesn't exist.\n", map_name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
com_quit_f_hook.invoke<void>();
|
||||
auto* current_mapname = game::Dvar_FindVar("mapname");
|
||||
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());
|
||||
command::execute("map_restart", false);
|
||||
return;
|
||||
}
|
||||
|
||||
console::info("Starting map: %s\n", map_name.c_str());
|
||||
|
||||
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);
|
||||
|
||||
game::SV_StartMapForParty(0, map_name.c_str(), false, false);
|
||||
}
|
||||
|
||||
void sys_error_stub(const char* msg, ...)
|
||||
void gscr_is_using_match_rules_data_stub()
|
||||
{
|
||||
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);
|
||||
game::Scr_AddInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +193,12 @@ namespace dedicated
|
||||
// Don't allow sv_hostname to be changed by the game
|
||||
dvars::disable::set_string("sv_hostname");
|
||||
|
||||
// Stop crashing from sys_errors
|
||||
utils::hook::jump(0x1404D6260, sys_error_stub);
|
||||
|
||||
// Hook R_SyncGpu
|
||||
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);
|
||||
|
||||
// delay startup commands until the initialization is done
|
||||
@@ -205,9 +208,6 @@ namespace dedicated
|
||||
utils::hook::call(0x1403CEC35, execute_console_command);
|
||||
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(0x1403AF719, 5); // ^
|
||||
utils::hook::set<uint8_t>(0x1403D2490, 0xC3); // don't save config file
|
||||
@@ -306,7 +306,7 @@ namespace dedicated
|
||||
console::info("==================================\n");
|
||||
|
||||
// remove disconnect command
|
||||
game::Cmd_RemoveCommand(reinterpret_cast<const char*>(751));
|
||||
game::Cmd_RemoveCommand(751);
|
||||
|
||||
execute_startup_command_queue();
|
||||
execute_console_command_queue();
|
||||
@@ -317,8 +317,38 @@ namespace dedicated
|
||||
command::add("heartbeat", send_heartbeat);
|
||||
}, scheduler::pipeline::main, 1s);
|
||||
|
||||
command::add("killserver", kill_server);
|
||||
com_quit_f_hook.create(0x1403D08C0, &kill_server);
|
||||
command::add("killserver", sv_kill_server_f);
|
||||
|
||||
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,15 +348,14 @@ namespace demonware
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
char buffer[2048]{};
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
|
||||
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||
printf("%s: %s\n", function, buffer);
|
||||
|
||||
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
printf("%s: %s\n", function, buffer);
|
||||
}
|
||||
|
||||
void startup_dw()
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/engine/sv_game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "console.hpp"
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace dvar_cheats
|
||||
{
|
||||
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::dvar_value* value)
|
||||
void apply_sv_cheats(const game::dvar_t* dvar, const game::DvarSetSource source, game::DvarValue* value)
|
||||
{
|
||||
if (dvar && dvar->name == "sv_cheats"s)
|
||||
{
|
||||
@@ -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* command = utils::string::va("q %s \"%s\"", dvar, value);
|
||||
|
||||
game::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command);
|
||||
game::engine::SV_GameSendServerCommand(static_cast<char>(entity_num), game::SV_CMD_RELIABLE, command);
|
||||
}
|
||||
|
||||
const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a)
|
||||
|
@@ -18,6 +18,14 @@ namespace fastfiles
|
||||
{
|
||||
utils::hook::detour db_try_load_x_file_internal_hook;
|
||||
utils::hook::detour db_find_x_asset_header_hook;
|
||||
utils::hook::detour db_read_stream_file_hook;
|
||||
|
||||
int db_read_stream_file_stub(int allow_abort, int finish)
|
||||
{
|
||||
// always use lz4 compressor type when reading stream files
|
||||
*game::g_compressor = game::DB_COMPRESSOR_LZX;
|
||||
return db_read_stream_file_hook.invoke<int>(allow_abort, finish);
|
||||
}
|
||||
|
||||
void db_try_load_x_file_internal(const char* zone_name, const int flags)
|
||||
{
|
||||
@@ -137,21 +145,6 @@ namespace fastfiles
|
||||
db_find_x_asset_header_hook.create(game::DB_FindXAssetHeader, db_find_x_asset_header_stub);
|
||||
dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE);
|
||||
|
||||
command::add("loadzone", [](const command::params& params)
|
||||
{
|
||||
if (params.size() < 2)
|
||||
{
|
||||
console::info("usage: loadzone <zone>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
game::XZoneInfo info{};
|
||||
info.name = params.get(1);
|
||||
info.allocFlags = 1;
|
||||
info.freeFlags = 0;
|
||||
game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_SYNC);
|
||||
});
|
||||
|
||||
command::add("g_poolSizes", []()
|
||||
{
|
||||
for (auto i = 0; i < game::ASSET_TYPE_COUNT; i++)
|
||||
@@ -160,6 +153,9 @@ namespace fastfiles
|
||||
}
|
||||
});
|
||||
|
||||
// Allow loading of mixed compressor types
|
||||
utils::hook::nop(SELECT_VALUE(0x1401536D7, 0x140242DF7), 2);
|
||||
|
||||
reallocate_asset_pool<game::ASSET_TYPE_FONT, 48>();
|
||||
|
||||
if (!game::environment::is_sp())
|
||||
@@ -170,6 +166,9 @@ namespace fastfiles
|
||||
utils::hook::inject(0x14026FFAC, xmodel_pool + 8);
|
||||
utils::hook::inject(0x14027463C, xmodel_pool + 8);
|
||||
utils::hook::inject(0x140274689, xmodel_pool + 8);
|
||||
|
||||
// Fix compressor type on streamed file load
|
||||
db_read_stream_file_hook.create(0x14027AA70, db_read_stream_file_stub);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -64,20 +64,22 @@ namespace filesystem
|
||||
|
||||
void startup()
|
||||
{
|
||||
register_path("s1");
|
||||
const auto base = std::filesystem::current_path();
|
||||
|
||||
register_path(base / "s1");
|
||||
register_path(get_binary_directory() + "\\data");
|
||||
|
||||
if (get_binary_directory() != std::filesystem::current_path())
|
||||
if (get_binary_directory() != base)
|
||||
{
|
||||
register_path(std::filesystem::current_path() / "data");
|
||||
register_path(base / "data");
|
||||
}
|
||||
|
||||
// game's search paths
|
||||
register_path("devraw");
|
||||
register_path("devraw_shared");
|
||||
register_path("raw_shared");
|
||||
register_path("raw");
|
||||
register_path("main");
|
||||
register_path(base / "devraw");
|
||||
register_path(base / "devraw_shared");
|
||||
register_path(base / "raw_shared");
|
||||
register_path(base / "raw");
|
||||
register_path(base / "main");
|
||||
}
|
||||
|
||||
void check_for_startup()
|
||||
|
@@ -63,7 +63,7 @@ namespace game_console
|
||||
|
||||
void clear()
|
||||
{
|
||||
strncpy_s(con.buffer, "", sizeof(con.buffer));
|
||||
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
|
||||
con.cursor = 0;
|
||||
|
||||
fixed_input = "";
|
||||
@@ -249,7 +249,7 @@ namespace game_console
|
||||
dvars::con_inputDvarInactiveValueColor->current.vector, offset);
|
||||
}
|
||||
|
||||
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
|
||||
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), 64);
|
||||
con.globals.may_auto_complete = true;
|
||||
}
|
||||
else if (matches.size() > 1)
|
||||
@@ -274,7 +274,7 @@ namespace game_console
|
||||
}
|
||||
}
|
||||
|
||||
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
|
||||
game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), 64);
|
||||
con.globals.may_auto_complete = true;
|
||||
}
|
||||
}
|
||||
@@ -365,11 +365,11 @@ namespace game_console
|
||||
|
||||
void print_internal(const char* fmt, ...)
|
||||
{
|
||||
char va_buffer[0x200]{};
|
||||
char va_buffer[1024]{};
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsprintf_s(va_buffer, fmt, ap);
|
||||
vsnprintf(va_buffer, sizeof(va_buffer), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
const auto formatted = std::string(va_buffer);
|
||||
@@ -425,7 +425,7 @@ namespace game_console
|
||||
con.buffer[1] = '\0';
|
||||
}
|
||||
|
||||
strncat_s(con.buffer, con.globals.auto_complete_choice, 64);
|
||||
game::I_strncat(con.buffer, sizeof(con.buffer), con.globals.auto_complete_choice);
|
||||
con.cursor = static_cast<int>(std::string(con.buffer).length());
|
||||
|
||||
if (con.cursor != 254)
|
||||
@@ -550,7 +550,7 @@ namespace game_console
|
||||
|
||||
if (history_index != -1)
|
||||
{
|
||||
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
|
||||
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
|
||||
con.cursor = static_cast<int>(strlen(con.buffer));
|
||||
}
|
||||
}
|
||||
@@ -565,7 +565,7 @@ namespace game_console
|
||||
|
||||
if (history_index != -1)
|
||||
{
|
||||
strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
|
||||
game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer));
|
||||
con.cursor = static_cast<int>(strlen(con.buffer));
|
||||
}
|
||||
}
|
||||
@@ -720,7 +720,7 @@ namespace game_console
|
||||
con.output_visible = false;
|
||||
con.display_line_offset = 0;
|
||||
con.line_count = 0;
|
||||
strncpy_s(con.buffer, "", sizeof(con.buffer));
|
||||
game::I_strncpyz(con.buffer, "", sizeof(con.buffer));
|
||||
|
||||
con.globals.x = 0.0f;
|
||||
con.globals.y = 0.0f;
|
||||
@@ -728,7 +728,7 @@ namespace game_console
|
||||
con.globals.font_height = 0.0f;
|
||||
con.globals.may_auto_complete = false;
|
||||
con.globals.info_line_count = 0;
|
||||
strncpy_s(con.globals.auto_complete_choice, "", 64);
|
||||
game::I_strncpyz(con.globals.auto_complete_choice, "", 64);
|
||||
|
||||
// add clear command
|
||||
command::add("clear", [&]()
|
||||
|
@@ -10,7 +10,6 @@
|
||||
|
||||
#include "gsc/script_extension.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
@@ -23,7 +22,7 @@ namespace game_log
|
||||
char buf[1024]{};
|
||||
std::size_t out_chars = 0;
|
||||
|
||||
for (auto i = 0u; i < game::Scr_GetNumParam(); ++i)
|
||||
for (std::uint32_t i = 0; i < game::Scr_GetNumParam(); ++i)
|
||||
{
|
||||
const auto* value = game::Scr_GetString(i);
|
||||
const auto len = std::strlen(value);
|
||||
@@ -34,7 +33,7 @@ namespace game_log
|
||||
break;
|
||||
}
|
||||
|
||||
strncat_s(buf, value, _TRUNCATE);
|
||||
game::I_strncat(buf, sizeof(buf), value);
|
||||
}
|
||||
|
||||
g_log_printf("%s", buf);
|
||||
@@ -53,18 +52,11 @@ namespace game_log
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
|
||||
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
const auto time = *game::level_time / 1000;
|
||||
utils::io::write_file(log, utils::string::va("%3i:%i%i %s",
|
||||
time / 60,
|
||||
time % 60 / 10,
|
||||
time % 60 % 10,
|
||||
buffer
|
||||
), true);
|
||||
utils::io::write_file(log, utils::string::va("%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, buffer), true);
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
|
@@ -357,12 +357,12 @@ namespace gsc
|
||||
build = static_cast<xsk::gsc::build>(static_cast<unsigned int>(build) | static_cast<unsigned int>(xsk::gsc::build::dev_maps));
|
||||
}
|
||||
|
||||
if (dvars::com_developer_script && dvars::com_developer_script->current.enabled)
|
||||
if (dvars::com_developer_script && dvars::com_developer_script->current.integer > 0)
|
||||
{
|
||||
build = static_cast<xsk::gsc::build>(static_cast<unsigned int>(build) | static_cast<unsigned int>(xsk::gsc::build::dev_blocks));
|
||||
}
|
||||
|
||||
gsc_ctx->init(build, []([[maybe_unused]] auto const* ctx, const auto& included_path) -> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
|
||||
gsc_ctx->init(build, []([[maybe_unused]] const auto* ctx, const auto& included_path) -> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
|
||||
{
|
||||
const auto script_name = std::filesystem::path(included_path).replace_extension().string();
|
||||
|
||||
@@ -419,7 +419,7 @@ namespace gsc
|
||||
public:
|
||||
void post_load() override
|
||||
{
|
||||
gsc_ctx = std::make_unique<xsk::gsc::s1_pc::context>();
|
||||
gsc_ctx = std::make_unique<xsk::gsc::s1_pc::context>(xsk::gsc::instance::server);
|
||||
}
|
||||
|
||||
void post_unpack() override
|
||||
@@ -434,8 +434,11 @@ namespace gsc
|
||||
utils::hook::call(SELECT_VALUE(0x14031AB47, 0x1403F7317), find_script);
|
||||
utils::hook::call(SELECT_VALUE(0x14031AB57, 0x1403F7327), db_is_x_asset_default);
|
||||
|
||||
// Enable development options
|
||||
dvars::com_developer = game::Dvar_RegisterInt("developer", 0, 0, 2, game::DVAR_FLAG_NONE);
|
||||
dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE);
|
||||
// Enable developer script comments: 0 disabled, 1 full developer script, 2 only dev scripts required by art/lighting tweaks.
|
||||
// gsc-tool will only have one mode which supports both 1 and 2 dev blocks in the code simultaneously.
|
||||
dvars::com_developer_script = game::Dvar_RegisterInt("developer_script", 0, 0, 2, game::DVAR_FLAG_NONE);
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
|
@@ -2,7 +2,6 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "party.hpp"
|
||||
#include "console.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
@@ -11,57 +10,15 @@ namespace logger
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour com_error_hook;
|
||||
|
||||
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, ...)
|
||||
{
|
||||
char buffer[2048]{};
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, msg);
|
||||
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
console::warn("%s", buffer);
|
||||
@@ -73,7 +30,7 @@ namespace logger
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, msg);
|
||||
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
console::info("%s", buffer);
|
||||
@@ -90,7 +47,7 @@ namespace logger
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, msg);
|
||||
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
console::info("%s", buffer);
|
||||
@@ -157,13 +114,6 @@ namespace logger
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@@ -29,7 +29,7 @@ namespace lui
|
||||
{
|
||||
if (params.size() <= 1)
|
||||
{
|
||||
console::info("usage: lui_open <name>\n");
|
||||
console::info("USAGE: %s <name>\n", params.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace lui
|
||||
{
|
||||
if (params.size() <= 1)
|
||||
{
|
||||
console::info("usage: lui_open_popup <name>\n");
|
||||
console::info("USAGE: %s <name>\n", params.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
|
233
src/client/component/mods.cpp
Normal file
233
src/client/component/mods.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
#include "mods.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace mods
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour sys_create_file_hook;
|
||||
|
||||
void db_build_os_path_from_source(const char* zone_name, const game::FF_DIR source, const int size, char* filename)
|
||||
{
|
||||
char user_map[MAX_PATH]{};
|
||||
|
||||
switch (source)
|
||||
{
|
||||
case game::FFD_DEFAULT:
|
||||
(void)game::Com_sprintf(filename, size, "%s\\%s.ff", std::filesystem::current_path().string().c_str(), zone_name);
|
||||
break;
|
||||
case game::FFD_MOD_DIR:
|
||||
assert(mods::is_using_mods());
|
||||
|
||||
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), (*dvars::fs_gameDirVar)->current.string, zone_name);
|
||||
break;
|
||||
case game::FFD_USER_MAP:
|
||||
game::I_strncpyz(user_map, zone_name, sizeof(user_map));
|
||||
|
||||
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s\\%s.ff", std::filesystem::current_path().string().c_str(), "usermaps", user_map, zone_name);
|
||||
break;
|
||||
default:
|
||||
assert(false && "inconceivable");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool fs_game_dir_domain_func(game::dvar_t* dvar, game::DvarValue new_value)
|
||||
{
|
||||
if (*new_value.string == '\0')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (game::I_strnicmp(new_value.string, "mods", 4) != 0)
|
||||
{
|
||||
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
|
||||
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (5 < std::strlen(new_value.string) && (new_value.string[4] == '\\' || new_value.string[4] == '/'))
|
||||
{
|
||||
const auto* s1 = std::strstr(new_value.string, "..");
|
||||
const auto* s2 = std::strstr(new_value.string, "::");
|
||||
if (s1 == nullptr && s2 == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
|
||||
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Invalid path specified
|
||||
game::LiveStorage_StatsWriteNotNeeded(game::CONTROLLER_INDEX_0);
|
||||
console::error("ERROR: Invalid server value '%s' for '%s'\n", new_value.string, dvar->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto skip_extra_zones_stub = utils::hook::assemble([](utils::hook::assembler& a) -> void
|
||||
{
|
||||
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);
|
||||
});
|
||||
|
||||
game::Sys_File sys_create_file_stub(game::Sys_Folder folder, const char* base_filename)
|
||||
{
|
||||
auto result = sys_create_file_hook.invoke<game::Sys_File>(folder, base_filename);
|
||||
|
||||
if (result.handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!is_using_mods())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// .ff extension was added previously
|
||||
if (!std::strcmp(base_filename, "mod.ff") && mods::db_mod_file_exists())
|
||||
{
|
||||
char file_path[MAX_PATH]{};
|
||||
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(file_path), file_path);
|
||||
result.handle = game::Sys_OpenFileReliable(file_path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void db_load_x_assets_stub(game::XZoneInfo* zone_info, unsigned int zone_count, game::DBSyncMode sync_mode)
|
||||
{
|
||||
std::vector<game::XZoneInfo> zones(zone_info, zone_info + zone_count);
|
||||
|
||||
if (db_mod_file_exists())
|
||||
{
|
||||
zones.emplace_back("mod", game::DB_ZONE_COMMON | game::DB_ZONE_CUSTOM, 0);
|
||||
}
|
||||
|
||||
game::DB_LoadXAssets(zones.data(), static_cast<unsigned int>(zones.size()), sync_mode);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_using_mods()
|
||||
{
|
||||
return (*dvars::fs_gameDirVar) && *(*dvars::fs_gameDirVar)->current.string;
|
||||
}
|
||||
|
||||
bool db_mod_file_exists()
|
||||
{
|
||||
if (!*(*dvars::fs_gameDirVar)->current.string)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char filename[MAX_PATH]{};
|
||||
db_build_os_path_from_source("mod", game::FFD_MOD_DIR, sizeof(filename), filename);
|
||||
|
||||
if (auto zone_file = game::Sys_OpenFileReliable(filename); zone_file != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
::CloseHandle(zone_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
static_assert(sizeof(game::Sys_File) == 8);
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
dvars::fs_gameDirVar = reinterpret_cast<game::dvar_t**>(SELECT_VALUE(0x14A6A7D98, 0x14B20EB48));
|
||||
|
||||
// Remove DVAR_INIT from fs_game
|
||||
utils::hook::set<std::uint32_t>(SELECT_VALUE(0x14036137F + 2, 0x1404AE4CB + 2), SELECT_VALUE(game::DVAR_FLAG_NONE, game::DVAR_FLAG_SERVERINFO));
|
||||
|
||||
utils::hook::inject(SELECT_VALUE(0x140361391 + 3, 0x1404AE4D6 + 3), &fs_game_dir_domain_func);
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::nop(0x140271B54, 15);
|
||||
utils::hook::jump(0x140271B54, skip_extra_zones_stub, true);
|
||||
|
||||
// Add custom zone paths
|
||||
sys_create_file_hook.create(game::Sys_CreateFile, sys_create_file_stub);
|
||||
|
||||
// Load mod.ff
|
||||
utils::hook::call(0x1405A562A, db_load_x_assets_stub); // R_LoadGraphicsAssets According to myself but I don't remember where I got it from
|
||||
|
||||
command::add("loadmod", [](const command::params& params) -> void
|
||||
{
|
||||
if (params.size() != 2)
|
||||
{
|
||||
console::info("USAGE: %s \"mods/<mod name>\"", params.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
std::string mod_name = utils::string::to_lower(params.get(1));
|
||||
|
||||
if (!mod_name.empty() && !mod_name.starts_with("mods/"))
|
||||
{
|
||||
mod_name.insert(0, "mods/");
|
||||
}
|
||||
|
||||
// change fs_game if needed
|
||||
if (mod_name != (*dvars::fs_gameDirVar)->current.string)
|
||||
{
|
||||
game::Dvar_SetString((*dvars::fs_gameDirVar), mod_name.c_str());
|
||||
command::execute("vid_restart\n");
|
||||
}
|
||||
});
|
||||
|
||||
command::add("unloadmod", []([[maybe_unused]] const command::params& params) -> void
|
||||
{
|
||||
if (*dvars::fs_gameDirVar == nullptr || *(*dvars::fs_gameDirVar)->current.string == '\0')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
game::Dvar_SetString(*dvars::fs_gameDirVar, "");
|
||||
command::execute("vid_restart\n");
|
||||
});
|
||||
|
||||
// TODO: without a way to monitor all the ways fs_game can be changed there is no way to detect when we
|
||||
// should unregister the path from the internal filesystem we use
|
||||
// HINT: It could be done in fs_game_dir_domain_func, but I haven't tested if that's the best place to monitor for changes and register/unregister the mods folder
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(mods::component)
|
7
src/client/component/mods.hpp
Normal file
7
src/client/component/mods.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace mods
|
||||
{
|
||||
bool is_using_mods();
|
||||
bool db_mod_file_exists();
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "game/engine/sv_game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "party.hpp"
|
||||
@@ -159,7 +160,7 @@ namespace party
|
||||
if (game::mp::g_entities[i].client)
|
||||
{
|
||||
char client_name[16] = {0};
|
||||
strncpy_s(client_name, game::mp::g_entities[i].client->name, sizeof(client_name));
|
||||
game::I_strncpyz(client_name, game::mp::g_entities[i].client->name, sizeof(client_name));
|
||||
game::I_CleanStr(client_name);
|
||||
|
||||
if (client_name == name)
|
||||
@@ -226,64 +227,6 @@ namespace party
|
||||
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()
|
||||
{
|
||||
return sv_maxclients;
|
||||
@@ -314,36 +257,6 @@ namespace party
|
||||
// enable custom kick reason in GScr_KickPlayer
|
||||
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)
|
||||
{
|
||||
if (!connect_state.hostDefined)
|
||||
@@ -442,7 +355,7 @@ namespace party
|
||||
{
|
||||
scheduler::once([i, reason]
|
||||
{
|
||||
game::SV_KickClientNum(i, reason.data());
|
||||
game::SV_KickClientNum(i, reason.c_str());
|
||||
}, scheduler::pipeline::server);
|
||||
}
|
||||
return;
|
||||
@@ -456,7 +369,7 @@ namespace party
|
||||
|
||||
scheduler::once([client_num, reason]()
|
||||
{
|
||||
game::SV_KickClientNum(client_num, reason.data());
|
||||
game::SV_KickClientNum(client_num, reason.c_str());
|
||||
}, scheduler::pipeline::server);
|
||||
});
|
||||
|
||||
@@ -476,9 +389,8 @@ namespace party
|
||||
const auto message = params.join(2);
|
||||
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
|
||||
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
|
||||
printf("%s -> %i: %s\n", name, client_num, message.data());
|
||||
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()));
|
||||
printf("%s -> %i: %s\n", name, client_num, message.c_str());
|
||||
});
|
||||
|
||||
command::add("tellraw", [](const command::params& params)
|
||||
@@ -491,9 +403,8 @@ namespace party
|
||||
const auto client_num = atoi(params.get(1));
|
||||
const auto message = params.join(2);
|
||||
|
||||
game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"%s\"", 84, message.data()));
|
||||
printf("%i: %s\n", client_num, message.data());
|
||||
game::engine::SV_GameSendServerCommand(static_cast<char>(client_num), game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.c_str()));
|
||||
printf("%i: %s\n", client_num, message.c_str());
|
||||
});
|
||||
|
||||
command::add("say", [](const command::params& params)
|
||||
@@ -506,9 +417,8 @@ namespace party
|
||||
const auto message = params.join(1);
|
||||
const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string;
|
||||
|
||||
game::SV_GameSendServerCommand(
|
||||
-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data()));
|
||||
printf("%s: %s\n", name, message.data());
|
||||
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.c_str()));
|
||||
printf("%s: %s\n", name, message.c_str());
|
||||
});
|
||||
|
||||
command::add("sayraw", [](const command::params& params)
|
||||
@@ -520,8 +430,7 @@ namespace party
|
||||
|
||||
const auto message = params.join(1);
|
||||
|
||||
game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE,
|
||||
utils::string::va("%c \"%s\"", 84, message.data()));
|
||||
game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.c_str()));
|
||||
printf("%s\n", message.data());
|
||||
});
|
||||
|
||||
|
@@ -5,7 +5,6 @@ namespace party
|
||||
void reset_connect_state();
|
||||
|
||||
void connect(const game::netadr_s& target);
|
||||
void start_map(const std::string& mapname);
|
||||
|
||||
void clear_sv_motd();
|
||||
int server_client_count();
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "game/engine/sv_game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
@@ -19,6 +20,8 @@ namespace patches
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour com_quit_f_hook;
|
||||
utils::hook::detour sv_shutdown_hook;
|
||||
utils::hook::detour live_get_local_client_name_hook;
|
||||
|
||||
const char* live_get_local_client_name()
|
||||
@@ -115,7 +118,7 @@ namespace patches
|
||||
char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
|
||||
{
|
||||
std::string file_name = filename;
|
||||
if (file_name.find(".cfg") == std::string::npos)
|
||||
if (!file_name.ends_with(".cfg"))
|
||||
{
|
||||
file_name.append(".cfg");
|
||||
}
|
||||
@@ -124,6 +127,7 @@ namespace patches
|
||||
if (file.exists())
|
||||
{
|
||||
snprintf(buf, size, "%s\n", file.get_buffer().data());
|
||||
buf[size - 1] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -159,17 +163,6 @@ namespace patches
|
||||
}
|
||||
}
|
||||
|
||||
void set_client_dvar_from_server_stub(void* a1, void* a2, const char* dvar, const char* value)
|
||||
{
|
||||
if (utils::string::to_lower(dvar) == "cg_fov")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// CG_SetClientDvarFromServer
|
||||
reinterpret_cast<void(*)(void*, void*, const char*, const char*)>(0x1401BF0A0)(a1, a2, dvar, value);
|
||||
}
|
||||
|
||||
utils::hook::detour cmd_lui_notify_server_hook;
|
||||
void cmd_lui_notify_server_stub(game::mp::gentity_s* ent)
|
||||
{
|
||||
@@ -178,13 +171,27 @@ namespace patches
|
||||
const auto client = &game::mp::svs_clients[ent->s.number];
|
||||
|
||||
// 22 => "end_game"
|
||||
if (menu_id == 22 && client->header.remoteAddress.type != game::NA_LOOPBACK)
|
||||
if (menu_id == 22 && client->header.netchan.remoteAddress.type != game::NA_LOOPBACK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -239,6 +246,10 @@ namespace patches
|
||||
|
||||
static void patch_mp()
|
||||
{
|
||||
// Bypass Arxan function
|
||||
utils::hook::nop(0x14043E120, 9);
|
||||
utils::hook::jump(0x14043E120, game::engine::SV_GameSendServerCommand);
|
||||
|
||||
// Use name dvar
|
||||
live_get_local_client_name_hook.create(0x1404D47F0, &live_get_local_client_name);
|
||||
|
||||
@@ -276,9 +287,8 @@ namespace patches
|
||||
utils::hook::inject(0x1404398B2, VERSION);
|
||||
|
||||
// prevent servers overriding our fov
|
||||
utils::hook::call(0x1401BB782, set_client_dvar_from_server_stub);
|
||||
utils::hook::nop(0x1403D1195, 5);
|
||||
utils::hook::nop(0x1400FAE36, 5);
|
||||
utils::hook::nop(0x1400FAE36, 5); // Dvar_SetFloat inside LUI_CoD_LuaCall_StopFollow (function doesn't exist on dev builds)
|
||||
utils::hook::set<uint8_t>(0x14019B9B9, 0xEB);
|
||||
|
||||
// some anti tamper thing that kills performance
|
||||
@@ -306,8 +316,14 @@ namespace patches
|
||||
|
||||
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
|
||||
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()
|
||||
|
@@ -86,7 +86,7 @@ namespace rcon
|
||||
}
|
||||
|
||||
char clean_name[32]{};
|
||||
strncpy_s(clean_name, client->name, _TRUNCATE);
|
||||
game::I_strncpyz(clean_name, client->name, sizeof(clean_name));
|
||||
game::I_CleanStr(clean_name);
|
||||
|
||||
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)),
|
||||
game::SV_GetGuid(i),
|
||||
clean_name,
|
||||
network::net_adr_to_string(client->header.remoteAddress),
|
||||
client->header.remoteAddress.port)
|
||||
network::net_adr_to_string(client->header.netchan.remoteAddress),
|
||||
client->header.netchan.remoteAddress.port)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -84,6 +84,36 @@ namespace security
|
||||
|
||||
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
|
||||
@@ -91,11 +121,18 @@ namespace security
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
// sprinf
|
||||
utils::hook::call(SELECT_VALUE(0x1402203DF, 0x1402F0BAF), hud_elem_set_enum_string_stub);
|
||||
|
||||
if (game::environment::is_sp()) return;
|
||||
|
||||
// Patch vulnerability in PlayerCards_SetCachedPlayerData
|
||||
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
|
||||
utils::hook::jump(0x14044DE3A, assemble(remap_cached_entities_stub), true);
|
||||
|
||||
|
@@ -26,6 +26,8 @@ namespace steam_proxy
|
||||
ownership_state state_;
|
||||
|
||||
utils::binary_resource runner_file(RUNNER, "s1-mod-runner.exe");
|
||||
|
||||
bool is_disabled() { return true; }
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@@ -33,7 +35,7 @@ namespace steam_proxy
|
||||
public:
|
||||
void post_load() override
|
||||
{
|
||||
if (game::environment::is_dedi())
|
||||
if (game::environment::is_dedi() || is_disabled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -206,6 +206,27 @@ namespace ui_scripting
|
||||
setup_functions();
|
||||
|
||||
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["luiglobals"] = lua;
|
||||
|
||||
|
@@ -36,13 +36,13 @@ namespace dvars
|
||||
|
||||
game::dvar_t* r_fullbright = nullptr;
|
||||
|
||||
game::dvar_t* cg_legacyCrashHandling = nullptr;
|
||||
|
||||
game::dvar_t* sv_cheats = nullptr;
|
||||
|
||||
game::dvar_t* com_developer = nullptr;
|
||||
game::dvar_t* com_developer_script = nullptr;
|
||||
|
||||
game::dvar_t** fs_gameDirVar = nullptr;
|
||||
|
||||
std::string get_dvar_string(const std::string& dvar)
|
||||
{
|
||||
const auto* dvar_value = game::Dvar_FindVar(dvar.data());
|
||||
|
@@ -35,13 +35,13 @@ namespace dvars
|
||||
|
||||
extern game::dvar_t* r_fullbright;
|
||||
|
||||
extern game::dvar_t* cg_legacyCrashHandling;
|
||||
|
||||
extern game::dvar_t* sv_cheats;
|
||||
|
||||
extern game::dvar_t* com_developer;
|
||||
extern game::dvar_t* com_developer_script;
|
||||
|
||||
extern game::dvar_t** fs_gameDirVar;
|
||||
|
||||
std::string get_dvar_string(const std::string& dvar);
|
||||
bool get_dvar_bool(const std::string& dvar);
|
||||
|
||||
|
181
src/client/game/engine/sv_game.cpp
Normal file
181
src/client/game/engine/sv_game.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
#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);
|
||||
}
|
||||
}
|
7
src/client/game/engine/sv_game.hpp
Normal file
7
src/client/game/engine/sv_game.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#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);
|
||||
}
|
@@ -30,6 +30,13 @@ namespace game
|
||||
return !game::environment::is_sp() && *mp::virtualLobby_loaded == 1;
|
||||
}
|
||||
|
||||
HANDLE Sys_OpenFileReliable(const char* filename)
|
||||
{
|
||||
return ::CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr);
|
||||
}
|
||||
|
||||
namespace environment
|
||||
{
|
||||
launcher::mode mode = launcher::mode::none;
|
||||
|
@@ -66,6 +66,8 @@ namespace game
|
||||
|
||||
[[nodiscard]] bool VirtualLobby_Loaded();
|
||||
|
||||
[[nodiscard]] HANDLE Sys_OpenFileReliable(const char* filename);
|
||||
|
||||
[[nodiscard]] bool is_headless();
|
||||
void show_error(const std::string& text, const std::string& title = "Error");
|
||||
}
|
||||
|
@@ -25,6 +25,14 @@ namespace game
|
||||
typedef void(*BuiltinMethod)(scr_entref_t);
|
||||
typedef void(*BuiltinFunction)();
|
||||
|
||||
enum ControllerIndex_t
|
||||
{
|
||||
INVALID_CONTROLLER_PORT = -1,
|
||||
CONTROLLER_INDEX_0 = 0x0,
|
||||
CONTROLLER_INDEX_FIRST = 0x0,
|
||||
CONTROLLER_INDEX_COUNT = 0x1,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
VAR_UNDEFINED = 0x0,
|
||||
@@ -855,10 +863,11 @@ namespace game
|
||||
int readcount;
|
||||
int bit;
|
||||
int lastEntityRef;
|
||||
netsrc_t targetLocalNetID;
|
||||
int useZlib;
|
||||
};
|
||||
|
||||
static_assert(sizeof(msg_t) == 0x38);
|
||||
|
||||
enum errorParm
|
||||
{
|
||||
ERR_FATAL = 0,
|
||||
@@ -904,6 +913,8 @@ namespace game
|
||||
DVAR_FLAG_LATCHED = 0x2,
|
||||
DVAR_FLAG_CHEAT = 0x4,
|
||||
DVAR_FLAG_REPLICATED = 0x8,
|
||||
DVAR_FLAG_SCRIPTINFO = 0x10,
|
||||
DVAR_FLAG_SERVERINFO = 0x400,
|
||||
DVAR_FLAG_WRITE = 0x800,
|
||||
DVAR_FLAG_READ = 0x2000,
|
||||
};
|
||||
@@ -922,7 +933,7 @@ namespace game
|
||||
rgb = 9 // Color without alpha
|
||||
};
|
||||
|
||||
union dvar_value
|
||||
union DvarValue
|
||||
{
|
||||
bool enabled;
|
||||
int integer;
|
||||
@@ -965,9 +976,9 @@ namespace game
|
||||
unsigned int flags;
|
||||
dvar_type type;
|
||||
bool modified;
|
||||
dvar_value current;
|
||||
dvar_value latched;
|
||||
dvar_value reset;
|
||||
DvarValue current;
|
||||
DvarValue latched;
|
||||
DvarValue reset;
|
||||
dvar_limits domain;
|
||||
};
|
||||
|
||||
@@ -1318,6 +1329,15 @@ namespace game
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct WeaponDef
|
||||
{};
|
||||
|
||||
struct WeaponCompleteDef
|
||||
{
|
||||
const char* szInternalName;
|
||||
WeaponDef* weapDef;
|
||||
}; // Incomplete
|
||||
|
||||
union XAssetHeader
|
||||
{
|
||||
void* data;
|
||||
@@ -1416,8 +1436,78 @@ namespace game
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DB_ZONE_COMMON = 0x1,
|
||||
DB_ZONE_UI = 0x2,
|
||||
DB_ZONE_GAME = 0x4,
|
||||
DB_ZONE_LOAD = 0x8,
|
||||
DB_ZONE_DEV = 0x10,
|
||||
DB_ZONE_BASEMAP = 0x20,
|
||||
DB_ZONE_TRANSIENT_POOL = 0x40,
|
||||
DB_ZONE_TRANSIENT_MASK = 0x40,
|
||||
DB_ZONE_CUSTOM = 0x1000,
|
||||
};
|
||||
|
||||
enum FF_DIR
|
||||
{
|
||||
FFD_DEFAULT = 0x0,
|
||||
FFD_MOD_DIR = 0x1,
|
||||
FFD_USER_MAP = 0x2,
|
||||
};
|
||||
|
||||
struct Sys_File
|
||||
{
|
||||
HANDLE handle;
|
||||
};
|
||||
|
||||
enum DB_CompressorType
|
||||
{
|
||||
DB_COMPRESSOR_INVALID = 0x0,
|
||||
DB_COMPRESSOR_ZLIB = 0x1,
|
||||
DB_COMPRESSOR_UNK2 = 0x2,
|
||||
DB_COMPRESSOR_PASSTHROUGH = 0x3,
|
||||
DB_COMPRESSOR_LZX = 0x4,
|
||||
};
|
||||
|
||||
namespace mp
|
||||
{
|
||||
enum
|
||||
{
|
||||
CS_FREE = 0x0,
|
||||
CS_ZOMBIE = 0x1,
|
||||
CS_RECONNECTING = 0x2,
|
||||
CS_CONNECTED = 0x3,
|
||||
CS_CLIENTLOADING = 0x4,
|
||||
CS_ACTIVE = 0x5,
|
||||
};
|
||||
|
||||
struct cachedSnapshot_t
|
||||
{
|
||||
int archivedFrame;
|
||||
@@ -1470,33 +1560,141 @@ 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
|
||||
{
|
||||
int state;
|
||||
char __pad0[36];
|
||||
netadr_s remoteAddress;
|
||||
}; // size = ?
|
||||
int sendAsActive;
|
||||
int deltaMessage;
|
||||
int rateDelayed;
|
||||
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
|
||||
{
|
||||
clientHeader_t header;
|
||||
char __pad0[3044];
|
||||
const char* dropReason;
|
||||
char userinfo[0x400];
|
||||
int reliableSequence;
|
||||
int reliableAcknowledge;
|
||||
char __pad1[265864];
|
||||
int reliableSent;
|
||||
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
|
||||
char name[32]; // 268984
|
||||
char __pad2[8];
|
||||
char name[32];
|
||||
int lastPacketTime;
|
||||
int lastConnectTime;
|
||||
int nextSnapshotTime; // 269024
|
||||
char __pad3[544];
|
||||
char __pad3[532];
|
||||
int pureAuthentic;
|
||||
unsigned int streamSyncWaitBits;
|
||||
unsigned int streamSyncWaitTimeout;
|
||||
LiveClientDropType liveDropRequest; // 269572
|
||||
char __pad4[24];
|
||||
TestClientType testClient; // 269600
|
||||
char __pad5[391700];
|
||||
}; // 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, name) == 0x41AB8);
|
||||
static_assert(offsetof(client_t, lastPacketTime) == 0x41AD8);
|
||||
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, testClient) == 0x41D20);
|
||||
static_assert(sizeof(client_t) == 0xA1738);
|
||||
@@ -1508,7 +1706,9 @@ namespace game
|
||||
{
|
||||
char __pad[56135];
|
||||
int flags; // 56136
|
||||
};
|
||||
}; // Incomplete
|
||||
|
||||
static_assert(offsetof(gclient_s, flags) == 56136);
|
||||
|
||||
struct gentity_s
|
||||
{
|
||||
|
@@ -23,12 +23,14 @@ namespace game
|
||||
WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0, 0x1404C9690};
|
||||
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1403D19B0};
|
||||
WEAK symbol<void()> Com_Quit_f{0x1402F9390, 0x1403D08C0};
|
||||
WEAK symbol<void(const char* finalmsg)> Com_Shutdown{0x0, 0x1403D1BF0};
|
||||
|
||||
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(const char*)> Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0};
|
||||
WEAK symbol<void(int index)> Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0};
|
||||
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{0x1402EEA30, 0x1403B0020};
|
||||
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};
|
||||
|
||||
@@ -58,6 +60,7 @@ namespace game
|
||||
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<char*(const char* filename, char* buf, int size)> DB_ReadRawFile{0x140180E30, 0x140273080};
|
||||
WEAK symbol<int(XAssetType type, void** assets, int maxCount)> DB_GetAllXAssetOfType_FastFile{0x0, 0x14026F970};
|
||||
|
||||
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140370860, 0x1404BF8B0};
|
||||
WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x140370700, 0x1404BF690};
|
||||
@@ -68,7 +71,7 @@ namespace game
|
||||
WEAK symbol<void(const dvar_t* dvar, const char* string)> Dvar_SetString{0x140373DE0, 0x1404C3610};
|
||||
WEAK symbol<void(const dvar_t* dvar, bool value)> Dvar_SetBool{0x0, 0x1404C1F30};
|
||||
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x1403737D0, 0x1404C2E40};
|
||||
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x140374E10, 0x1404C47B0};
|
||||
WEAK symbol<const char*(dvar_t* dvar, DvarValue value)> Dvar_ValueToString{0x140374E10, 0x1404C47B0};
|
||||
|
||||
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned int flags)> Dvar_RegisterBool{0x140371850, 0x1404C0BE0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char** valueList, int defaultIndex, unsigned int flags)> Dvar_RegisterEnum{0x140371B30, 0x1404C0EC0};
|
||||
@@ -96,13 +99,13 @@ namespace game
|
||||
WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{0x0, 0x1403F3730};
|
||||
|
||||
WEAK symbol<void()> G_Glass_Update{0x14021D540, 0x1402EDEE0};
|
||||
|
||||
WEAK symbol<int(int clientNum)> G_GetClientScore{0, 0x1402F6AB0};
|
||||
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x140274590, 0x14033FF60};
|
||||
WEAK symbol<int(playerState_s* ps, unsigned int weapon, int dualWield, int startInAltMode, int, int, int, char)> G_GivePlayerWeapon{0x1402749B0, 0x140340470};
|
||||
WEAK symbol<void(playerState_s* ps, unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x1402217F0, 0x1402F22B0};
|
||||
WEAK symbol<void(int clientNum, unsigned int weapon)> G_SelectWeapon{0x140275380, 0x140340D50};
|
||||
WEAK symbol<int(playerState_s* ps, unsigned int weapon)> G_TakePlayerWeapon{0x1402754E0, 0x1403411D0};
|
||||
WEAK symbol<void()> G_SetupLevelWeaponDef{0x0, 0x140340DE0};
|
||||
|
||||
WEAK symbol<char*(char* string)> I_CleanStr{0x140379010, 0x1404C99A0};
|
||||
|
||||
@@ -111,6 +114,7 @@ namespace game
|
||||
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x14013F380, 0x140207C50};
|
||||
|
||||
WEAK symbol<unsigned int(int)> Live_SyncOnlineDataFlags{0x1404459A0, 0x140562830};
|
||||
WEAK symbol<void(int localControllerIndex)> LiveStorage_StatsWriteNotNeeded{0x1402F51F0, 0x1403C3CD0};
|
||||
|
||||
WEAK symbol<void(int localClientNum, const char* menuName, int isPopup, int isModal, unsigned int isExclusive)> LUI_OpenMenu{0, 0x14048E450};
|
||||
WEAK symbol<void()> LUI_EnterCriticalSection{0, 0x1400D2B10};
|
||||
@@ -184,7 +188,7 @@ namespace game
|
||||
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(int localClientNum)> SV_FastRestart{0, 0x1404374E0};
|
||||
WEAK symbol<void(int clientNum, svscmd_type type, const char* text)> SV_GameSendServerCommand{0x1403F3A70, 0x14043E120};
|
||||
WEAK symbol<int(mp::client_t* client, const unsigned char* cmd, int cmdSize)> SV_CanReplaceServerCommand{0x0, 0x140441A00};
|
||||
WEAK symbol<const char*(int clientNum)> SV_GetGuid{0, 0x14043E1E0};
|
||||
WEAK symbol<int(int clientNum)> SV_GetClientPing{0, 0x14043E1C0};
|
||||
WEAK symbol<playerState_s*(int num)> SV_GetPlayerstateForClientNum{0x1403F3AB0, 0x14043E260};
|
||||
@@ -220,6 +224,11 @@ namespace game
|
||||
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, 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<int(const char* s0, const char* s1, int n)> I_strnicmp{0x1403793E0, 0x1404C9E90};
|
||||
|
||||
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14059C5C0, 0x1406FD930};
|
||||
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x14059CD00, 0x1406FE070};
|
||||
@@ -260,6 +269,7 @@ namespace game
|
||||
WEAK symbol<XAssetEntry> g_assetEntryPool{0x142CC2400, 0x14379F100};
|
||||
WEAK symbol<int> g_poolSize{0x140804140, 0x1409B4B90};
|
||||
WEAK symbol<const char*> g_assetNames{0x140803C90, 0x1409B3180};
|
||||
WEAK symbol<int> g_compressor{0x141598580, 0x141E0B080};
|
||||
|
||||
WEAK symbol<DWORD> threadIds{0x149632EC0, 0x147DCEA30};
|
||||
|
||||
|
@@ -64,9 +64,7 @@ LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info)
|
||||
&exception_information, nullptr, nullptr))
|
||||
{
|
||||
char buf[4096]{};
|
||||
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n",
|
||||
exception_info->ExceptionRecord->ExceptionCode,
|
||||
exception_info->ExceptionRecord->ExceptionAddress);
|
||||
sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n", exception_info->ExceptionRecord->ExceptionCode, exception_info->ExceptionRecord->ExceptionAddress);
|
||||
game::show_error(buf);
|
||||
}
|
||||
|
||||
|
@@ -54,6 +54,7 @@
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
|
@@ -1,109 +0,0 @@
|
||||
#include "toast.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 6387)
|
||||
#include <wintoastlib.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
namespace utils
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool initialize()
|
||||
{
|
||||
static bool initialized = false;
|
||||
static bool success = false;
|
||||
if (initialized)
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
auto* instance = WinToastLib::WinToast::instance();
|
||||
if (!instance)
|
||||
{
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
instance->setAppName(L"s1-mod");
|
||||
instance->setAppUserModelId(
|
||||
WinToastLib::WinToast::configureAUMI(L"AlterWare", L"s1-mod", L"", L"20201212"));
|
||||
|
||||
WinToastLib::WinToast::WinToastError error;
|
||||
success = instance->initialize(&error);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
class toast_handler : public WinToastLib::IWinToastHandler
|
||||
{
|
||||
public:
|
||||
void toastActivated() const override
|
||||
{
|
||||
}
|
||||
|
||||
void toastActivated(const int /*actionIndex*/) const override
|
||||
{
|
||||
}
|
||||
|
||||
void toastFailed() const override
|
||||
{
|
||||
}
|
||||
|
||||
void toastDismissed(WinToastDismissalReason /*state*/) const override
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
toast::toast(const int64_t id)
|
||||
: id_(id)
|
||||
{
|
||||
}
|
||||
|
||||
toast::operator bool() const
|
||||
{
|
||||
return this->id_ >= 0;
|
||||
}
|
||||
|
||||
void toast::hide() const
|
||||
{
|
||||
if (this->operator bool())
|
||||
{
|
||||
WinToastLib::WinToast::instance()->hideToast(this->id_);
|
||||
}
|
||||
}
|
||||
|
||||
toast toast::show(const std::string& title, const std::string& text)
|
||||
{
|
||||
if (!initialize())
|
||||
{
|
||||
return toast{-1};
|
||||
}
|
||||
|
||||
WinToastLib::WinToastTemplate toast_template(WinToastLib::WinToastTemplate::Text02);
|
||||
toast_template.setTextField(string::convert(title), WinToastLib::WinToastTemplate::FirstLine);
|
||||
toast_template.setTextField(string::convert(text), WinToastLib::WinToastTemplate::SecondLine);
|
||||
toast_template.setDuration(WinToastLib::WinToastTemplate::Long);
|
||||
|
||||
return toast{WinToastLib::WinToast::instance()->showToast(toast_template, new toast_handler())};
|
||||
}
|
||||
|
||||
toast toast::show(const std::string& title, const std::string& text, const std::string& image)
|
||||
{
|
||||
if (!initialize())
|
||||
{
|
||||
return {-1};
|
||||
}
|
||||
|
||||
WinToastLib::WinToastTemplate toast_template(WinToastLib::WinToastTemplate::ImageAndText02);
|
||||
toast_template.setTextField(string::convert(title), WinToastLib::WinToastTemplate::FirstLine);
|
||||
toast_template.setTextField(string::convert(text), WinToastLib::WinToastTemplate::SecondLine);
|
||||
toast_template.setDuration(WinToastLib::WinToastTemplate::Long);
|
||||
toast_template.setImagePath(string::convert(image));
|
||||
|
||||
return {WinToastLib::WinToast::instance()->showToast(toast_template, new toast_handler())};
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class toast
|
||||
{
|
||||
public:
|
||||
static toast show(const std::string& title, const std::string& text);
|
||||
static toast show(const std::string& title, const std::string& text, const std::string& image);
|
||||
|
||||
operator bool() const;
|
||||
void hide() const;
|
||||
|
||||
private:
|
||||
toast(int64_t id);
|
||||
int64_t id_;
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user