diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 487806c89f..84f2fc1520 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3702,6 +3702,13 @@ STR_6627 :Track speed too high! STR_6628 :Can only be placed on path edges! STR_6629 :Align toolbar buttons horizontally centred STR_6630 :This setting will align the toolbar buttons horizontally in the centre of the screen. The traditional way of aligning them is in the left and right corner. +STR_6631 :Loading... +STR_6632 :Checking object files... +STR_6633 :Checking scenario files... +STR_6634 :Checking track design files... +STR_6635 :Checking asset packs... +STR_6636 :Checking title sequences... +STR_6637 :Loading title sequence... ############# # Scenarios # diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 38800cab15..d7b8eaf4b0 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -2,6 +2,7 @@ ------------------------------------------------------------------------ - Feature: [#622] Add option to align the top toolbar buttons horizontally centred (off by default). - Feature: [#21714] [Plugin] Costume assignment is now tailored to each staff type. +- Feature: [#21893] On launch, the game now indicates what system is being initialised. - Feature: [#21913] [Plugin] Allow precise and safe control of peep animations. - Improved: [#21981] Rendering performance of the map window has been improved considerably. - Improved: [#21981] The map window now defaults to showing as much of the map as fits the screen. diff --git a/src/openrct2-ui/windows/Main.cpp b/src/openrct2-ui/windows/Main.cpp index d99748a713..f13a01bbc8 100644 --- a/src/openrct2-ui/windows/Main.cpp +++ b/src/openrct2-ui/windows/Main.cpp @@ -46,6 +46,10 @@ static Widget _mainWidgets[] = { void OnDraw(DrawPixelInfo& dpi) override { + // Skip viewport render during preloader + if (GetContext()->GetActiveScene() == GetContext()->GetPreloaderScene()) + return; + ViewportRender(dpi, viewport, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } }); } diff --git a/src/openrct2-ui/windows/NetworkStatus.cpp b/src/openrct2-ui/windows/NetworkStatus.cpp index 0273474906..6e25fe4af9 100644 --- a/src/openrct2-ui/windows/NetworkStatus.cpp +++ b/src/openrct2-ui/windows/NetworkStatus.cpp @@ -117,6 +117,7 @@ static Widget window_network_status_widgets[] = { void SetWindowNetworkStatusText(const std::string& text) { _windowNetworkStatusText = text; + Invalidate(); } void SetPassword(char* password) @@ -132,8 +133,17 @@ static Widget window_network_status_widgets[] = { WindowBase* NetworkStatusOpen(const std::string& text, close_callback onClose) { - auto window = WindowFocusOrCreate( - WindowClass::NetworkStatus, 420, 90, WF_10 | WF_TRANSPARENT | WF_CENTRE_SCREEN); + NetworkStatusWindow* window; + if ((window = static_cast(WindowFindByClass(WindowClass::NetworkStatus))) != nullptr) + { + WindowBringToFront(*window); + } + else + { + window = WindowCreate( + WindowClass::NetworkStatus, 420, 90, WF_10 | WF_TRANSPARENT | WF_CENTRE_SCREEN); + } + window->SetCloseCallBack(onClose); window->SetWindowNetworkStatusText(text); return window; diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 08ce5d777c..b22c438b6f 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -68,6 +68,7 @@ #include "scenario/ScenarioRepository.h" #include "scenes/game/GameScene.h" #include "scenes/intro/IntroScene.h" +#include "scenes/preloader/PreloaderScene.h" #include "scenes/title/TitleScene.h" #include "scenes/title/TitleSequenceManager.h" #include "scripting/HookEngine.h" @@ -124,6 +125,7 @@ namespace OpenRCT2 #endif // Scenes + std::unique_ptr _preloaderScene; std::unique_ptr _introScene; std::unique_ptr _titleScene; std::unique_ptr _gameScene; @@ -175,9 +177,6 @@ namespace OpenRCT2 #ifndef DISABLE_NETWORK , _network(*this) #endif - , _introScene(std::make_unique(*this)) - , _titleScene(std::make_unique(*this)) - , _gameScene(std::make_unique(*this)) , _painter(std::make_unique(uiContext)) { // Can't have more than one context currently. @@ -311,24 +310,41 @@ namespace OpenRCT2 return EXIT_FAILURE; } - IScene* GetLoadingScene() override + // NB: This takes some liberty in returning PreloaderScene* instead of IScene*. + // PreloaderScene adds some methods to Scene, which are used internally by Context. + PreloaderScene* GetPreloaderScene() override { - // TODO: Implement me. - return nullptr; + if (auto* scene = _preloaderScene.get()) + return scene; + + _preloaderScene = std::make_unique(*this); + return _preloaderScene.get(); } IScene* GetIntroScene() override { + if (auto* scene = _introScene.get()) + return scene; + + _introScene = std::make_unique(*this); return _introScene.get(); } IScene* GetTitleScene() override { + if (auto* scene = _titleScene.get()) + return scene; + + _titleScene = std::make_unique(*this); return _titleScene.get(); } IScene* GetGameScene() override { + if (auto* scene = _gameScene.get()) + return scene; + + _gameScene = std::make_unique(*this); return _gameScene.get(); } @@ -477,26 +493,6 @@ namespace OpenRCT2 EnsureUserContentDirectoriesExist(); - // TODO Ideally we want to delay this until we show the title so that we can - // still open the game window and draw a progress screen for the creation - // of the object cache. - _objectRepository->LoadOrConstruct(_localisationService->GetCurrentLanguage()); - - if (!gOpenRCT2Headless) - { - _assetPackManager->Scan(); - _assetPackManager->LoadEnabledAssetPacks(); - _assetPackManager->Reload(); - } - - // TODO Like objects, this can take a while if there are a lot of track designs - // its also really something really we might want to do in the background - // as its not required until the player wants to place a new ride. - _trackDesignRepository->Scan(_localisationService->GetCurrentLanguage()); - - _scenarioRepository->Scan(_localisationService->GetCurrentLanguage()); - TitleSequenceManager::Scan(); - if (!gOpenRCT2Headless) { Audio::Init(); @@ -520,7 +516,20 @@ namespace OpenRCT2 InputResetPlaceObjModifier(); ViewportInitAll(); - gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE); + ContextInit(); + + if (!gOpenRCT2Headless) + { + auto* preloaderScene = GetPreloaderScene(); + preloaderScene->AddJob([this]() { InitialiseRepositories(); }); + + // TODO: preload the title scene in another (parallel) job. + SetActiveScene(preloaderScene); + } + else + { + InitialiseRepositories(); + } #ifdef ENABLE_SCRIPTING _scriptEngine.Initialise(); @@ -531,6 +540,44 @@ namespace OpenRCT2 return true; } + private: + void InitialiseRepositories() + { + if (!_initialised) + { + throw std::runtime_error("Context needs to be initialised first."); + } + + auto currentLanguage = _localisationService->GetCurrentLanguage(); + auto* preloaderScene = GetPreloaderScene(); + + preloaderScene->UpdateCaption(STR_CHECKING_OBJECT_FILES); + _objectRepository->LoadOrConstruct(currentLanguage); + + if (!gOpenRCT2Headless) + { + preloaderScene->UpdateCaption(STR_CHECKING_ASSET_PACKS); + _assetPackManager->Scan(); + _assetPackManager->LoadEnabledAssetPacks(); + _assetPackManager->Reload(); + } + + preloaderScene->UpdateCaption(STR_CHECKING_TRACK_DESIGN_FILES); + _trackDesignRepository->Scan(currentLanguage); + + preloaderScene->UpdateCaption(STR_CHECKING_SCENARIO_FILES); + _scenarioRepository->Scan(currentLanguage); + + preloaderScene->UpdateCaption(STR_CHECKING_TITLE_SEQUENCES); + TitleSequenceManager::Scan(); + + if (preloaderScene->GetCompletionScene() == GetTitleScene()) + preloaderScene->UpdateCaption(STR_LOADING_TITLE_SEQUENCE); + else + preloaderScene->UpdateCaption(STR_LOADING_GENERIC); + } + + public: void InitialiseDrawingEngine() final override { assert(_drawingEngine == nullptr); @@ -596,6 +643,14 @@ namespace OpenRCT2 _drawingEngine = nullptr; } + void SetProgress(size_t currentProgress, size_t totalCount) override + { + if (GetActiveScene() != GetPreloaderScene()) + return; + + GetPreloaderScene()->SetProgress(currentProgress, totalCount); + } + bool LoadParkFromFile(const u8string& path, bool loadTitleScreenOnFail = false, bool asScenario = false) final override { LOG_VERBOSE("Context::LoadParkFromFile(%s)", path.c_str()); @@ -1012,7 +1067,14 @@ namespace OpenRCT2 } auto* scene = DetermineStartUpScene(); - SetActiveScene(scene); + if (!gOpenRCT2Headless) + { + _preloaderScene->SetCompletionScene(scene); + } + else + { + SetActiveScene(scene); + } if (scene == GetGameScene()) { diff --git a/src/openrct2/Context.h b/src/openrct2/Context.h index 8f3b463008..4fa8e38c66 100644 --- a/src/openrct2/Context.h +++ b/src/openrct2/Context.h @@ -144,7 +144,7 @@ namespace OpenRCT2 virtual NetworkBase& GetNetwork() abstract; #endif - virtual IScene* GetLoadingScene() abstract; + virtual IScene* GetPreloaderScene() abstract; virtual IScene* GetIntroScene() abstract; virtual IScene* GetTitleScene() abstract; virtual IScene* GetGameScene() abstract; @@ -158,6 +158,8 @@ namespace OpenRCT2 virtual bool Initialise() abstract; virtual void InitialiseDrawingEngine() abstract; virtual void DisposeDrawingEngine() abstract; + virtual void SetProgress(size_t currentProgress, size_t totalCount) abstract; + virtual bool LoadParkFromFile( const u8string& path, bool loadTitleScreenOnFail = false, bool asScenario = false) abstract; virtual bool LoadParkFromStream( diff --git a/src/openrct2/core/FileIndex.hpp b/src/openrct2/core/FileIndex.hpp index 18c9256a8d..442bb92f38 100644 --- a/src/openrct2/core/FileIndex.hpp +++ b/src/openrct2/core/FileIndex.hpp @@ -9,6 +9,7 @@ #pragma once +#include "../Context.h" #include "../common.h" #include "Console.hpp" #include "DataSerialiser.h" @@ -211,6 +212,7 @@ private: auto reportProgress = [&]() { const size_t completed = processed; Console::WriteFormat("File %5zu of %zu, done %3d%%\r", completed, totalCount, completed * 100 / totalCount); + OpenRCT2::GetContext()->SetProgress(completed, totalCount); }; for (size_t rangeStart = 0; rangeStart < totalCount; rangeStart += stepSize) diff --git a/src/openrct2/core/JobPool.cpp b/src/openrct2/core/JobPool.cpp index e4038663ab..ca757ca3fa 100644 --- a/src/openrct2/core/JobPool.cpp +++ b/src/openrct2/core/JobPool.cpp @@ -95,6 +95,12 @@ size_t JobPool::CountPending() return _pending.size(); } +size_t JobPool::CountProcessing() +{ + unique_lock lock(_mutex); + return _processing; +} + void JobPool::ProcessQueue() { unique_lock lock(_mutex); diff --git a/src/openrct2/core/JobPool.h b/src/openrct2/core/JobPool.h index aa558929fc..bfd51ad71c 100644 --- a/src/openrct2/core/JobPool.h +++ b/src/openrct2/core/JobPool.h @@ -46,6 +46,7 @@ public: void AddTask(std::function workFn, std::function completionFn = nullptr); void Join(std::function reportFn = nullptr); size_t CountPending(); + size_t CountProcessing(); private: void ProcessQueue(); diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 6f9af5f1bb..9bfa837ac4 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -522,8 +522,9 @@ - + + @@ -1015,6 +1016,7 @@ + diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 82f39e674e..ad8cca6f7a 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3855,6 +3855,14 @@ enum : uint16_t STR_OPTIONS_TOOLBAR_BUTTONS_CENTRED = 6629, STR_OPTIONS_TOOLBAR_BUTTONS_CENTRED_TIP = 6630, + STR_LOADING_GENERIC = 6631, + STR_CHECKING_OBJECT_FILES = 6632, + STR_CHECKING_SCENARIO_FILES = 6633, + STR_CHECKING_TRACK_DESIGN_FILES = 6634, + STR_CHECKING_ASSET_PACKS = 6635, + STR_CHECKING_TITLE_SEQUENCES = 6636, + STR_LOADING_TITLE_SEQUENCE = 6637, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working /* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings }; diff --git a/src/openrct2/scenes/Scene.cpp b/src/openrct2/scenes/Scene.cpp index 8f2681b29e..f300c0d02c 100644 --- a/src/openrct2/scenes/Scene.cpp +++ b/src/openrct2/scenes/Scene.cpp @@ -28,3 +28,22 @@ GameState_t& Scene::GetGameState() { return OpenRCT2::GetGameState(); } + +void Scene::FinishScene() +{ + if (_nextScene != nullptr) + { + _context.SetActiveScene(_nextScene); + _nextScene = nullptr; + } +} + +IScene* Scene::GetCompletionScene() +{ + return _nextScene; +} + +void Scene::SetCompletionScene(IScene* scene) +{ + _nextScene = scene; +} diff --git a/src/openrct2/scenes/Scene.h b/src/openrct2/scenes/Scene.h index 4e1e135d88..a082717d26 100644 --- a/src/openrct2/scenes/Scene.h +++ b/src/openrct2/scenes/Scene.h @@ -27,6 +27,9 @@ namespace OpenRCT2 virtual void Load() = 0; virtual void Tick() = 0; virtual void Stop() = 0; + + virtual IScene* GetCompletionScene() = 0; + virtual void SetCompletionScene(IScene* scene) = 0; }; class Scene : public IScene @@ -37,8 +40,15 @@ namespace OpenRCT2 GameState_t& GetGameState() override; IContext& GetContext() override; + IScene* GetCompletionScene() override; + void SetCompletionScene(IScene* scene) override; + + protected: + void FinishScene(); + protected: IContext& _context; + IScene* _nextScene = nullptr; }; } // namespace OpenRCT2 diff --git a/src/openrct2/scenes/preloader/PreloaderScene.cpp b/src/openrct2/scenes/preloader/PreloaderScene.cpp new file mode 100644 index 0000000000..b4505f0fbb --- /dev/null +++ b/src/openrct2/scenes/preloader/PreloaderScene.cpp @@ -0,0 +1,97 @@ +/***************************************************************************** + * Copyright (c) 2014-2024 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "PreloaderScene.h" + +#include "../../Context.h" +#include "../../Game.h" +#include "../../GameState.h" +#include "../../OpenRCT2.h" +#include "../../audio/audio.h" +#include "../../drawing/IDrawingContext.h" +#include "../../drawing/IDrawingEngine.h" +#include "../../interface/Viewport.h" +#include "../../interface/Window.h" +#include "../../localisation/LocalisationService.h" +#include "../../localisation/StringIds.h" +#include "../../windows/Intent.h" + +#include + +using namespace OpenRCT2; + +PreloaderScene::PreloaderScene(IContext& context) + : Scene(context) + , _jobs(1) + , _captionId(STR_LOADING_GENERIC) +{ +} + +void PreloaderScene::Load() +{ + LOG_VERBOSE("PreloaderScene::Load()"); + + gScreenFlags = SCREEN_FLAGS_PLAYING; + gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE); + ViewportInitAll(); + ContextOpenWindow(WindowClass::MainWindow); + WindowResizeGui(ContextGetWidth(), ContextGetHeight()); + + // Reset screen + auto* engine = GetContext().GetDrawingEngine(); + auto* drawingContext = engine->GetDrawingContext(); + drawingContext->Clear(*engine->GetDrawingPixelInfo(), PALETTE_INDEX_10); + + UpdateCaption(_captionId); + + LOG_VERBOSE("PreloaderScene::Load() finished"); +} + +void PreloaderScene::Tick() +{ + gInUpdateCode = true; + + ContextHandleInput(); + WindowInvalidateAll(); + + gInUpdateCode = false; + + if (_jobs.CountPending() == 0 && _jobs.CountProcessing() == 0) + { + // Make sure the job is fully completed. + _jobs.Join(); + + FinishScene(); + } +} + +void PreloaderScene::UpdateCaption(StringId stringId) +{ + _captionId = stringId; + + auto intent = Intent(WindowClass::NetworkStatus); + intent.PutExtra(INTENT_EXTRA_MESSAGE, GetContext().GetLocalisationService().GetString(stringId)); + ContextOpenIntent(&intent); +}; + +void PreloaderScene::SetProgress(size_t currentProgress, size_t totalCount) +{ + std::stringstream caption; + caption << GetContext().GetLocalisationService().GetString(_captionId); + caption << " (" << currentProgress << " / " << totalCount << ")"; + + auto intent = Intent(WindowClass::NetworkStatus); + intent.PutExtra(INTENT_EXTRA_MESSAGE, caption.str()); + ContextOpenIntent(&intent); +} + +void PreloaderScene::Stop() +{ + Audio::StopAll(); +} diff --git a/src/openrct2/scenes/preloader/PreloaderScene.h b/src/openrct2/scenes/preloader/PreloaderScene.h new file mode 100644 index 0000000000..eaf7eafcd7 --- /dev/null +++ b/src/openrct2/scenes/preloader/PreloaderScene.h @@ -0,0 +1,37 @@ +/***************************************************************************** + * Copyright (c) 2014-2024 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#include "../../core/JobPool.h" +#include "../../drawing/Drawing.h" +#include "../Scene.h" + +namespace OpenRCT2 +{ + class PreloaderScene final : public Scene + { + public: + PreloaderScene(IContext& context); + + void Load() override; + void Tick() override; + void Stop() override; + void UpdateCaption(StringId stringId); + void SetProgress(size_t currentProgress, size_t totalCount); + void AddJob(const std::function& fn) + { + _jobs.AddTask(fn); + } + + private: + JobPool _jobs; + StringId _captionId; + }; +} // namespace OpenRCT2