From 6baee87d6eb62d7fd9a87f3657d382a9f026656c Mon Sep 17 00:00:00 2001 From: Jari van der Kaap Date: Tue, 28 Feb 2023 23:58:07 +0100 Subject: [PATCH 1/2] Added compiled GSC loading by overriding existing scripts & implemented fix for change team not being available --- data/scripts/mp/gametypes/_clientids.gsc | Bin 0 -> 732 bytes data/scripts/mp/gametypes/_clientids.gsc_raw | 33 +++++ data/ui_scripts/party/__init__.lua | 77 ++++++++++- src/client/component/script.cpp | 132 +++++++++++++++++++ src/client/component/ui_scripting.cpp | 2 +- src/client/game/structs.hpp | 4 +- 6 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 data/scripts/mp/gametypes/_clientids.gsc create mode 100644 data/scripts/mp/gametypes/_clientids.gsc_raw create mode 100644 src/client/component/script.cpp diff --git a/data/scripts/mp/gametypes/_clientids.gsc b/data/scripts/mp/gametypes/_clientids.gsc new file mode 100644 index 0000000000000000000000000000000000000000..0c2f5f36133e507e03aa29362543da6030205ac9 GIT binary patch literal 732 zcmZp04|e9|Vvx~S+g@^kfq~%&69dB?7!72z0C5ON42m@v85q_8<(U{*7}$X{2rvVA z#mPmP1trD$$@wX%Fm`cCQE74sT&g%Du_!e~KRGccCn+&GyEq;qfG%HLSzMBu3zJOF z$xO{F$xJC`FoY}2EznO-%uOw+EJ!WZkB6(!OD|4lXa>3`8HhcA*fJ=+7D#~suL7UK zRr9Ylzs%|L7r;8oC9s#n#mN!NQkFW->Y04QYNYIsw36_7DYKh`?s>G8+AH3SrZ zTrdFHuW47L#$b|SrCuDPcP91OHIRg{qE#5k3{`_!S|ED0rm~j55+6gJx??(sZ)nD7 zK1176L4qMt8^ph16jTPH<*gT+gJ^3b0for2n>$_r%}ShlQ1{*>-5y|cGORm$FBlZp zpl||<0p<6HF$Yxu#Xw;I6LY!Mk+1?N2J$yd45kMttoQ+_4-}?d*30%W0#g$Ql75i9 z4v+wap~l1Aw-^~1nJj>Om{}lsA0Pn=LzsL7kPq@Z$S$CIrFB650-zm0ih%`4fWi;N Ho&Y2OWr5T7 literal 0 HcmV?d00001 diff --git a/data/scripts/mp/gametypes/_clientids.gsc_raw b/data/scripts/mp/gametypes/_clientids.gsc_raw new file mode 100644 index 00000000..1e8ac023 --- /dev/null +++ b/data/scripts/mp/gametypes/_clientids.gsc_raw @@ -0,0 +1,33 @@ +#using scripts\codescripts\struct; + +#using scripts\shared\callbacks_shared; +#using scripts\shared\system_shared; + +#insert scripts\shared\shared.gsh; + +#namespace clientids; + +REGISTER_SYSTEM( "clientids", &__init__, undefined ) + +function __init__() +{ + callback::on_start_gametype( &init ); + callback::on_connect( &on_player_connect ); +} + +function init() +{ + level.clientid = 0; + wait 0.5; + level.allow_teamchange = "1"; +} + +function on_player_connect() +{ + self.clientid = matchRecordNewPlayer( self ); + if ( !isdefined( self.clientid ) || self.clientid == -1 ) + { + self.clientid = level.clientid; + level.clientid++; + } +} \ No newline at end of file diff --git a/data/ui_scripts/party/__init__.lua b/data/ui_scripts/party/__init__.lua index 5416f138..b51b1fc0 100644 --- a/data/ui_scripts/party/__init__.lua +++ b/data/ui_scripts/party/__init__.lua @@ -6,4 +6,79 @@ CoD.IsTeamChangeAllowed = function() else return false end -end \ No newline at end of file +end + +DataSources.StartMenuGameOptions = ListHelper_SetupDataSource("StartMenuGameOptions", function (controller) + local options = {} + if Engine.IsDemoPlaying() then + if not IsDemoRestrictedBasicMode() then + table.insert(options, {models = {displayText = Engine.ToUpper(Engine.Localize("MENU_UPLOAD_CLIP", Engine.GetDemoSegmentCount())), action = StartMenuUploadClip, disabledFunction = IsUploadClipButtonDisabled}, properties = {hideHelpItemLabel = true}}) + end + if Engine.IsDemoHighlightReelMode() then + table.insert(options, {models = {displayText = Engine.ToUpper(Engine.Localize("MENU_DEMO_CUSTOMIZE_HIGHLIGHT_REEL")), action = StartMenuOpenCustomizeHighlightReel, disabledFunction = IsCustomizeHighlightReelButtonDisabled}}) + end + table.insert(options, {models = {displayText = Engine.ToUpper(Engine.ToUpper(Engine.Localize("MENU_JUMP_TO_START"))), action = StartMenuJumpToStart, disabledFunction = IsJumpToStartButtonDisabled}, properties = {hideHelpItemLabel = true}}) + local endDemoButtonText = nil + if Engine.IsDemoClipPlaying() then + endDemoButtonText = Engine.ToUpper(Engine.Localize("MENU_END_CLIP")) + else + endDemoButtonText = Engine.ToUpper(Engine.Localize("MENU_END_FILM")) + end + table.insert(options, {models = {displayText = Engine.ToUpper(endDemoButtonText), action = StartMenuEndDemo}}) + elseif CoD.isCampaign then + table.insert(options, {models = {displayText = "MENU_RESUMEGAME_CAPS", action = StartMenuGoBack_ListElement}}) + local inTrainingSim = CoD.SafeGetModelValue(Engine.GetModelForController(controller), "safehouse.inTrainingSim") + if not inTrainingSim then + inTrainingSim = 0 + end + if Engine.IsLobbyHost(Enum.LobbyType.LOBBY_TYPE_GAME) then + if not CoD.isSafehouse and controller == Engine.GetPrimaryController() then + table.insert(options, {models = {displayText = "MENU_RESTART_MISSION_CAPS", action = RestartMission}}) + if LUI.DEV ~= nil then + table.insert(options, {models = {displayText = "MENU_RESTART_CHECKPOINT_CAPS", action = RestartFromCheckpoint}}) + end + end + if controller == Engine.GetPrimaryController() then + table.insert(options, {models = {displayText = "MENU_CHANGE_DIFFICULTY_CAPS", action = OpenDifficultySelect}}) + end + if CoD.isSafehouse and inTrainingSim == 1 then + table.insert(options, {models = {displayText = "MENU_END_TRAINING_SIM", action = EndTrainingSim}}) + elseif controller == Engine.GetPrimaryController() then + if Engine.DvarBool(0, "ui_blocksaves") then + table.insert(options, {models = {displayText = "MENU_EXIT_CAPS", action = SaveAndQuitGame}}) + else + table.insert(options, {models = {displayText = "MENU_SAVE_AND_QUIT_CAPS", action = SaveAndQuitGame}}) + end + end + elseif CoD.isSafehouse and inTrainingSim == 1 then + table.insert(options, {models = {displayText = "MENU_END_TRAINING_SIM", action = EndTrainingSim}}) + else + table.insert(options, {models = {displayText = "MENU_LEAVE_PARTY_AND_EXIT_CAPS", action = QuitGame}}) + end + elseif CoD.isMultiplayer then + if Engine.Team(controller, "name") ~= "TEAM_SPECTATOR" and Engine.GetGametypeSetting("disableClassSelection") ~= 1 then + table.insert(options, {models = {displayText = "MPUI_CHOOSE_CLASS_BUTTON_CAPS", action = ChooseClass}}) + end + if not Engine.IsVisibilityBitSet(controller, Enum.UIVisibilityBit.BIT_ROUND_END_KILLCAM) and not Engine.IsVisibilityBitSet(controller, Enum.UIVisibilityBit.BIT_FINAL_KILLCAM) and CoD.IsTeamChangeAllowed() then + table.insert(options, {models = {displayText = "MPUI_CHANGE_TEAM_BUTTON_CAPS", action = ChooseTeam}}) + end + if controller == 0 then + local endGameText = "MENU_QUIT_GAME_CAPS" + if Engine.IsLobbyHost(Enum.LobbyType.LOBBY_TYPE_GAME) and not CoD.isOnlineGame() then + endGameText = "MENU_END_GAME_CAPS" + end + table.insert(options, {models = {displayText = endGameText, action = QuitGame_MP}}) + end + elseif CoD.isZombie then + table.insert(options, {models = {displayText = "MENU_RESUMEGAME_CAPS", action = StartMenuGoBack_ListElement}}) + if Engine.IsLobbyHost(Enum.LobbyType.LOBBY_TYPE_GAME) and (not not (Engine.SessionModeIsMode(CoD.SESSIONMODE_SYSTEMLINK) == true) or Engine.SessionModeIsMode(CoD.SESSIONMODE_OFFLINE) == true) then + table.insert(options, {models = {displayText = "MENU_RESTART_LEVEL_CAPS", action = RestartGame}}) + end + if Engine.IsLobbyHost(Enum.LobbyType.LOBBY_TYPE_GAME) == true then + table.insert(options, {models = {displayText = "MENU_END_GAME_CAPS", action = QuitGame_MP}}) + else + table.insert(options, {models = {displayText = "MENU_QUIT_GAME_CAPS", action = QuitGame_MP}}) + end + end + return options +end, true) \ No newline at end of file diff --git a/src/client/component/script.cpp b/src/client/component/script.cpp new file mode 100644 index 00000000..b18e02c2 --- /dev/null +++ b/src/client/component/script.cpp @@ -0,0 +1,132 @@ +#include +#include "loader/component_loader.hpp" +#include "game/game.hpp" + +#include "scheduler.hpp" + +#include +#include +#include + +namespace script +{ + namespace + { + utils::hook::detour db_findxassetheader_hook; + + struct globals_t + { + std::unordered_map loaded_scripts; + }; + + globals_t globals; + + game::RawFile* get_loaded_script(const std::string& name) + { + const auto itr = globals.loaded_scripts.find(name); + return (itr == globals.loaded_scripts.end()) ? NULL : itr->second; + } + + void print_loading_script(const std::string& name) + { + printf("Loading GSC script '%s'\n", name.data()); + } + + void load_script(std::string& name, const std::string& data) + { + auto& allocator = *utils::memory::get_allocator(); + auto file_string = static_cast(allocator.allocate(data.length())); + std::memcpy(file_string, data.data(), data.length()); + + const utils::nt::library host{}; + auto appdata_path = (game::get_appdata_path() / "data/").generic_string(); + auto host_path = (host.get_folder() / "boiii/").generic_string(); + std::string::size_type i = name.find(appdata_path); + if (i != std::string::npos) + name.erase(i, appdata_path.length()); + i = name.find(host_path); + if (i != std::string::npos) + name.erase(i, host_path.length()); + + auto rawfile = static_cast(allocator.allocate(24)); + rawfile->name = name.c_str(); + rawfile->buffer = file_string; + rawfile->len = data.length(); + + globals.loaded_scripts[name] = rawfile; + } + + void load_scripts_folder(const std::string& script_dir) + { + if (!utils::io::directory_exists(script_dir)) + { + return; + } + + const auto scripts = utils::io::list_files(script_dir); + + for (const auto& script : scripts) + { + std::string data; + auto script_file = script.generic_string(); + if (!std::filesystem::is_directory(script) && utils::io::read_file(script_file, &data)) + { + print_loading_script(script_file); + load_script(script_file, data); + } + else if (std::filesystem::is_directory(script)) + { + load_scripts_folder(script_file); + } + } + } + + void load_scripts() + { + globals = {}; + const utils::nt::library host{}; + load_scripts_folder((game::get_appdata_path() / "data/scripts").string()); + load_scripts_folder((host.get_folder() / "boiii/scripts").string()); + } + + game::RawFile* db_findxassetheader_stub(game::XAssetType type, const char* name, bool errorIfMissing, int waitTime) + { + if (type != game::ASSET_TYPE_SCRIPTPARSETREE) + { + return db_findxassetheader_hook.invoke(type, name, errorIfMissing, waitTime); + } + + auto asset_header = db_findxassetheader_hook.invoke(type, name, errorIfMissing, waitTime); + if (globals.loaded_scripts.contains(name)) + { + auto script = get_loaded_script(name); + + utils::hook::copy((void*)(script->buffer + 0x8), asset_header->buffer + 0x8, 4); // Copy over the checksum of the original script + + return script; + } + + return asset_header; + } + } + + struct component final : generic_component + { + void post_unpack() override + { + if (game::is_server()) + { + load_scripts(); + } + else + { + scheduler::once(load_scripts, scheduler::pipeline::renderer); + } + + db_findxassetheader_hook.create(game::select(0x141420ED0, 0x1401D5FB0), db_findxassetheader_stub); + } + + }; +}; + +REGISTER_COMPONENT(script::component) diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 0c9a9388..7a3a522c 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -313,7 +313,7 @@ namespace ui_scripting globals.raw_script_name = target_script; return game::XAssetHeader{ - .luaFile = reinterpret_cast(1) // + .luaFile = reinterpret_cast(1) // }; } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 0eecbf5f..2601428e 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -556,7 +556,7 @@ namespace game ASSET_TYPE_FULL_COUNT = 0x6C, }; - struct LuaFile + struct RawFile { const char* name; int len; @@ -1684,7 +1684,7 @@ namespace game BeamDef* beamDef; StreamerHint* streamerHint;*/ void* data; - LuaFile* luaFile; + RawFile* luaFile; }; struct XAsset From 573534415219c74c4fd08e08109cd409396e6207 Mon Sep 17 00:00:00 2001 From: Jari van der Kaap Date: Wed, 1 Mar 2023 00:13:25 +0100 Subject: [PATCH 2/2] fix: Fixed casting error --- src/client/component/script.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/component/script.cpp b/src/client/component/script.cpp index b18e02c2..b7559bec 100644 --- a/src/client/component/script.cpp +++ b/src/client/component/script.cpp @@ -51,7 +51,7 @@ namespace script auto rawfile = static_cast(allocator.allocate(24)); rawfile->name = name.c_str(); rawfile->buffer = file_string; - rawfile->len = data.length(); + rawfile->len = static_cast(data.length()); globals.loaded_scripts[name] = rawfile; }