mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2025-07-20 12:55:45 +00:00
Update checker (#132)
(with some extra spice) Maybe this should be a target for Android as well. Signed-off-by: swurl <swurl@swurl.xyz> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/132 Co-authored-by: swurl <swurl@swurl.xyz> Co-committed-by: swurl <swurl@swurl.xyz>
This commit is contained in:
parent
71df7b1451
commit
4235492079
12 changed files with 208 additions and 9 deletions
|
@ -34,10 +34,9 @@ else
|
||||||
export EXTRA_CMAKE_FLAGS=(-DYUZU_USE_PRECOMPILED_HEADERS=OFF)
|
export EXTRA_CMAKE_FLAGS=(-DYUZU_USE_PRECOMPILED_HEADERS=OFF)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# TODO(crueter): update checker
|
if [ "$RELEASE" == "1" ]; then
|
||||||
# if [ "$GITHUB_REF_TYPE" == "tag" ]; then
|
export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" -DENABLE_QT_UPDATE_CHECKER=ON)
|
||||||
# export EXTRA_CMAKE_FLAGS=($EXTRA_CMAKE_FLAGS -DENABLE_QT_UPDATE_CHECKER=ON)
|
fi
|
||||||
# fi
|
|
||||||
|
|
||||||
mkdir -p build && cd build
|
mkdir -p build && cd build
|
||||||
cmake .. -G Ninja \
|
cmake .. -G Ninja \
|
||||||
|
|
2
.github/workflows/trigger_release.yml
vendored
2
.github/workflows/trigger_release.yml
vendored
|
@ -93,7 +93,7 @@ jobs:
|
||||||
fetch-tags: true
|
fetch-tags: true
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: TARGET=appimage ./.ci/linux/build.sh v3 8
|
run: TARGET=appimage RELEASE=1 ./.ci/linux/build.sh v3 8
|
||||||
|
|
||||||
- name: Package AppImage
|
- name: Package AppImage
|
||||||
run: ./.ci/linux/package.sh v3 &> /dev/null
|
run: ./.ci/linux/package.sh v3 &> /dev/null
|
||||||
|
|
|
@ -34,9 +34,11 @@ cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "NOT ANDROID"
|
||||||
|
|
||||||
option(ENABLE_OPENGL "Enable OpenGL" ON)
|
option(ENABLE_OPENGL "Enable OpenGL" ON)
|
||||||
mark_as_advanced(FORCE ENABLE_OPENGL)
|
mark_as_advanced(FORCE ENABLE_OPENGL)
|
||||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
|
||||||
|
|
||||||
|
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||||
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
|
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
|
||||||
|
option(ENABLE_QT_UPDATE_CHECKER "Enable update checker for the Qt frontend" OFF)
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
|
||||||
|
|
||||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||||
|
@ -353,6 +355,9 @@ endif()
|
||||||
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
find_package(cpp-jwt 1.4 CONFIG)
|
find_package(cpp-jwt 1.4 CONFIG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (ENABLE_WEB_SERVICE OR ENABLE_QT_UPDATE_CHECKER)
|
||||||
find_package(httplib 0.12 MODULE COMPONENTS OpenSSL)
|
find_package(httplib 0.12 MODULE COMPONENTS OpenSSL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
2
externals/CMakeLists.txt
vendored
2
externals/CMakeLists.txt
vendored
|
@ -119,7 +119,7 @@ endif()
|
||||||
add_subdirectory(sirit)
|
add_subdirectory(sirit)
|
||||||
|
|
||||||
# httplib
|
# httplib
|
||||||
if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib)
|
if ((ENABLE_WEB_SERVICE OR ENABLE_QT_UPDATE_CHECKER) AND NOT TARGET httplib::httplib)
|
||||||
set(HTTPLIB_REQUIRE_OPENSSL ON)
|
set(HTTPLIB_REQUIRE_OPENSSL ON)
|
||||||
add_subdirectory(cpp-httplib)
|
add_subdirectory(cpp-httplib)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -266,6 +266,12 @@ file(GLOB COMPAT_LIST
|
||||||
file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
|
file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
|
||||||
file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
|
file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
|
||||||
|
|
||||||
|
if (ENABLE_QT_UPDATE_CHECKER)
|
||||||
|
target_link_libraries(yuzu PRIVATE httplib::httplib)
|
||||||
|
target_sources(yuzu PRIVATE update_checker.cpp)
|
||||||
|
target_compile_definitions(yuzu PUBLIC ENABLE_QT_UPDATE_CHECKER)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (ENABLE_QT_TRANSLATION)
|
if (ENABLE_QT_TRANSLATION)
|
||||||
set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
|
set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
|
||||||
option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
|
option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "input_common/drivers/virtual_amiibo.h"
|
#include "input_common/drivers/virtual_amiibo.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "ui_qt_amiibo_settings.h"
|
#include "ui_qt_amiibo_settings.h"
|
||||||
|
#include "web_service/web_result.h"
|
||||||
#ifdef ENABLE_WEB_SERVICE
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
#include "web_service/web_backend.h"
|
#include "web_service/web_backend.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -296,6 +296,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||||
INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
|
INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
|
||||||
tr("Forcibly disables the use of the controller applet by guests.\nWhen a guest "
|
tr("Forcibly disables the use of the controller applet by guests.\nWhen a guest "
|
||||||
"attempts to open the controller applet, it is immediately closed."));
|
"attempts to open the controller applet, it is immediately closed."));
|
||||||
|
INSERT(UISettings,
|
||||||
|
check_for_updates,
|
||||||
|
tr("Check for updates"),
|
||||||
|
tr("Whether or not to check for updates upon startup."));
|
||||||
|
|
||||||
// Linux
|
// Linux
|
||||||
INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QString());
|
INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QString());
|
||||||
|
|
|
@ -52,6 +52,10 @@
|
||||||
#include "yuzu/multiplayer/state.h"
|
#include "yuzu/multiplayer/state.h"
|
||||||
#include "yuzu/util/controller_navigation.h"
|
#include "yuzu/util/controller_navigation.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_QT_UPDATE_CHECKER
|
||||||
|
#include "yuzu/update_checker.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef YUZU_ROOM
|
#ifdef YUZU_ROOM
|
||||||
#include "dedicated_room/yuzu_room.h"
|
#include "dedicated_room/yuzu_room.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -313,6 +317,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
|
||||||
provider{std::make_unique<FileSys::ManualContentProvider>()} {
|
provider{std::make_unique<FileSys::ManualContentProvider>()} {
|
||||||
Common::FS::CreateEdenPaths();
|
Common::FS::CreateEdenPaths();
|
||||||
this->config = std::make_unique<QtConfig>();
|
this->config = std::make_unique<QtConfig>();
|
||||||
|
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
SetupSigInterrupts();
|
SetupSigInterrupts();
|
||||||
SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
|
SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
|
||||||
|
@ -410,6 +415,27 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
|
||||||
|
|
||||||
show();
|
show();
|
||||||
|
|
||||||
|
#ifdef ENABLE_QT_UPDATE_CHECKER
|
||||||
|
if (UISettings::values.check_for_updates) {
|
||||||
|
update_future = QtConcurrent::run([]() -> QString {
|
||||||
|
const bool is_prerelease =
|
||||||
|
((strstr(Common::g_build_fullname, "pre-alpha") != NULL) ||
|
||||||
|
(strstr(Common::g_build_fullname, "alpha") != NULL) ||
|
||||||
|
(strstr(Common::g_build_fullname, "beta") != NULL) ||
|
||||||
|
(strstr(Common::g_build_fullname, "rc") != NULL));
|
||||||
|
const std::optional<std::string> latest_release_tag =
|
||||||
|
UpdateChecker::GetLatestRelease(is_prerelease);
|
||||||
|
if (latest_release_tag && latest_release_tag.value() != Common::g_build_fullname) {
|
||||||
|
return QString::fromStdString(latest_release_tag.value());
|
||||||
|
}
|
||||||
|
return QString{};
|
||||||
|
});
|
||||||
|
QObject::connect(&update_watcher, &QFutureWatcher<QString>::finished, this,
|
||||||
|
&GMainWindow::OnEmulatorUpdateAvailable);
|
||||||
|
update_watcher.setFuture(update_future);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||||
system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
||||||
provider.get());
|
provider.get());
|
||||||
|
@ -4767,6 +4793,28 @@ void GMainWindow::MigrateConfigFiles() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_QT_UPDATE_CHECKER
|
||||||
|
void GMainWindow::OnEmulatorUpdateAvailable() {
|
||||||
|
QString version_string = update_future.result();
|
||||||
|
if (version_string.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QMessageBox update_prompt(this);
|
||||||
|
update_prompt.setWindowTitle(tr("Update Available"));
|
||||||
|
update_prompt.setIcon(QMessageBox::Information);
|
||||||
|
update_prompt.addButton(QMessageBox::Yes);
|
||||||
|
update_prompt.addButton(QMessageBox::Ignore);
|
||||||
|
update_prompt.setText(tr("Update %1 for Eden is available.\nWould you like to download it?")
|
||||||
|
.arg(version_string));
|
||||||
|
update_prompt.exec();
|
||||||
|
if (update_prompt.button(QMessageBox::Yes) == update_prompt.clickedButton()) {
|
||||||
|
QDesktopServices::openUrl(
|
||||||
|
QUrl(QString::fromStdString("https://github.com/eden-emulator/Releases/releases/tag/") +
|
||||||
|
version_string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_view title_version,
|
void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_view title_version,
|
||||||
std::string_view gpu_vendor) {
|
std::string_view gpu_vendor) {
|
||||||
const auto branch_name = std::string(Common::g_scm_branch);
|
const auto branch_name = std::string(Common::g_scm_branch);
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
|
||||||
#include "common/announce_multiplayer_room.h"
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "configuration/qt_config.h"
|
#include "configuration/qt_config.h"
|
||||||
#include "frontend_common/content_manager.h"
|
#include "frontend_common/content_manager.h"
|
||||||
|
@ -21,14 +20,19 @@
|
||||||
#include "user_data_migration.h"
|
#include "user_data_migration.h"
|
||||||
#include "yuzu/compatibility_list.h"
|
#include "yuzu/compatibility_list.h"
|
||||||
#include "yuzu/hotkeys.h"
|
#include "yuzu/hotkeys.h"
|
||||||
#include "yuzu/util/controller_navigation.h"
|
|
||||||
|
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
|
#include <QDBusObjectPath>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QtDBus/QDBusInterface>
|
#include <QtDBus/QDBusInterface>
|
||||||
#include <QtDBus/QtDBus>
|
#include <QtDBus/QtDBus>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_QT_UPDATE_CHECKER
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
#endif
|
||||||
|
|
||||||
class QtConfig;
|
class QtConfig;
|
||||||
class ClickableLabel;
|
class ClickableLabel;
|
||||||
class EmuThread;
|
class EmuThread;
|
||||||
|
@ -414,6 +418,10 @@ private slots:
|
||||||
void OnEmulationStopped();
|
void OnEmulationStopped();
|
||||||
void OnEmulationStopTimeExpired();
|
void OnEmulationStopTimeExpired();
|
||||||
|
|
||||||
|
#ifdef ENABLE_QT_UPDATE_CHECKER
|
||||||
|
void OnEmulatorUpdateAvailable();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString GetGameListErrorRemoving(InstalledEntryType type) const;
|
QString GetGameListErrorRemoving(InstalledEntryType type) const;
|
||||||
void RemoveBaseContent(u64 program_id, InstalledEntryType type);
|
void RemoveBaseContent(u64 program_id, InstalledEntryType type);
|
||||||
|
@ -483,6 +491,11 @@ private:
|
||||||
std::unique_ptr<PlayTime::PlayTimeManager> play_time_manager;
|
std::unique_ptr<PlayTime::PlayTimeManager> play_time_manager;
|
||||||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
|
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
|
||||||
|
|
||||||
|
#ifdef ENABLE_QT_UPDATE_CHECKER
|
||||||
|
QFuture<QString> update_future;
|
||||||
|
QFutureWatcher<QString> update_watcher;
|
||||||
|
#endif
|
||||||
|
|
||||||
MultiplayerState* multiplayer_state = nullptr;
|
MultiplayerState* multiplayer_state = nullptr;
|
||||||
|
|
||||||
GRenderWindow* render_window;
|
GRenderWindow* render_window;
|
||||||
|
|
|
@ -141,6 +141,7 @@ struct Values {
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Ui};
|
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Ui};
|
||||||
|
Setting<bool> check_for_updates{linkage, true, "check_for_updates", Category::UiGeneral};
|
||||||
|
|
||||||
// Discord RPC
|
// Discord RPC
|
||||||
Setting<bool> enable_discord_presence{linkage, false, "enable_discord_presence", Category::Ui};
|
Setting<bool> enable_discord_presence{linkage, false, "enable_discord_presence", Category::Ui};
|
||||||
|
|
109
src/yuzu/update_checker.cpp
Normal file
109
src/yuzu/update_checker.cpp
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "update_checker.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <httplib.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::optional<std::string> UpdateChecker::GetResponse(std::string url, std::string path)
|
||||||
|
{
|
||||||
|
constexpr std::size_t timeout_seconds = 15;
|
||||||
|
|
||||||
|
std::unique_ptr<httplib::Client> client = std::make_unique<httplib::Client>(url);
|
||||||
|
client->set_connection_timeout(timeout_seconds);
|
||||||
|
client->set_read_timeout(timeout_seconds);
|
||||||
|
client->set_write_timeout(timeout_seconds);
|
||||||
|
|
||||||
|
if (client == nullptr) {
|
||||||
|
LOG_ERROR(Frontend, "Invalid URL {}{}", url, path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
httplib::Request request{
|
||||||
|
.method = "GET",
|
||||||
|
.path = path,
|
||||||
|
};
|
||||||
|
|
||||||
|
client->set_follow_location(true);
|
||||||
|
const auto result = client->send(request);
|
||||||
|
if (!result) {
|
||||||
|
LOG_ERROR(Frontend, "GET to {}{} returned null", url, path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& response = result.value();
|
||||||
|
if (response.status >= 400) {
|
||||||
|
LOG_ERROR(Frontend, "GET to {}{} returned error status code: {}", url, path, response.status);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (!response.headers.contains("content-type")) {
|
||||||
|
LOG_ERROR(Frontend, "GET to {}{} returned no content", url, path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> UpdateChecker::GetLatestRelease(bool include_prereleases)
|
||||||
|
{
|
||||||
|
constexpr auto update_check_url = "http://api.github.com";
|
||||||
|
std::string update_check_path = "/repos/eden-emulator/Releases";
|
||||||
|
try {
|
||||||
|
if (include_prereleases) { // This can return either a prerelease or a stable release,
|
||||||
|
// whichever is more recent.
|
||||||
|
const auto update_check_tags_path = update_check_path + "/tags";
|
||||||
|
const auto update_check_releases_path = update_check_path + "/releases";
|
||||||
|
|
||||||
|
const auto tags_response = GetResponse(update_check_url, update_check_tags_path);
|
||||||
|
const auto releases_response = GetResponse(update_check_url, update_check_releases_path);
|
||||||
|
|
||||||
|
if (!tags_response || !releases_response)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const std::string latest_tag
|
||||||
|
= nlohmann::json::parse(tags_response.value()).at(0).at("name");
|
||||||
|
const bool latest_tag_has_release = releases_response.value().find(
|
||||||
|
fmt::format("\"{}\"", latest_tag))
|
||||||
|
!= std::string::npos;
|
||||||
|
|
||||||
|
// If there is a newer tag, but that tag has no associated release, don't prompt the
|
||||||
|
// user to update.
|
||||||
|
if (!latest_tag_has_release)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return latest_tag;
|
||||||
|
} else { // This is a stable release, only check for other stable releases.
|
||||||
|
update_check_path += "/releases/latest";
|
||||||
|
const auto response = GetResponse(update_check_url, update_check_path);
|
||||||
|
|
||||||
|
if (!response)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const std::string latest_tag = nlohmann::json::parse(response.value()).at("tag_name");
|
||||||
|
return latest_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (nlohmann::detail::out_of_range&) {
|
||||||
|
LOG_ERROR(Frontend,
|
||||||
|
"Parsing JSON response from {}{} failed during update check: "
|
||||||
|
"nlohmann::detail::out_of_range",
|
||||||
|
update_check_url,
|
||||||
|
update_check_path);
|
||||||
|
return {};
|
||||||
|
} catch (nlohmann::detail::type_error&) {
|
||||||
|
LOG_ERROR(Frontend,
|
||||||
|
"Parsing JSON response from {}{} failed during update check: "
|
||||||
|
"nlohmann::detail::type_error",
|
||||||
|
update_check_url,
|
||||||
|
update_check_path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
13
src/yuzu/update_checker.h
Normal file
13
src/yuzu/update_checker.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace UpdateChecker {
|
||||||
|
std::optional<std::string> GetResponse(std::string url, std::string path);
|
||||||
|
std::optional<std::string> GetLatestRelease(bool);
|
||||||
|
} // namespace UpdateChecker
|
Loading…
Add table
Add a link
Reference in a new issue