From 27f70e3656bf37baebe1ea5ac6302b36e9ab3bba Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 2 Feb 2019 17:49:34 +0000 Subject: [PATCH 1/7] Get basic object downloading working --- data/language/en-GB.txt | 1 + src/openrct2-ui/windows/ObjectLoadError.cpp | 88 ++++++++++++++++++++- src/openrct2/localisation/StringIds.h | 2 + src/openrct2/network/Http.cpp | 5 +- src/openrct2/object/ObjectRepository.cpp | 17 ++++ src/openrct2/object/ObjectRepository.h | 1 + 6 files changed, 109 insertions(+), 5 deletions(-) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index a5d741af45..c28b17aad7 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3746,6 +3746,7 @@ STR_6295 :MiB STR_6296 :GiB STR_6297 :TiB STR_6298 :{STRING}/sec +STR_6299 :Download all ############# # Scenarios # diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index bca8314ca4..b5275dc941 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -9,9 +9,14 @@ #include #include +#include +#include +#include #include +#include #include #include +#include #include #include #include @@ -26,7 +31,8 @@ enum WINDOW_OBJECT_LOAD_ERROR_WIDGET_IDX { WIDX_COLUMN_OBJECT_TYPE, WIDX_SCROLL, WIDX_COPY_CURRENT, - WIDX_COPY_ALL + WIDX_COPY_ALL, + WIDX_DOWNLOAD_ALL }; #define WW 450 @@ -45,8 +51,9 @@ static rct_widget window_object_load_error_widgets[] = { { WWT_TABLE_HEADER, 0, SOURCE_COL_LEFT, TYPE_COL_LEFT - 1, 57, 70, STR_OBJECT_SOURCE, STR_NONE }, // 'Object source' header { WWT_TABLE_HEADER, 0, TYPE_COL_LEFT, WW_LESS_PADDING - 1, 57, 70, STR_OBJECT_TYPE, STR_NONE }, // 'Object type' header { WWT_SCROLL, 0, 4, WW_LESS_PADDING, 70, WH - 33, SCROLL_VERTICAL, STR_NONE }, // Scrollable list area - { WWT_BUTTON, 0, 45, 220, WH - 23, WH - 10, STR_COPY_SELECTED, STR_NONE }, // Copy selected btn - { WWT_BUTTON, 0, 230, 400, WH - 23, WH - 10, STR_COPY_ALL, STR_NONE }, // Copy all btn + { WWT_BUTTON, 0, 4, 148, WH - 23, WH - 10, STR_COPY_SELECTED, STR_NONE }, // Copy selected btn + { WWT_BUTTON, 0, 152, 296, WH - 23, WH - 10, STR_COPY_ALL, STR_NONE }, // Copy all btn + { WWT_BUTTON, 0, 300, WW_LESS_PADDING, WH - 23, WH - 10, STR_DOWNLOAD_ALL, STR_NONE }, // Download all { WIDGETS_END }, }; @@ -59,6 +66,7 @@ static void window_object_load_error_scrollmouseover(rct_window *w, int32_t scro static void window_object_load_error_scrollmousedown(rct_window *w, int32_t scrollIndex, int32_t x, int32_t y); static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); +static void window_object_load_error_download_all(rct_window* w); static rct_window_event_list window_object_load_error_events = { window_object_load_error_close, @@ -191,7 +199,8 @@ rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, window = window_create_centred(WW, WH, &window_object_load_error_events, WC_OBJECT_LOAD_ERROR, 0); window->widgets = window_object_load_error_widgets; - window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_COPY_CURRENT) | (1 << WIDX_COPY_ALL); + window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_COPY_CURRENT) | (1 << WIDX_COPY_ALL) + | (1 << WIDX_DOWNLOAD_ALL); window_init_scroll_widgets(window); window->colours[0] = COLOUR_LIGHT_BLUE; @@ -245,6 +254,9 @@ static void window_object_load_error_mouseup(rct_window* w, rct_widgetindex widg case WIDX_COPY_ALL: copy_object_names_to_clipboard(w); break; + case WIDX_DOWNLOAD_ALL: + window_object_load_error_download_all(w); + break; } } @@ -335,3 +347,71 @@ static void window_object_load_error_scrollpaint(rct_window* w, rct_drawpixelinf gfx_draw_string_left(dpi, type, nullptr, COLOUR_DARK_GREEN, TYPE_COL_LEFT - 3, y); } } + +static void DownloadObject(IObjectRepository& objRepo, const std::string name, const std::string_view url) +{ + using namespace OpenRCT2::Network; + try + { + Http::Request req; + req.method = Http::Method::GET; + req.url = url; + auto response = Http::Do(req); + if (response.status == Http::Status::OK) + { + auto data = (uint8_t*)response.body.data(); + auto dataLen = response.body.size(); + objRepo.AddObjectFromFile(name, data, dataLen); + } + else + { + throw std::runtime_error("Non 200 status"); + } + } + catch (const std::exception&) + { + std::printf(" Failed to download %s\n", name.c_str()); + } +} + +static void window_object_load_error_download_all(rct_window* w) +{ + using namespace OpenRCT2::Network; + + auto& objRepo = OpenRCT2::GetContext()->GetObjectRepository(); + for (const auto& entry : _invalid_entries) + { + auto name = String::Trim(std::string(entry.name, sizeof(entry.name))); + + std::printf("Downloading %s...\n", name.c_str()); + try + { + Http::Request req; + req.method = Http::Method::GET; + req.url = "http://localhost:5000/objects/legacy/" + name; + auto response = Http::Do(req); + if (response.status == Http::Status::OK) + { + auto jresponse = Json::FromString(response.body); + if (jresponse != nullptr) + { + auto objName = json_string_value(json_object_get(jresponse, "name")); + auto downloadLink = json_string_value(json_object_get(jresponse, "download")); + if (downloadLink != nullptr) + { + DownloadObject(objRepo, objName, downloadLink); + } + json_decref(jresponse); + } + } + else + { + std::printf(" %s not found\n", name.c_str()); + } + } + catch (const std::exception&) + { + std::printf(" Failed to query %s\n", name.c_str()); + } + } +} diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 573f5a0861..8154ceb540 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3927,6 +3927,8 @@ enum STR_NETWORK_SPEED_SEC = 6298, + STR_DOWNLOAD_ALL = 6299, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/openrct2/network/Http.cpp b/src/openrct2/network/Http.cpp index a3cea59fda..6e4b74057d 100644 --- a/src/openrct2/network/Http.cpp +++ b/src/openrct2/network/Http.cpp @@ -160,7 +160,10 @@ namespace OpenRCT2::Network::Http curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type); res.status = static_cast(code); - res.content_type = std::string(content_type); + if (content_type != nullptr) + { + res.content_type = std::string(content_type); + } return res; } diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index 68a92d409f..c1bd1d0af5 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -339,6 +339,23 @@ public: } } + void AddObjectFromFile(const std::string_view& objectName, const void* data, size_t dataSize) override + { + utf8 path[MAX_PATH]; + GetPathForNewObject(path, sizeof(path), std::string(objectName).c_str()); + + log_verbose("Adding object: [%s]", objectName); + try + { + File::WriteAllBytes(path, data, dataSize); + ScanObject(path); + } + catch (const std::exception&) + { + Console::Error::WriteLine("Failed saving object: [%s] to '%s'.", objectName, path); + } + } + void ExportPackedObject(IStream* stream) override { auto chunkReader = SawyerChunkReader(stream); diff --git a/src/openrct2/object/ObjectRepository.h b/src/openrct2/object/ObjectRepository.h index f358995568..f73f2007bd 100644 --- a/src/openrct2/object/ObjectRepository.h +++ b/src/openrct2/object/ObjectRepository.h @@ -75,6 +75,7 @@ interface IObjectRepository virtual void UnregisterLoadedObject(const ObjectRepositoryItem* ori, Object* object) abstract; virtual void AddObject(const rct_object_entry* objectEntry, const void* data, size_t dataSize) abstract; + virtual void AddObjectFromFile(const std::string_view& objectName, const void* data, size_t dataSize) abstract; virtual void ExportPackedObject(IStream * stream) abstract; virtual void WritePackedObjects(IStream * stream, std::vector & objects) abstract; From 5fb3c10c3ab53b0b42a3904ee03ce0f53fc46ad6 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 3 Feb 2019 22:42:25 +0000 Subject: [PATCH 2/7] Make download objects async with progress --- data/language/en-GB.txt | 1 + src/openrct2-ui/windows/ObjectLoadError.cpp | 97 +++++++++++++++------ src/openrct2/localisation/StringIds.h | 1 + 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index c28b17aad7..aa934d9242 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3747,6 +3747,7 @@ STR_6296 :GiB STR_6297 :TiB STR_6298 :{STRING}/sec STR_6299 :Download all +STR_6300 :Downloading object ({COMMA16} / {COMMA16}): [{STRING}] ############# # Scenarios # diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index b5275dc941..281e39e803 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -18,9 +18,12 @@ #include #include #include +#include #include #include +static constexpr auto OPENRCT2_API_LEGACY_OBJECT_URL = "https://api.openrct2.io/objects/legacy/"; + // clang-format off enum WINDOW_OBJECT_LOAD_ERROR_WIDGET_IDX { WIDX_BACKGROUND, @@ -67,6 +70,7 @@ static void window_object_load_error_scrollmousedown(rct_window *w, int32_t scro static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); static void window_object_load_error_download_all(rct_window* w); +static void next_download(); static rct_window_event_list window_object_load_error_events = { window_object_load_error_close, @@ -103,6 +107,8 @@ static rct_window_event_list window_object_load_error_events = { static std::vector _invalid_entries; static int32_t highlighted_index = -1; static std::string file_path; +static int32_t _currentDownloadIndex; +static bool _downloadingObjects; /** * Returns an rct_string_id that represents an rct_object_entry's type. @@ -348,7 +354,22 @@ static void window_object_load_error_scrollpaint(rct_window* w, rct_drawpixelinf } } -static void DownloadObject(IObjectRepository& objRepo, const std::string name, const std::string_view url) +static void show_progress(const std::string& name, int32_t count, int32_t total) +{ + char str_downloading_objects[256]; + set_format_arg(0, int16_t, count); + set_format_arg(2, int16_t, total); + set_format_arg(4, char*, name.c_str()); + + format_string(str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS, gCommonFormatArgs); + + auto intent = Intent(WC_NETWORK_STATUS); + intent.putExtra(INTENT_EXTRA_MESSAGE, std::string(str_downloading_objects)); + intent.putExtra(INTENT_EXTRA_CALLBACK, []() -> void { _downloadingObjects = false; }); + context_open_intent(&intent); +} + +static void DownloadObject(const std::string name, const std::string_view url) { using namespace OpenRCT2::Network; try @@ -356,40 +377,50 @@ static void DownloadObject(IObjectRepository& objRepo, const std::string name, c Http::Request req; req.method = Http::Method::GET; req.url = url; - auto response = Http::Do(req); - if (response.status == Http::Status::OK) - { - auto data = (uint8_t*)response.body.data(); - auto dataLen = response.body.size(); - objRepo.AddObjectFromFile(name, data, dataLen); - } - else - { - throw std::runtime_error("Non 200 status"); - } + Http::DoAsync(req, [name](Http::Response response) { + if (response.status == Http::Status::OK) + { + auto data = (uint8_t*)response.body.data(); + auto dataLen = response.body.size(); + + auto& objRepo = OpenRCT2::GetContext()->GetObjectRepository(); + objRepo.AddObjectFromFile(name, data, dataLen); + } + else + { + throw std::runtime_error("Non 200 status"); + } + next_download(); + }); } catch (const std::exception&) { std::printf(" Failed to download %s\n", name.c_str()); + next_download(); } } -static void window_object_load_error_download_all(rct_window* w) +static void next_download() { using namespace OpenRCT2::Network; - auto& objRepo = OpenRCT2::GetContext()->GetObjectRepository(); - for (const auto& entry : _invalid_entries) + if (!_downloadingObjects || _currentDownloadIndex >= _invalid_entries.size()) { - auto name = String::Trim(std::string(entry.name, sizeof(entry.name))); + // Finished... + return; + } - std::printf("Downloading %s...\n", name.c_str()); - try - { - Http::Request req; - req.method = Http::Method::GET; - req.url = "http://localhost:5000/objects/legacy/" + name; - auto response = Http::Do(req); + auto& entry = _invalid_entries[_currentDownloadIndex]; + auto name = String::Trim(std::string(entry.name, sizeof(entry.name))); + show_progress(name, _currentDownloadIndex + 1, (int32_t)_invalid_entries.size()); + std::printf("Downloading %s...\n", name.c_str()); + _currentDownloadIndex++; + try + { + Http::Request req; + req.method = Http::Method::GET; + req.url = OPENRCT2_API_LEGACY_OBJECT_URL + name; + Http::DoAsync(req, [name](Http::Response response) { if (response.status == Http::Status::OK) { auto jresponse = Json::FromString(response.body); @@ -399,7 +430,7 @@ static void window_object_load_error_download_all(rct_window* w) auto downloadLink = json_string_value(json_object_get(jresponse, "download")); if (downloadLink != nullptr) { - DownloadObject(objRepo, objName, downloadLink); + DownloadObject(objName, downloadLink); } json_decref(jresponse); } @@ -407,11 +438,19 @@ static void window_object_load_error_download_all(rct_window* w) else { std::printf(" %s not found\n", name.c_str()); + next_download(); } - } - catch (const std::exception&) - { - std::printf(" Failed to query %s\n", name.c_str()); - } + }); + } + catch (const std::exception&) + { + std::printf(" Failed to query %s\n", name.c_str()); } } + +static void window_object_load_error_download_all(rct_window* w) +{ + _currentDownloadIndex = 0; + _downloadingObjects = true; + next_download(); +} diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 8154ceb540..7fa4d6fc6d 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3928,6 +3928,7 @@ enum STR_NETWORK_SPEED_SEC = 6298, STR_DOWNLOAD_ALL = 6299, + STR_DOWNLOADING_OBJECTS = 6300, // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 From 3aa55ff875448f53b7d7d65808b58ff6ae37f1d5 Mon Sep 17 00:00:00 2001 From: Ted John Date: Mon, 4 Feb 2019 18:37:49 +0000 Subject: [PATCH 3/7] Refactor and improve download of objects --- src/openrct2-ui/windows/ObjectLoadError.cpp | 285 +++++++++++++------- src/openrct2/network/Http.h | 3 +- 2 files changed, 185 insertions(+), 103 deletions(-) diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index 281e39e803..25576bbe76 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014-2018 OpenRCT2 developers + * Copyright (c) 2014-2019 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 @@ -7,6 +7,7 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ +#include #include #include #include @@ -22,7 +23,148 @@ #include #include -static constexpr auto OPENRCT2_API_LEGACY_OBJECT_URL = "https://api.openrct2.io/objects/legacy/"; +class ObjectDownloader +{ +private: + static constexpr auto OPENRCT2_API_LEGACY_OBJECT_URL = "https://api.openrct2.io/objects/legacy/"; + + std::vector _entries; + std::vector _downloadedEntries; + int32_t _currentDownloadIndex{}; + std::mutex _downloadedEntriesMutex; + + // TODO static due to INTENT_EXTRA_CALLBACK not allowing a std::function + inline static bool _downloadingObjects; + +public: + void Begin(const std::vector& entries) + { + _entries = entries; + _currentDownloadIndex = 0; + _downloadingObjects = true; + NextDownload(); + } + + bool IsDownloading() const + { + return _downloadingObjects; + } + + std::vector GetDownloadedEntries() + { + std::lock_guard guard(_downloadedEntriesMutex); + return _downloadedEntries; + } + +private: + void UpdateProgress(const std::string& name, int32_t count, int32_t total) + { + char str_downloading_objects[256]; + set_format_arg(0, int16_t, count); + set_format_arg(2, int16_t, total); + set_format_arg(4, char*, name.c_str()); + + format_string(str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS, gCommonFormatArgs); + + auto intent = Intent(WC_NETWORK_STATUS); + intent.putExtra(INTENT_EXTRA_MESSAGE, std::string(str_downloading_objects)); + intent.putExtra(INTENT_EXTRA_CALLBACK, []() -> void { _downloadingObjects = false; }); + context_open_intent(&intent); + } + + void DownloadObject(const rct_object_entry& entry, const std::string name, const std::string_view url) + { + using namespace OpenRCT2::Network; + try + { + Http::Request req; + req.method = Http::Method::GET; + req.url = url; + Http::DoAsync(req, [this, entry, name](Http::Response response) { + if (response.status == Http::Status::OK) + { + // Check that download operation hasn't been cancelled + if (_downloadingObjects) + { + auto data = (uint8_t*)response.body.data(); + auto dataLen = response.body.size(); + + auto& objRepo = OpenRCT2::GetContext()->GetObjectRepository(); + objRepo.AddObjectFromFile(name, data, dataLen); + + std::lock_guard guard(_downloadedEntriesMutex); + _downloadedEntries.push_back(entry); + } + } + else + { + throw std::runtime_error("Non 200 status"); + } + NextDownload(); + }); + } + catch (const std::exception&) + { + std::printf(" Failed to download %s\n", name.c_str()); + NextDownload(); + } + } + + void NextDownload() + { + using namespace OpenRCT2::Network; + + if (!_downloadingObjects || _currentDownloadIndex >= _entries.size()) + { + // Finished... + _downloadingObjects = false; + context_force_close_window_by_class(WC_NETWORK_STATUS); + return; + } + + auto& entry = _entries[_currentDownloadIndex]; + auto name = String::Trim(std::string(entry.name, sizeof(entry.name))); + UpdateProgress(name, _currentDownloadIndex + 1, (int32_t)_entries.size()); + std::printf("Downloading %s...\n", name.c_str()); + _currentDownloadIndex++; + try + { + Http::Request req; + req.method = Http::Method::GET; + req.url = OPENRCT2_API_LEGACY_OBJECT_URL + name; + Http::DoAsync(req, [this, entry, name](Http::Response response) { + if (response.status == Http::Status::OK) + { + auto jresponse = Json::FromString(response.body); + if (jresponse != nullptr) + { + auto objName = json_string_value(json_object_get(jresponse, "name")); + auto downloadLink = json_string_value(json_object_get(jresponse, "download")); + if (downloadLink != nullptr) + { + DownloadObject(entry, objName, downloadLink); + } + json_decref(jresponse); + } + } + else if (response.status == Http::Status::NotFound) + { + std::printf(" %s not found\n", name.c_str()); + NextDownload(); + } + else + { + std::printf(" %s query failed (status %d)\n", name.c_str(), (int32_t)response.status); + NextDownload(); + } + }); + } + catch (const std::exception&) + { + std::printf(" Failed to query %s\n", name.c_str()); + } + } +}; // clang-format off enum WINDOW_OBJECT_LOAD_ERROR_WIDGET_IDX { @@ -70,7 +212,7 @@ static void window_object_load_error_scrollmousedown(rct_window *w, int32_t scro static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); static void window_object_load_error_download_all(rct_window* w); -static void next_download(); +static void window_object_load_error_update_list(rct_window* w); static rct_window_event_list window_object_load_error_events = { window_object_load_error_close, @@ -107,8 +249,8 @@ static rct_window_event_list window_object_load_error_events = { static std::vector _invalid_entries; static int32_t highlighted_index = -1; static std::string file_path; -static int32_t _currentDownloadIndex; -static bool _downloadingObjects; +static ObjectDownloader _objDownloader; +static bool _updatedListAfterDownload; /** * Returns an rct_string_id that represents an rct_object_entry's type. @@ -230,12 +372,29 @@ static void window_object_load_error_close(rct_window* w) static void window_object_load_error_update(rct_window* w) { + w->frame_no++; + // Check if the mouse is hovering over the list if (!widget_is_highlighted(w, WIDX_SCROLL)) { highlighted_index = -1; widget_invalidate(w, WIDX_SCROLL); } + + // Remove downloaded objects from our invalid entry list + if (_objDownloader.IsDownloading()) + { + // Don't do this too often as it isn't particularly efficient + if (w->frame_no % 64 == 0) + { + window_object_load_error_update_list(w); + } + } + else if (!_updatedListAfterDownload) + { + window_object_load_error_update_list(w); + _updatedListAfterDownload = true; + } } static void window_object_load_error_mouseup(rct_window* w, rct_widgetindex widgetIndex) @@ -354,103 +513,25 @@ static void window_object_load_error_scrollpaint(rct_window* w, rct_drawpixelinf } } -static void show_progress(const std::string& name, int32_t count, int32_t total) -{ - char str_downloading_objects[256]; - set_format_arg(0, int16_t, count); - set_format_arg(2, int16_t, total); - set_format_arg(4, char*, name.c_str()); - - format_string(str_downloading_objects, sizeof(str_downloading_objects), STR_DOWNLOADING_OBJECTS, gCommonFormatArgs); - - auto intent = Intent(WC_NETWORK_STATUS); - intent.putExtra(INTENT_EXTRA_MESSAGE, std::string(str_downloading_objects)); - intent.putExtra(INTENT_EXTRA_CALLBACK, []() -> void { _downloadingObjects = false; }); - context_open_intent(&intent); -} - -static void DownloadObject(const std::string name, const std::string_view url) -{ - using namespace OpenRCT2::Network; - try - { - Http::Request req; - req.method = Http::Method::GET; - req.url = url; - Http::DoAsync(req, [name](Http::Response response) { - if (response.status == Http::Status::OK) - { - auto data = (uint8_t*)response.body.data(); - auto dataLen = response.body.size(); - - auto& objRepo = OpenRCT2::GetContext()->GetObjectRepository(); - objRepo.AddObjectFromFile(name, data, dataLen); - } - else - { - throw std::runtime_error("Non 200 status"); - } - next_download(); - }); - } - catch (const std::exception&) - { - std::printf(" Failed to download %s\n", name.c_str()); - next_download(); - } -} - -static void next_download() -{ - using namespace OpenRCT2::Network; - - if (!_downloadingObjects || _currentDownloadIndex >= _invalid_entries.size()) - { - // Finished... - return; - } - - auto& entry = _invalid_entries[_currentDownloadIndex]; - auto name = String::Trim(std::string(entry.name, sizeof(entry.name))); - show_progress(name, _currentDownloadIndex + 1, (int32_t)_invalid_entries.size()); - std::printf("Downloading %s...\n", name.c_str()); - _currentDownloadIndex++; - try - { - Http::Request req; - req.method = Http::Method::GET; - req.url = OPENRCT2_API_LEGACY_OBJECT_URL + name; - Http::DoAsync(req, [name](Http::Response response) { - if (response.status == Http::Status::OK) - { - auto jresponse = Json::FromString(response.body); - if (jresponse != nullptr) - { - auto objName = json_string_value(json_object_get(jresponse, "name")); - auto downloadLink = json_string_value(json_object_get(jresponse, "download")); - if (downloadLink != nullptr) - { - DownloadObject(objName, downloadLink); - } - json_decref(jresponse); - } - } - else - { - std::printf(" %s not found\n", name.c_str()); - next_download(); - } - }); - } - catch (const std::exception&) - { - std::printf(" Failed to query %s\n", name.c_str()); - } -} - static void window_object_load_error_download_all(rct_window* w) { - _currentDownloadIndex = 0; - _downloadingObjects = true; - next_download(); + if (!_objDownloader.IsDownloading()) + { + _updatedListAfterDownload = false; + _objDownloader.Begin(_invalid_entries); + } +} + +static void window_object_load_error_update_list(rct_window* w) +{ + auto entries = _objDownloader.GetDownloadedEntries(); + for (auto& de : entries) + { + _invalid_entries.erase( + std::remove_if( + _invalid_entries.begin(), _invalid_entries.end(), + [de](const rct_object_entry& e) { return std::memcmp(de.name, e.name, sizeof(e.name)) == 0; }), + _invalid_entries.end()); + w->no_list_items = (uint16_t)_invalid_entries.size(); + } } diff --git a/src/openrct2/network/Http.h b/src/openrct2/network/Http.h index fd972068fa..54204e0fb3 100644 --- a/src/openrct2/network/Http.h +++ b/src/openrct2/network/Http.h @@ -21,7 +21,8 @@ namespace OpenRCT2::Network::Http { enum class Status { - OK = 200 + OK = 200, + NotFound = 404 }; enum class Method From 0d138c96cf86bebd417d5249727c979e4254b73b Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 5 Feb 2019 19:38:40 +0000 Subject: [PATCH 4/7] Fix signed / unsigned comparison --- src/openrct2-ui/windows/ObjectLoadError.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index 25576bbe76..54ea6365a3 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -30,7 +30,7 @@ private: std::vector _entries; std::vector _downloadedEntries; - int32_t _currentDownloadIndex{}; + size_t _currentDownloadIndex{}; std::mutex _downloadedEntriesMutex; // TODO static due to INTENT_EXTRA_CALLBACK not allowing a std::function @@ -57,7 +57,7 @@ public: } private: - void UpdateProgress(const std::string& name, int32_t count, int32_t total) + void UpdateProgress(const std::string& name, size_t count, size_t total) { char str_downloading_objects[256]; set_format_arg(0, int16_t, count); From b74a21bf54d1bff70f1bad776c0de2b1648012fc Mon Sep 17 00:00:00 2001 From: Ted John Date: Wed, 6 Feb 2019 22:51:20 +0000 Subject: [PATCH 5/7] Fix no network builds --- src/openrct2-ui/windows/ObjectLoadError.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index 54ea6365a3..725a9d15f4 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -23,6 +23,8 @@ #include #include +#ifndef DISABLE_HTTP + class ObjectDownloader { private: @@ -166,6 +168,8 @@ private: } }; +#endif + // clang-format off enum WINDOW_OBJECT_LOAD_ERROR_WIDGET_IDX { WIDX_BACKGROUND, @@ -198,7 +202,9 @@ static rct_widget window_object_load_error_widgets[] = { { WWT_SCROLL, 0, 4, WW_LESS_PADDING, 70, WH - 33, SCROLL_VERTICAL, STR_NONE }, // Scrollable list area { WWT_BUTTON, 0, 4, 148, WH - 23, WH - 10, STR_COPY_SELECTED, STR_NONE }, // Copy selected btn { WWT_BUTTON, 0, 152, 296, WH - 23, WH - 10, STR_COPY_ALL, STR_NONE }, // Copy all btn +#ifndef DISABLE_HTTP { WWT_BUTTON, 0, 300, WW_LESS_PADDING, WH - 23, WH - 10, STR_DOWNLOAD_ALL, STR_NONE }, // Download all +#endif { WIDGETS_END }, }; @@ -211,8 +217,10 @@ static void window_object_load_error_scrollmouseover(rct_window *w, int32_t scro static void window_object_load_error_scrollmousedown(rct_window *w, int32_t scrollIndex, int32_t x, int32_t y); static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); +#ifndef DISABLE_HTTP static void window_object_load_error_download_all(rct_window* w); static void window_object_load_error_update_list(rct_window* w); +#endif static rct_window_event_list window_object_load_error_events = { window_object_load_error_close, @@ -249,8 +257,10 @@ static rct_window_event_list window_object_load_error_events = { static std::vector _invalid_entries; static int32_t highlighted_index = -1; static std::string file_path; +#ifndef DISABLE_HTTP static ObjectDownloader _objDownloader; static bool _updatedListAfterDownload; +#endif /** * Returns an rct_string_id that represents an rct_object_entry's type. @@ -381,6 +391,7 @@ static void window_object_load_error_update(rct_window* w) widget_invalidate(w, WIDX_SCROLL); } +#ifndef DISABLE_HTTP // Remove downloaded objects from our invalid entry list if (_objDownloader.IsDownloading()) { @@ -395,6 +406,7 @@ static void window_object_load_error_update(rct_window* w) window_object_load_error_update_list(w); _updatedListAfterDownload = true; } +#endif } static void window_object_load_error_mouseup(rct_window* w, rct_widgetindex widgetIndex) @@ -419,9 +431,11 @@ static void window_object_load_error_mouseup(rct_window* w, rct_widgetindex widg case WIDX_COPY_ALL: copy_object_names_to_clipboard(w); break; +#ifndef DISABLE_HTTP case WIDX_DOWNLOAD_ALL: window_object_load_error_download_all(w); break; +#endif } } @@ -513,6 +527,8 @@ static void window_object_load_error_scrollpaint(rct_window* w, rct_drawpixelinf } } +#ifndef DISABLE_HTTP + static void window_object_load_error_download_all(rct_window* w) { if (!_objDownloader.IsDownloading()) @@ -535,3 +551,5 @@ static void window_object_load_error_update_list(rct_window* w) w->no_list_items = (uint16_t)_invalid_entries.size(); } } + +#endif From 459ce12650fe951e670b4f68b3d69a4be9b3a4e3 Mon Sep 17 00:00:00 2001 From: Ted John Date: Wed, 6 Feb 2019 22:57:00 +0000 Subject: [PATCH 6/7] Reduce length of copy item string --- data/language/en-GB.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index aa934d9242..fd854407a4 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3576,7 +3576,7 @@ STR_6125 :Object type STR_6126 :Unknown type STR_6127 :File: {STRING} STR_6128 :The file could not be loaded as some of the objects referenced in it are missing or corrupt. A list of these objects is given below. -STR_6129 :Copy selected item to clipboard +STR_6129 :Copy item to clipboard STR_6130 :Copy entire list to clipboard STR_6131 :Object source STR_6132 :Ignore research status From 535b07531093c1462f0d4ef1a2a587011fee2535 Mon Sep 17 00:00:00 2001 From: Ted John Date: Thu, 7 Feb 2019 16:57:39 +0000 Subject: [PATCH 7/7] Reduce button text further and add tooltips --- data/language/en-GB.txt | 9 ++++++--- src/openrct2-ui/windows/ObjectLoadError.cpp | 6 +++--- src/openrct2/localisation/StringIds.h | 5 ++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index fd854407a4..4f4bc413a1 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3576,8 +3576,8 @@ STR_6125 :Object type STR_6126 :Unknown type STR_6127 :File: {STRING} STR_6128 :The file could not be loaded as some of the objects referenced in it are missing or corrupt. A list of these objects is given below. -STR_6129 :Copy item to clipboard -STR_6130 :Copy entire list to clipboard +STR_6129 :Copy +STR_6130 :Copy all STR_6131 :Object source STR_6132 :Ignore research status STR_6133 :{SMALLFONT}{BLACK}Access rides and scenery that have not yet been invented @@ -3747,7 +3747,10 @@ STR_6296 :GiB STR_6297 :TiB STR_6298 :{STRING}/sec STR_6299 :Download all -STR_6300 :Downloading object ({COMMA16} / {COMMA16}): [{STRING}] +STR_6300 :{SMALLFONT}{BLACK}Download all missing objects if available online. +STR_6301 :{SMALLFONT}{BLACK}Copy the selected object name to the clipboard. +STR_6302 :{SMALLFONT}{BLACK}Copy the entire list of missing objects to the clipboard. +STR_6303 :Downloading object ({COMMA16} / {COMMA16}): [{STRING}] ############# # Scenarios # diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index 725a9d15f4..407292bbf4 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -200,10 +200,10 @@ static rct_widget window_object_load_error_widgets[] = { { WWT_TABLE_HEADER, 0, SOURCE_COL_LEFT, TYPE_COL_LEFT - 1, 57, 70, STR_OBJECT_SOURCE, STR_NONE }, // 'Object source' header { WWT_TABLE_HEADER, 0, TYPE_COL_LEFT, WW_LESS_PADDING - 1, 57, 70, STR_OBJECT_TYPE, STR_NONE }, // 'Object type' header { WWT_SCROLL, 0, 4, WW_LESS_PADDING, 70, WH - 33, SCROLL_VERTICAL, STR_NONE }, // Scrollable list area - { WWT_BUTTON, 0, 4, 148, WH - 23, WH - 10, STR_COPY_SELECTED, STR_NONE }, // Copy selected btn - { WWT_BUTTON, 0, 152, 296, WH - 23, WH - 10, STR_COPY_ALL, STR_NONE }, // Copy all btn + { WWT_BUTTON, 0, 4, 148, WH - 23, WH - 10, STR_COPY_SELECTED, STR_COPY_SELECTED_TIP }, // Copy selected button + { WWT_BUTTON, 0, 152, 296, WH - 23, WH - 10, STR_COPY_ALL, STR_COPY_ALL_TIP }, // Copy all button #ifndef DISABLE_HTTP - { WWT_BUTTON, 0, 300, WW_LESS_PADDING, WH - 23, WH - 10, STR_DOWNLOAD_ALL, STR_NONE }, // Download all + { WWT_BUTTON, 0, 300, WW_LESS_PADDING, WH - 23, WH - 10, STR_DOWNLOAD_ALL, STR_DOWNLOAD_ALL_TIP }, // Download all button #endif { WIDGETS_END }, }; diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 7fa4d6fc6d..c07c48951b 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3928,7 +3928,10 @@ enum STR_NETWORK_SPEED_SEC = 6298, STR_DOWNLOAD_ALL = 6299, - STR_DOWNLOADING_OBJECTS = 6300, + STR_DOWNLOAD_ALL_TIP = 6300, + STR_COPY_SELECTED_TIP = 6301, + STR_COPY_ALL_TIP = 6302, + STR_DOWNLOADING_OBJECTS = 6303, // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768