From 8d33957ca7cc7132bb6fe16d9e03e92a8b129eca Mon Sep 17 00:00:00 2001 From: Delta <46466697+DeltaGW2@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:01:58 +0200 Subject: [PATCH 1/5] raise volatile event per addon --- src/Consts.cpp | 2 +- src/Consts.h | 2 +- src/Loader/Loader.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Consts.cpp b/src/Consts.cpp index e9fdb70..98926e3 100644 --- a/src/Consts.cpp +++ b/src/Consts.cpp @@ -54,7 +54,7 @@ const char* EV_WINDOW_RESIZED = "EV_WINDOW_RESIZED"; const char* EV_MUMBLE_IDENTITY_UPDATED = "EV_MUMBLE_IDENTITY_UPDATED"; const char* EV_ADDON_LOADED = "EV_ADDON_LOADED"; const char* EV_ADDON_UNLOADED = "EV_ADDON_UNLOADED"; -const char* EV_VOLATILE_ADDONS_DISABLED = "EV_VOLATILE_ADDONS_DISABLED"; +const char* EV_VOLATILE_ADDON_DISABLED = "EV_VOLATILE_ADDON_DISABLED"; /* DataLink */ const char* DL_MUMBLE_LINK = "DL_MUMBLE_LINK"; diff --git a/src/Consts.h b/src/Consts.h index 95b3ac7..96e1c30 100644 --- a/src/Consts.h +++ b/src/Consts.h @@ -55,7 +55,7 @@ extern const char* EV_WINDOW_RESIZED; extern const char* EV_MUMBLE_IDENTITY_UPDATED; extern const char* EV_ADDON_LOADED; extern const char* EV_ADDON_UNLOADED; -extern const char* EV_VOLATILE_ADDONS_DISABLED; +extern const char* EV_VOLATILE_ADDON_DISABLED; /* DataLink */ extern const char* DL_MUMBLE_LINK; diff --git a/src/Loader/Loader.cpp b/src/Loader/Loader.cpp index 6ec2252..3ecf78e 100644 --- a/src/Loader/Loader.cpp +++ b/src/Loader/Loader.cpp @@ -387,7 +387,6 @@ namespace Loader DisableVolatileUntilUpdate = true; LogWarning(CH_LOADER, "Game updated. Current Build %d. Old Build: %d. Disabling volatile addons until they update.", gameBuild, lastGameBuild); - Events::Raise(EV_VOLATILE_ADDONS_DISABLED); std::string msg = Language.Translate("((000001))"); msg.append("\n"); msg.append(Language.Translate("((000002))")); @@ -707,6 +706,7 @@ namespace Loader std::string msg = addon->Definitions->Name; msg.append(" "); msg.append(Language.Translate("((000073))")); + Events::Raise(EV_VOLATILE_ADDON_DISABLED, &addon->Definitions->Signature); GUI::Alerts::Notify(msg.c_str()); } }) From b8b0b5a1bc22ae719df628d5c3ee2ae1bb312eae Mon Sep 17 00:00:00 2001 From: Delta <46466697+DeltaGW2@users.noreply.github.com> Date: Thu, 18 Apr 2024 20:13:38 +0200 Subject: [PATCH 2/5] fix addon config not saving --- src/GUI/Widgets/Addons/AddonItem.cpp | 2 ++ src/Loader/Loader.cpp | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/GUI/Widgets/Addons/AddonItem.cpp b/src/GUI/Widgets/Addons/AddonItem.cpp index e083f2c..a2eb2ba 100644 --- a/src/GUI/Widgets/Addons/AddonItem.cpp +++ b/src/GUI/Widgets/Addons/AddonItem.cpp @@ -226,6 +226,7 @@ namespace GUI if (ImGui::GW2::Button((Language.Translate("((000022))") + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) { aAddon->IsFlaggedForDisable = true; + Loader::SaveAddonConfig(); } ImGui::GW2::TooltipGeneric(Language.Translate("((000023))"), additionalInfo.c_str()); } @@ -242,6 +243,7 @@ namespace GUI if (ImGui::GW2::Button((Language.Translate("((000024))") + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) { aAddon->IsFlaggedForDisable = false; + Loader::SaveAddonConfig(); } ImGui::GW2::TooltipGeneric(Language.Translate("((000025))"), additionalInfo.c_str()); } diff --git a/src/Loader/Loader.cpp b/src/Loader/Loader.cpp index 3ecf78e..f014ae2 100644 --- a/src/Loader/Loader.cpp +++ b/src/Loader/Loader.cpp @@ -238,7 +238,7 @@ namespace Loader }; /* override loaded state, if it's supposed to disable next launch */ - if (addon->State == EAddonState::LoadedLOCKED && addon->IsFlaggedForDisable) + if (addon->IsFlaggedForDisable) { addonInfo["IsLoaded"] = false; } @@ -836,6 +836,16 @@ namespace Loader addon->IsWaitingForUnload = true; std::thread unloadTask([addon, aPath, isShutdown, aDoReload]() { + if (!isShutdown) + { + /* cache the flag, save that the addon will disable, then restore it */ + bool flagDisable = addon->IsFlaggedForDisable; + addon->IsFlaggedForDisable = true; + const std::lock_guard lock(Mutex); + SaveAddonConfig(); + addon->IsFlaggedForDisable = flagDisable; + } + std::chrono::steady_clock::time_point start_time = std::chrono::high_resolution_clock::now(); if (addon->Definitions->Unload) { @@ -884,11 +894,6 @@ namespace Loader unloadTask.detach(); } } - - if (!isShutdown) - { - SaveAddonConfig(); - } } void FreeAddon(const std::filesystem::path& aPath) @@ -927,6 +932,8 @@ namespace Loader addon->State = EAddonState::NotLoaded; + SaveAddonConfig(); + if (!std::filesystem::exists(aPath)) { auto it = std::find(Addons.begin(), Addons.end(), addon); From 76ee5dd28897324ac74d51bc501673aa062f542f Mon Sep 17 00:00:00 2001 From: Delta <46466697+DeltaGW2@users.noreply.github.com> Date: Sat, 20 Apr 2024 15:12:13 +0200 Subject: [PATCH 3/5] remove last modified header check --- src/Loader/Loader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Loader/Loader.cpp b/src/Loader/Loader.cpp index f014ae2..9050eeb 100644 --- a/src/Loader/Loader.cpp +++ b/src/Loader/Loader.cpp @@ -1266,7 +1266,7 @@ namespace Loader return false; } - auto lmHeader = resultMd5Req->headers.find("Last-Modified"); + /*auto lmHeader = resultMd5Req->headers.find("Last-Modified"); if (lmHeader != resultMd5Req->headers.end()) { @@ -1285,7 +1285,7 @@ namespace Loader { return false; } - } + }*/ size_t bytesWritten = 0; std::ofstream fileUpdate(pathUpdate, std::ofstream::binary); From 22534984b8d3f8911cb46992ebc71489d9060421 Mon Sep 17 00:00:00 2001 From: Delta <46466697+DeltaGW2@users.noreply.github.com> Date: Sat, 20 Apr 2024 17:05:30 +0200 Subject: [PATCH 4/5] implement OnlyLoadDuringGameLaunchSequence - refactored some AddonItem code - added state IsGameLaunchSequence - added ButtonDisabled for GW2 style - implemented IsGameLaunchSequence flag for addons, preventing the addon to load later than the initial launch --- src/GUI/Widgets/Addons/AddonItem.cpp | 35 +++++++++-------- src/Loader/Addon.h | 1 + src/Loader/EAddonFlags.h | 7 ++-- src/Loader/Loader.cpp | 57 +++++++++++++++++++++++++--- src/Resources/Locales | 2 +- src/Shared.cpp | 2 + src/Shared.h | 2 + src/imgui/imgui_extensions.h | 16 ++++++++ 8 files changed, 97 insertions(+), 25 deletions(-) diff --git a/src/GUI/Widgets/Addons/AddonItem.cpp b/src/GUI/Widgets/Addons/AddonItem.cpp index a2eb2ba..308cdfc 100644 --- a/src/GUI/Widgets/Addons/AddonItem.cpp +++ b/src/GUI/Widgets/Addons/AddonItem.cpp @@ -15,6 +15,8 @@ #include "Events/EventHandler.h" +#include "GUI/Widgets/Alerts/Alerts.h" + #include "imgui.h" #include "imgui_extensions.h" @@ -213,7 +215,7 @@ namespace GUI ImGui::GW2::TooltipGeneric(Language.Translate("((000021))")); } } - else if (aAddon->State == EAddonState::LoadedLOCKED && aAddon->IsFlaggedForDisable == false) + else if (aAddon->State == EAddonState::LoadedLOCKED) { std::string additionalInfo; @@ -223,29 +225,31 @@ namespace GUI additionalInfo.append(Language.Translate("((000021))")); } - if (ImGui::GW2::Button((Language.Translate("((000022))") + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) + if (ImGui::GW2::Button((Language.Translate(aAddon->IsFlaggedForDisable ? "((000024))" : "((000022))") + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) { - aAddon->IsFlaggedForDisable = true; + aAddon->IsFlaggedForDisable = !aAddon->IsFlaggedForDisable; Loader::SaveAddonConfig(); } - ImGui::GW2::TooltipGeneric(Language.Translate("((000023))"), additionalInfo.c_str()); + ImGui::GW2::TooltipGeneric(Language.Translate(aAddon->IsFlaggedForDisable ? "((000025))" : "((000023))"), additionalInfo.c_str()); } - else if (aAddon->State == EAddonState::LoadedLOCKED && aAddon->IsFlaggedForDisable == true) + else if (aAddon->State == EAddonState::NotLoaded && (aAddon->Definitions->HasFlag(EAddonFlags::OnlyLoadDuringGameLaunchSequence) || aAddon->Definitions->Signature == 0xFFF694D1) && !IsGameLaunchSequence) { - std::string additionalInfo; - - if (RequestedAddons.size() > 0) + /* if it's too late to load this addon */ + if (ImGui::GW2::Button((Language.Translate(aAddon->IsFlaggedForEnable ? "((000020))" : "((000024))") + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) { - additionalInfo.append("\n"); - additionalInfo.append(Language.Translate("((000021))")); + aAddon->IsFlaggedForEnable = !aAddon->IsFlaggedForEnable; + if (aAddon->IsFlaggedForEnable) + { + std::string msg = aAddon->Definitions->Name; + msg.append(" "); + msg.append(Language.Translate("((000080))")); + GUI::Alerts::Notify(msg.c_str()); + } } - - if (ImGui::GW2::Button((Language.Translate("((000024))") + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) + if (aAddon->IsFlaggedForEnable) { - aAddon->IsFlaggedForDisable = false; - Loader::SaveAddonConfig(); + ImGui::GW2::TooltipGeneric(Language.Translate("((000025))"), ""); } - ImGui::GW2::TooltipGeneric(Language.Translate("((000025))"), additionalInfo.c_str()); } else if (aAddon->State == EAddonState::NotLoaded) { @@ -260,6 +264,7 @@ namespace GUI ImGui::GW2::TooltipGeneric(Language.Translate("((000021))")); } } + if (aAddon->Definitions->Provider == EUpdateProvider::GitHub && aAddon->Definitions->UpdateLink) { if (ImGui::GW2::Button((Language.Translate("((000030))") + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) diff --git a/src/Loader/Addon.h b/src/Loader/Addon.h index b81fb9b..0da57a5 100644 --- a/src/Loader/Addon.h +++ b/src/Loader/Addon.h @@ -29,6 +29,7 @@ struct Addon bool IsWaitingForUnload; bool IsFlaggedForUninstall; bool IsFlaggedForDisable; + bool IsFlaggedForEnable; }; #endif \ No newline at end of file diff --git a/src/Loader/EAddonFlags.h b/src/Loader/EAddonFlags.h index d13660e..89dd5f4 100644 --- a/src/Loader/EAddonFlags.h +++ b/src/Loader/EAddonFlags.h @@ -3,9 +3,10 @@ enum class EAddonFlags { - None = 0, - IsVolatile = 1, /* is hooking functions or doing anything else that's volatile and game build dependant */ - DisableHotloading = 2 /* prevents unloading at runtime, aka. will require a restart if updated, etc. */ + None = 0, + IsVolatile = 1 << 0, /* is hooking functions or doing anything else that's volatile and game build dependant */ + DisableHotloading = 1 << 1, /* prevents unloading at runtime, aka. will require a restart if updated, etc. */ + OnlyLoadDuringGameLaunchSequence = 1 << 2 /* prevents loading later than character select, aka will require restart to get loaded */ }; EAddonFlags operator|(EAddonFlags lhs, EAddonFlags rhs); diff --git a/src/Loader/Loader.cpp b/src/Loader/Loader.cpp index 9050eeb..d416d8f 100644 --- a/src/Loader/Loader.cpp +++ b/src/Loader/Loader.cpp @@ -74,7 +74,7 @@ namespace Loader std::vector WhitelistedAddons; /* List of addons that should be loaded on initial startup. */ - bool DisableVolatileUntilUpdate = false; + bool DisableVolatileUntilUpdate = false; void Initialize() { @@ -112,6 +112,25 @@ namespace Loader std::thread arclib(ArcDPS::GetPluginLibrary); arclib.detach(); + std::thread checkLaunchSequence([]() + { + Sleep(5000); + int nothingCounter = 0; + while (!IsGameplay) + { + /* do nothing */ + Sleep(1); + nothingCounter++; + + if (nothingCounter > 10000) + { + break; + } + } + IsGameLaunchSequence = false; + }); + checkLaunchSequence.detach(); + LoaderThread = std::thread(ProcessChanges); LoaderThread.detach(); } @@ -232,7 +251,7 @@ namespace Loader json addonInfo = { {"Signature", addon->Definitions ? addon->Definitions->Signature : addon->MatchSignature}, - {"IsLoaded", addon->State == EAddonState::Loaded || addon->State == EAddonState::LoadedLOCKED ? true : false}, + {"IsLoaded", addon->State == EAddonState::Loaded || addon->State == EAddonState::LoadedLOCKED || addon->IsFlaggedForEnable}, {"IsPausingUpdates", addon->IsPausingUpdates}, {"IsDisabledUntilUpdate", addon->IsDisabledUntilUpdate} }; @@ -670,13 +689,22 @@ namespace Loader /* predeclare locked helper for later */ bool locked = addon->Definitions->Unload == nullptr || addon->Definitions->HasFlag(EAddonFlags::DisableHotloading); + bool onlyInitialLaunch = addon->Definitions->HasFlag(EAddonFlags::OnlyLoadDuringGameLaunchSequence) || addon->Definitions->Signature == 0xFFF694D1; + // FIXME: remove the arcdps check as soon as it adds the flag + + /* override shoudLoad */ + if (!IsGameLaunchSequence && onlyInitialLaunch) + { + shouldLoad = false; + } /* don't update when reloading; check when: it's waiting to re-enable but wasn't manually invoked, it's not pausing updates atm */ if (!aIsReload && ((addon->IsDisabledUntilUpdate && isInitialLoad) || !addon->IsPausingUpdates)) { std::filesystem::path tmpPath = aPath.string(); - std::thread([tmpPath, addon, locked, shouldLoad]() + std::thread([tmpPath, addon, locked, shouldLoad, onlyInitialLaunch]() { + Sleep(5000); if (UpdateAddon(tmpPath, addon->Definitions->Signature, addon->Definitions->Name, addon->Definitions->Version, addon->Definitions->Provider, addon->Definitions->UpdateLink != nullptr ? addon->Definitions->UpdateLink : "")) @@ -693,12 +721,29 @@ namespace Loader SaveAddonConfig(); // save the DUU state } } - QueueAddon(ELoaderAction::Reload, tmpPath); + + /* only call reload if it wasn't unloaded */ + if (!onlyInitialLaunch) + { + QueueAddon(ELoaderAction::Reload, tmpPath); + } + else + { + std::string msg = addon->Definitions->Name; + msg.append(" "); + msg.append(Language.Translate("((000079))")); + GUI::Alerts::Notify(msg.c_str()); + } } else if (locked && shouldLoad && !addon->IsDisabledUntilUpdate) // if addon is locked and not DUU { // the lock state is checked because if it will be locked it means it was unloaded, prior to checking for an update - QueueAddon(ELoaderAction::Reload, tmpPath); + + /* only call reload if it wasn't unloaded */ + if (!onlyInitialLaunch) + { + QueueAddon(ELoaderAction::Reload, tmpPath); + } } else if (addon->IsDisabledUntilUpdate && DisableVolatileUntilUpdate) // if addon is DUP and the global state is too { @@ -713,7 +758,7 @@ namespace Loader .detach(); /* if will be locked, explicitly unload so the update can invoke a reload */ - if (locked) + if (locked && !onlyInitialLaunch) { FreeLibrary(addon->Module); addon->State = EAddonState::NotLoaded; diff --git a/src/Resources/Locales b/src/Resources/Locales index 0fd89d3..a049529 160000 --- a/src/Resources/Locales +++ b/src/Resources/Locales @@ -1 +1 @@ -Subproject commit 0fd89d34b3c8ec4b36a934bbb3e7eacd996c6bf0 +Subproject commit a049529e34f597be513c233ae0931d1e68d08989 diff --git a/src/Shared.cpp b/src/Shared.cpp index b81bfa4..3ee08b0 100644 --- a/src/Shared.cpp +++ b/src/Shared.cpp @@ -59,3 +59,5 @@ CAPIClient* GitHubAPI = nullptr; std::string ChangelogText; bool IsUpdateAvailable = false; + +bool IsGameLaunchSequence = true; diff --git a/src/Shared.h b/src/Shared.h index e57715c..705ce00 100644 --- a/src/Shared.h +++ b/src/Shared.h @@ -55,4 +55,6 @@ extern CAPIClient* GitHubAPI; extern std::string ChangelogText; extern bool IsUpdateAvailable; +extern bool IsGameLaunchSequence; + #endif \ No newline at end of file diff --git a/src/imgui/imgui_extensions.h b/src/imgui/imgui_extensions.h index a88ef40..1aeb974 100644 --- a/src/imgui/imgui_extensions.h +++ b/src/imgui/imgui_extensions.h @@ -194,6 +194,22 @@ namespace ImGui return ret; } + static bool ButtonDisabled(const char* label, const ImVec2& size_arg = ImVec2(0, 0)) + { + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0, 0, 0, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_BorderShadow, ImVec4(0, 0, 0, 0)); + bool ret = ButtonEx(label, size_arg, ImGuiButtonFlags_None); + ImGui::PopStyleColor(6); + ImGui::PopStyleVar(1); + + return ret; + } + static bool ContextMenuItem(const char* id, const char* label, ImTextureID bullet_texture_id, ImTextureID highlight_texture_id, const ImVec2& size_arg = ImVec2(0, 0)) { float itemWidth = ImGui::GetWindowContentRegionWidth(); From 93a163f02e7222f590f82337198104512a93da82 Mon Sep 17 00:00:00 2001 From: Delta <46466697+DeltaGW2@users.noreply.github.com> Date: Sat, 20 Apr 2024 17:37:59 +0200 Subject: [PATCH 5/5] fix crash + loader misbehaving --- src/Loader/Loader.cpp | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Loader/Loader.cpp b/src/Loader/Loader.cpp index d416d8f..a78b371 100644 --- a/src/Loader/Loader.cpp +++ b/src/Loader/Loader.cpp @@ -453,7 +453,12 @@ namespace Loader if ((addon->MD5.empty() || addon->MD5 != md5) || std::filesystem::exists(updatePath)) { UpdateSwapAddon(addon->Path); - QueueAddon(ELoaderAction::Reload, addon->Path); + + // only reload if it already is loadedw + if (addon->State == EAddonState::Loaded) + { + QueueAddon(ELoaderAction::Reload, addon->Path); + } } } @@ -704,7 +709,7 @@ namespace Loader std::filesystem::path tmpPath = aPath.string(); std::thread([tmpPath, addon, locked, shouldLoad, onlyInitialLaunch]() { - Sleep(5000); + bool lShouldLoad = shouldLoad; if (UpdateAddon(tmpPath, addon->Definitions->Signature, addon->Definitions->Name, addon->Definitions->Version, addon->Definitions->Provider, addon->Definitions->UpdateLink != nullptr ? addon->Definitions->UpdateLink : "")) @@ -714,6 +719,7 @@ namespace Loader { // reset state, because it updated addon->IsDisabledUntilUpdate = false; + lShouldLoad = true; // mutex because we're async/threading { @@ -723,19 +729,22 @@ namespace Loader } /* only call reload if it wasn't unloaded */ - if (!onlyInitialLaunch) + if (lShouldLoad) { - QueueAddon(ELoaderAction::Reload, tmpPath); - } - else - { - std::string msg = addon->Definitions->Name; - msg.append(" "); - msg.append(Language.Translate("((000079))")); - GUI::Alerts::Notify(msg.c_str()); + if (!onlyInitialLaunch) + { + QueueAddon(ELoaderAction::Reload, tmpPath); + } + else + { + std::string msg = addon->Definitions->Name; + msg.append(" "); + msg.append(Language.Translate("((000079))")); + GUI::Alerts::Notify(msg.c_str()); + } } } - else if (locked && shouldLoad && !addon->IsDisabledUntilUpdate) // if addon is locked and not DUU + else if (locked && lShouldLoad && !addon->IsDisabledUntilUpdate) // if addon is locked and not DUU { // the lock state is checked because if it will be locked it means it was unloaded, prior to checking for an update