diff --git a/UE4SS/include/SettingsManager.hpp b/UE4SS/include/SettingsManager.hpp index 321497282..892069d79 100644 --- a/UE4SS/include/SettingsManager.hpp +++ b/UE4SS/include/SettingsManager.hpp @@ -25,6 +25,7 @@ namespace RC bool EnableDebugKeyBindings{false}; int64_t SecondsToScanBeforeGivingUp{30}; bool UseUObjectArrayCache{true}; + bool LatestVersionCheck{true}; } General; struct SectionEngineVersionOverride diff --git a/UE4SS/include/UE4SSProgram.hpp b/UE4SS/include/UE4SSProgram.hpp index e542f7e52..0c6fd1d94 100644 --- a/UE4SS/include/UE4SSProgram.hpp +++ b/UE4SS/include/UE4SSProgram.hpp @@ -219,6 +219,9 @@ namespace RC RC_UE4SS_API auto generate_uht_compatible_headers() -> void; RC_UE4SS_API auto generate_cxx_headers(const std::filesystem::path& output_dir) -> void; RC_UE4SS_API auto generate_lua_types(const std::filesystem::path& output_dir) -> void; + RC_UE4SS_API auto get_latest_version_check_setting() -> bool; + RC_UE4SS_API auto get_latest_ue4ss_version() -> StringType; + RC_UE4SS_API auto is_latest_ue4ss_version(StringType latest_ver) -> bool; auto get_debugging_ui() -> GUI::DebuggingGUI& { return m_debugging_gui; diff --git a/UE4SS/src/CrashDumper.cpp b/UE4SS/src/CrashDumper.cpp index d500eece8..9202ba9d3 100644 --- a/UE4SS/src/CrashDumper.cpp +++ b/UE4SS/src/CrashDumper.cpp @@ -58,7 +58,18 @@ namespace RC return EXCEPTION_CONTINUE_SEARCH; } - const std::wstring message = fmt::format(L"Crashdump written to: {}", dump_path); + std::wstring version_message = L""; + + if (UE4SSProgram::get_program().get_latest_version_check_setting()) + { + std::wstring latest_version = UE4SSProgram::get_program().get_latest_ue4ss_version(); + if (!UE4SSProgram::get_program().is_latest_ue4ss_version(latest_version)) + { + version_message = fmt::format(L"\n\nA newer version ({}) is available. Updating may solve your issue.", latest_version); + } + } + + const std::wstring message = fmt::format(L"Crashdump written to: {}{}", dump_path, version_message); MessageBoxW(NULL, message.c_str(), L"Fatal Error!", MB_OK); return EXCEPTION_EXECUTE_HANDLER; diff --git a/UE4SS/src/SettingsManager.cpp b/UE4SS/src/SettingsManager.cpp index 56557f7d4..325ac6850 100644 --- a/UE4SS/src/SettingsManager.cpp +++ b/UE4SS/src/SettingsManager.cpp @@ -57,6 +57,7 @@ namespace RC REGISTER_BOOL_SETTING(General.EnableDebugKeyBindings, section_general, EnableDebugKeyBindings) REGISTER_INT64_SETTING(General.SecondsToScanBeforeGivingUp, section_general, SecondsToScanBeforeGivingUp) REGISTER_BOOL_SETTING(General.UseUObjectArrayCache, section_general, bUseUObjectArrayCache) + REGISTER_BOOL_SETTING(General.LatestVersionCheck, section_general, bLatestVersionCheck) constexpr static File::CharType section_engine_version_override[] = STR("EngineVersionOverride"); REGISTER_INT64_SETTING(EngineVersionOverride.MajorVersion, section_engine_version_override, MajorVersion) diff --git a/UE4SS/src/UE4SSProgram.cpp b/UE4SS/src/UE4SSProgram.cpp index 2c613cb83..0b6aaca2c 100644 --- a/UE4SS/src/UE4SSProgram.cpp +++ b/UE4SS/src/UE4SSProgram.cpp @@ -56,6 +56,8 @@ #include #include +#include +#include namespace RC { @@ -226,6 +228,16 @@ namespace RC UE4SS_LIB_BETA_STARTED == 0 ? L"" : (UE4SS_LIB_IS_BETA == 0 ? L" Beta #?" : fmt::format(L" Beta #{}", UE4SS_LIB_VERSION_BETA))), to_wstring(UE4SS_LIB_BUILD_GITSHA)); + if (settings_manager.General.LatestVersionCheck) + { + StringType latest_version = get_latest_ue4ss_version(); + if (!is_latest_ue4ss_version(latest_version)) + { + if (latest_version != L"") + Output::send(STR("A newer version ({}) is available, please consider downloading from https://github.com/UE4SS-RE/RE-UE4SS/releases/latest\n"), latest_version); + } + } + #ifdef __clang__ #define UE4SS_COMPILER L"Clang" #else @@ -1460,6 +1472,126 @@ namespace RC Output::send(STR("SDK generated in {} seconds.\n"), generator_duration); } + auto UE4SSProgram::get_latest_version_check_setting() -> bool + { + return settings_manager.General.LatestVersionCheck; + } + + static auto write_callback(void* contents, size_t size, size_t nmemb, void* userp) -> size_t + { + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; + } + + auto UE4SSProgram::get_latest_ue4ss_version() -> StringType + { + CURL* curl; + CURLcode response; + std::string finalUrl; + std::string tag; + + curl = curl_easy_init(); + if (curl) + { + // This is a redirect hack to avoid having to use github api, + // which would require a user agent such as a github app to be made in UE4SS org, + // and is not a good idea to use in a public project + // CI version: curl -L -s -o /dev/null -w "%{url_effective}" "https://github.com/UE4SS-RE/RE-UE4SS/releases/latest" + curl_easy_setopt(curl, CURLOPT_URL, "https://github.com/UE4SS-RE/RE-UE4SS/releases/latest"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &finalUrl); + + response = curl_easy_perform(curl); + + if (response != CURLE_OK) + { + return L""; + } + else + { + char* effectiveUrl = nullptr; + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effectiveUrl); + if (effectiveUrl) + { + finalUrl = std::string(effectiveUrl); + } + + std::size_t lastSlashPos = finalUrl.find_last_of('/'); + if (lastSlashPos != std::string::npos) + { + tag = finalUrl.substr(lastSlashPos + 1); + } + } + + curl_easy_cleanup(curl); + } + + if (tag.empty()) + { + return L""; + } + + return to_wstring(tag); + } + + static auto split_version(const std::string& version) -> std::vector + { + std::vector parts; + std::stringstream ss(version); + std::string item; + + while (std::getline(ss, item, '.')) + { + parts.push_back(std::stoi(item)); + } + + return parts; + } + + auto UE4SSProgram::is_latest_ue4ss_version(StringType latest_ver) -> bool + { + std::string current_version = fmt::format("{}.{}.{}", UE4SS_LIB_VERSION_MAJOR, UE4SS_LIB_VERSION_MINOR, UE4SS_LIB_VERSION_HOTFIX); + std::string latest_version = to_string(latest_ver); + if (latest_version.empty()) + { + // If we can't get the latest version, assume it's not + return false; + } + if (latest_version[0] == 'v') + { + latest_version = latest_version.substr(1); + } + + std::vector current = split_version(current_version); + std::vector latest = split_version(latest_version); + + while (current.size() < latest.size()) + { + current.push_back(0); + } + + while (latest.size() < current.size()) + { + latest.push_back(0); + } + + for (size_t i = 0; i < current.size(); ++i) + { + if (latest[i] > current[i]) + { + return false; + } + else if (latest[i] < current[i]) + { + return true; + } + } + + // If both equal + return true; + } + auto UE4SSProgram::stop_render_thread() -> void { if (m_render_thread.joinable()) @@ -1519,8 +1651,7 @@ namespace RC return m_input_handler.is_keydown_event_registered(key, modifier_keys); } - auto UE4SSProgram::find_mod_by_name_internal(std::wstring_view mod_name, IsInstalled is_installed, IsStarted is_started, FMBNI_ExtraPredicate extra_predicate) - -> Mod* + auto UE4SSProgram::find_mod_by_name_internal(std::wstring_view mod_name, IsInstalled is_installed, IsStarted is_started, FMBNI_ExtraPredicate extra_predicate) -> Mod* { auto mod_exists_with_name = std::find_if(get_program().m_mods.begin(), get_program().m_mods.end(), [&](auto& elem) -> bool { bool found = true; diff --git a/UE4SS/xmake.lua b/UE4SS/xmake.lua index aa0be80ad..28cd26cdc 100644 --- a/UE4SS/xmake.lua +++ b/UE4SS/xmake.lua @@ -7,6 +7,7 @@ add_requires("glfw 3.3.9", { debug = is_mode_debug() , configs = {runtimes = get add_requires("opengl", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()} }) add_requires("glaze v2.9.5", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()} }) add_requires("fmt 10.2.1", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()} }) +add_requires("libcurl 8.7.1", { debug = is_mode_debug(), configs = {runtimes = get_mode_runtimes()} }) option("ue4ssBetaIsStarted") set_default(true) @@ -66,6 +67,8 @@ target(projectName) add_packages("fmt", { public = true }) + add_packages("libcurl", { public = true }) + add_packages("imgui", "ImGuiTextEdit", "IconFontCppHeaders", "glfw", "opengl", { public = true }) add_packages("glaze", "polyhook_2", { public = true }) diff --git a/assets/Changelog.md b/assets/Changelog.md index 823a068df..f32939358 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -15,6 +15,8 @@ UE Platform support, which allows for much easier internal implementation of new Added new installation method by allowing overriding of the location of the `UE4SS.dll`, [documentation](https://docs.ue4ss.com/installation-guide.html#overriding-install-location). - ([UE4SS #506](https://github.com/UE4SS-RE/RE-UE4SS/pull/506)) - Buckminsterfullerene +Add opt-out checking for the latest version of UE4SS during load or when a crash dump is created. ([UE4SS #617](https://github.com/UE4SS-RE/RE-UE4SS/pull/617)) - Buckminsterfullerene + ### Live View Added search filter: `IncludeClassNames`. ([UE4SS #472](https://github.com/UE4SS-RE/RE-UE4SS/pull/472)) - Buckminsterfullerene @@ -127,6 +129,9 @@ Fixes mods not loading when UE4SS initializes too late ([UE4SS #454](https://git ### Added ```ini +[General] +bLatestVersionCheck = 1 + [Hooks] HookLoadMap = 1 HookAActorTick = 1 diff --git a/assets/UE4SS-settings.ini b/assets/UE4SS-settings.ini index 5280f7944..8449282c7 100644 --- a/assets/UE4SS-settings.ini +++ b/assets/UE4SS-settings.ini @@ -23,6 +23,11 @@ SecondsToScanBeforeGivingUp = 30 ; Default: true bUseUObjectArrayCache = true +; Whether to check if the user is running the latest stable version of UE4SS +; If enabled, and user is not running latest stable version, a warning will be displayed in console and crash popup box +; Default: 1 +bLatestVersionCheck = 1 + [EngineVersionOverride] MajorVersion = MinorVersion =