diff --git a/src/client/component/motd.cpp b/src/client/component/motd.cpp index b63bc3dc..f63dc757 100644 --- a/src/client/component/motd.cpp +++ b/src/client/component/motd.cpp @@ -5,6 +5,7 @@ #include "console.hpp" #include "images.hpp" #include "command.hpp" +#include "updater.hpp" #include #include @@ -13,15 +14,14 @@ #include #include -#define MAX_FEATURED_TABS 8 - -#define CACHE_MAX_AGE 24h * 5 -#define CACHE_FILE_SIGNATURE 'CM2H' // H2MC (H2-MOD Cache) - namespace motd { namespace { + constexpr auto max_featured_tabs = 8; + constexpr auto cache_max_age = 24h * 5; // 5 days + constexpr auto cache_file_signature = 'CM2H'; // H2MC (H2-MOD Cache); + utils::concurrency::container links; utils::concurrency::container marketing; @@ -31,6 +31,12 @@ namespace motd time_t date_created; }; + struct parsed_cache_file + { + cached_file_header header{}; + std::string data; + }; + std::filesystem::path get_cache_folder() { return utils::properties::get_appdata_path() / "cache"; @@ -45,10 +51,10 @@ namespace motd void cache_file(const std::string& name, const std::string& data) { std::string buffer; - auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + const auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); cached_file_header header{}; - header.signature = CACHE_FILE_SIGNATURE; + header.signature = cache_file_signature; header.date_created = now; buffer.append(reinterpret_cast(&header), sizeof(header)); @@ -58,6 +64,42 @@ namespace motd utils::io::write_file(path, buffer, false); } + std::optional parse_cached_file_header(std::string& data) + { + if (data.size() < sizeof(cached_file_header)) + { + return {}; + } + + auto buffer = data.data(); + const auto header = reinterpret_cast(buffer); + return {*header}; + } + + std::optional parse_cached_file(const std::string& path) + { + parsed_cache_file parsed{}; + + auto data = utils::io::read_file(path); + const auto header = parse_cached_file_header(data); + + if (!header.has_value()) + { + return {}; + } + + if (header->signature != cache_file_signature) + { + return {}; + } + + const auto file_data = data.data() + sizeof(cached_file_header); + parsed.header = *header; + parsed.data = std::string{file_data, data.size() - sizeof(cached_file_header)}; + + return {parsed}; + } + std::optional read_cached_file(const std::string& name) { const auto path = get_cached_file_name(name); @@ -66,29 +108,41 @@ namespace motd return {}; } - auto now = std::chrono::system_clock::now(); - auto data = utils::io::read_file(path); - if (data.size() < sizeof(cached_file_header)) + const auto parsed = parse_cached_file(path); + if (!parsed.has_value()) { return {}; } - auto buffer = data.data(); - const auto header = reinterpret_cast(buffer); - if (header->signature != CACHE_FILE_SIGNATURE) + return {parsed->data}; + } + + void delete_old_files() + { + const auto path = get_cache_folder().generic_string(); + if (!utils::io::directory_exists(path)) { - return {}; + return; } - const auto date = std::chrono::system_clock::from_time_t(header->date_created); - if (now - date >= CACHE_MAX_AGE) - { - utils::io::remove_file(path); - return {}; - } + const auto now = std::chrono::system_clock::now(); + const auto files = utils::io::list_files(path); - const auto file_data = buffer + sizeof(cached_file_header); - return std::string{file_data, data.size() - sizeof(cached_file_header)}; + for (const auto& file : files) + { + std::string data{}; + if (!utils::io::read_file(file, &data)) + { + continue; + } + + const auto header = parse_cached_file_header(data); + const auto date = std::chrono::system_clock::from_time_t(header->date_created); + if (now - date >= cache_max_age) + { + utils::io::remove_file(file); + } + } } std::optional download_image(const std::string& url) @@ -99,6 +153,8 @@ namespace motd return {cached.value()}; } + console::debug("[HTTP] GET File \"%s\"\n", url.data()); + const auto res = utils::http::get_data(url); if (res.has_value()) { @@ -120,7 +176,6 @@ namespace motd if (image_data.has_value()) { const auto& image = image_data.value(); - console::debug("Downloaded motd image\n"); images::override_texture("motd_image", image); } } @@ -136,7 +191,7 @@ namespace motd for (const auto& [key, tab] : data["featured"].items()) { index++; - if (index >= MAX_FEATURED_TABS + 1) + if (index >= max_featured_tabs + 1) { return; } @@ -153,7 +208,6 @@ namespace motd if (image_data.has_value()) { const auto& image = image_data.value(); - console::debug("Downloaded featured tab image %i\n", index); images::override_texture(std::format("{}_{}", image_name, index), image); } }; @@ -216,6 +270,8 @@ namespace motd void init(bool load_images = true) { + delete_old_files(); + links.access([](links_map_t& map) { init_links(map); @@ -225,7 +281,7 @@ namespace motd { data.clear(); - const auto marketing_data = utils::http::get_data("https://master.fed0001.xyz/h2-mod/marketing.json"); + const auto marketing_data = updater::get_server_file("h2-mod/marketing.json"); if (marketing_data.has_value()) { try @@ -266,7 +322,7 @@ namespace motd return 0; } - return std::min(MAX_FEATURED_TABS, static_cast(data["featured"].size())); + return std::min(max_featured_tabs, static_cast(data["featured"].size())); }); } diff --git a/src/client/component/updater.cpp b/src/client/component/updater.cpp index ed1ec677..b2853b51 100644 --- a/src/client/component/updater.cpp +++ b/src/client/component/updater.cpp @@ -70,12 +70,6 @@ namespace updater std::vector garbage_files{}; }; - // remove this at some point - std::vector old_data_files = - { - {"./cdata"}, - }; - utils::concurrency::container update_data; std::string select(const std::string& main, const std::string& develop) @@ -165,31 +159,16 @@ namespace updater return utils::string::va("%i", uint32_t(time(nullptr))); } - std::optional download_file(const std::string& name) + std::optional download_data_file(const std::string& name) { - return utils::http::get_data(MASTER + select(DATA_PATH, DATA_PATH_DEV) + name + "?" + get_time_str()); + const auto file = std::format("{}{}?{}", select(DATA_PATH, DATA_PATH_DEV), name, get_time_str()); + return updater::get_server_file(file); } - bool has_old_data_files() + std::optional download_file_list() { - bool has = false; - for (const auto& file : old_data_files) - { - if (utils::io::directory_exists(file)) - { - has = true; - } - } - - return has; - } - - void delete_old_data_files() - { - for (const auto& file : old_data_files) - { - std::filesystem::remove_all(file); - } + const auto file = std::format("{}?{}", select(FILES_PATH, FILES_PATH_DEV), get_time_str()); + return updater::get_server_file(file); } bool is_update_cancelled() @@ -285,6 +264,34 @@ namespace updater } } + std::optional get_server_file(const std::string& endpoint) + { + static std::vector server_urls = + { + {"https://h2-mod.fed.cat/"}, + {"https://master.fed0001.xyz/"}, // remove this at some point + }; + + const auto try_url = [&](const std::string& base_url) + { + const auto url = base_url + endpoint; + console::debug("[HTTP] GET file \"%s\"\n", url.data()); + const auto result = utils::http::get_data(url); + return result; + }; + + for (const auto& url : server_urls) + { + const auto result = try_url(url); + if (result.has_value()) + { + return result; + } + } + + return {}; + } + void relaunch() { utils::nt::relaunch_self("-singleplayer"); @@ -342,7 +349,7 @@ namespace updater { return update_data.access([](update_data_t& data_) { - return data_.required_files.size() > 0 || data_.garbage_files.size() > 0 || has_old_data_files(); + return data_.required_files.size() > 0 || data_.garbage_files.size() > 0; }); } @@ -393,7 +400,7 @@ namespace updater scheduler::once([]() { - const auto files_data = utils::http::get_data(MASTER + select(FILES_PATH, FILES_PATH_DEV) + "?" + get_time_str()); + const auto files_data = download_file_list(); if (is_update_cancelled()) { @@ -484,8 +491,6 @@ namespace updater return; } - delete_old_data_files(); - const auto garbage_files = update_data.access>([](update_data_t& data_) { return data_.garbage_files; @@ -531,7 +536,7 @@ namespace updater console::info("[Updater] downloading file %s\n", file.name.data()); #endif - const auto data = download_file(file.name); + const auto data = download_data_file(file.name); if (is_update_cancelled()) { diff --git a/src/client/component/updater.hpp b/src/client/component/updater.hpp index ae93d3c0..a72e34b4 100644 --- a/src/client/component/updater.hpp +++ b/src/client/component/updater.hpp @@ -4,6 +4,8 @@ namespace updater { + std::optional get_server_file(const std::string& endpoint); + void relaunch(); void set_has_tried_update(bool tried); diff --git a/src/client/main.cpp b/src/client/main.cpp index 0c15e44b..153ac55e 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -125,10 +125,10 @@ FARPROC load_binary(const launcher::mode mode) return loader.load(self, data); #endif } + void remove_crash_file() { utils::io::remove_file("__h2Exe"); - utils::io::remove_file("h2_sp_patched.exe"); // remove this at some point } void verify_version()