From 7163973bd2dca445ed49cc2e1b4b6849ce50bb29 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 26 Mar 2017 21:04:14 +0100 Subject: [PATCH] Start moving code from OpenRCT2 to Context --- src/openrct2-ui/SDLException.h | 48 ++ src/openrct2-ui/UiContext.cpp | 38 +- .../drawing/engines/DrawingEngines.h | 6 +- .../drawing/engines/SoftwareDrawingEngine.cpp | 42 +- .../engines/opengl/OpenGLDrawingEngine.cpp | 13 +- src/openrct2-ui/libopenrct2ui.vcxproj | 1 + src/openrct2/Context.cpp | 506 +++++++++++++++++- src/openrct2/Context.h | 1 + src/openrct2/OpenRCT2.cpp | 504 ----------------- src/openrct2/OpenRCT2.h | 15 +- src/openrct2/cmdline/ConvertCommand.cpp | 10 +- src/openrct2/cmdline/RootCommands.cpp | 6 +- src/openrct2/drawing/NewDrawing.cpp | 2 +- src/openrct2/interface/screenshot.c | 6 +- src/openrct2/rct2.c | 2 +- src/openrct2/ui/UiContext.h | 6 +- 16 files changed, 621 insertions(+), 585 deletions(-) create mode 100644 src/openrct2-ui/SDLException.h diff --git a/src/openrct2-ui/SDLException.h b/src/openrct2-ui/SDLException.h new file mode 100644 index 0000000000..9567a106d8 --- /dev/null +++ b/src/openrct2-ui/SDLException.h @@ -0,0 +1,48 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** +* OpenRCT2, an open source clone of Roller Coaster Tycoon 2. +* +* OpenRCT2 is the work of many authors, a full list can be found in contributors.md +* For more information, visit https://github.com/OpenRCT2/OpenRCT2 +* +* OpenRCT2 is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* A full copy of the GNU General Public License can be found in licence.txt +*****************************************************************************/ +#pragma endregion + +#pragma once + +#include +#include + +/** + * An exception which wraps an SDL error. + */ +class SDLException : public std::runtime_error +{ +public: + explicit SDLException(const std::string& message) + : runtime_error(message.c_str()) + { + SDL_GetError(); + } + + explicit SDLException(const char * message) + : runtime_error(message) + { + } + + /** + * Throws an SDL exception with a message containing the given call information + * and the message given by SDL_GetError. + */ + static void Throw(const char * call) + { + std::string message = std::string(call) + ": " + std::string(SDL_GetError()); + throw SDLException(message); + } +}; diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index 37e9b40043..58c03ef2de 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -26,6 +26,7 @@ #include #include "CursorRepository.h" #include "drawing/engines/DrawingEngines.h" +#include "SDLException.h" #include "TextComposition.h" #include "UiContext.h" @@ -70,7 +71,7 @@ private: CursorState _cursorState; uint32 _lastKeyPressed; const uint8 * _keysState; - uint8 * _keysPressed; + uint8 _keysPressed[256]; uint32 _lastGestureTimestamp; float _gestureRadius; @@ -78,15 +79,20 @@ public: UiContext(IPlatformUiContext * platformUiContext) : _platformUiContext(platformUiContext) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + SDLException::Throw("SDL_Init(SDL_INIT_VIDEO)"); + } } ~UiContext() override { CloseWindow(); + SDL_Quit(); } // Window - void * GetWindow() override + SDL_Window * GetWindow() override { return _window; } @@ -190,12 +196,12 @@ public: { switch ((sint32)type) { case DRAWING_ENGINE_SOFTWARE: - return CreateSoftwareDrawingEngine(); + return CreateSoftwareDrawingEngine(this); case DRAWING_ENGINE_SOFTWARE_WITH_HARDWARE_DISPLAY: - return CreateHardwareDisplayDrawingEngine(); + return CreateHardwareDisplayDrawingEngine(this); #ifndef DISABLE_OPENGL case DRAWING_ENGINE_OPENGL: - return CreateOpenGLDrawingEngine(); + return CreateOpenGLDrawingEngine(this); #endif default: return nullptr; @@ -434,26 +440,19 @@ public: OnResize(width, height); } -private: - void CreateWindow() + void CreateWindow() override { - if (SDL_Init(SDL_INIT_VIDEO) < 0) - { - log_fatal("SDL_Init %s", SDL_GetError()); - exit(-1); - } - SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, gConfigGeneral.minimize_fullscreen_focus_loss ? "1" : "0"); // TODO This should probably be called somewhere else. It has nothing to do with window creation and can be done as soon as // g1.dat is loaded. - sub_68371D(); + // sub_68371D(); // Get saved window size sint32 width = gConfigGeneral.window_width; sint32 height = gConfigGeneral.window_height; - if (width == -1) width = 640; - if (height == -1) height = 480; + if (width <= 0) width = 640; + if (height <= 0) height = 480; // Create window in window first rather than fullscreen so we have the display the window is on first uint32 flags = SDL_WINDOW_RESIZABLE; @@ -465,8 +464,7 @@ private: _window = SDL_CreateWindow(OPENRCT2_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags); if (_window == nullptr) { - log_fatal("SDL_CreateWindow failed %s", SDL_GetError()); - exit(-1); + SDLException::Throw("SDL_CreateWindow(...)"); } SDL_SetWindowGrab(_window, gConfigGeneral.trap_cursor ? SDL_TRUE : SDL_FALSE); @@ -487,13 +485,13 @@ private: TriggerResize(); } - void CloseWindow() + void CloseWindow() override { drawing_engine_dispose(); SDL_DestroyWindow(_window); - SDL_Quit(); } +private: void OnResize(sint32 width, sint32 height) { // Scale the native window size to the game's canvas size diff --git a/src/openrct2-ui/drawing/engines/DrawingEngines.h b/src/openrct2-ui/drawing/engines/DrawingEngines.h index 68aa8296bd..668ba596e7 100644 --- a/src/openrct2-ui/drawing/engines/DrawingEngines.h +++ b/src/openrct2-ui/drawing/engines/DrawingEngines.h @@ -27,10 +27,10 @@ namespace OpenRCT2 namespace Ui { - Drawing::IDrawingEngine * CreateSoftwareDrawingEngine(); - Drawing::IDrawingEngine * CreateHardwareDisplayDrawingEngine(); + Drawing::IDrawingEngine * CreateSoftwareDrawingEngine(IUiContext * uiContext); + Drawing::IDrawingEngine * CreateHardwareDisplayDrawingEngine(IUiContext * uiContext); #ifndef DISABLE_OPENGL - Drawing::IDrawingEngine * CreateOpenGLDrawingEngine(); + Drawing::IDrawingEngine * CreateOpenGLDrawingEngine(IUiContext * uiContext); #endif } } diff --git a/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp index 641994f6b4..710c945fec 100644 --- a/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp @@ -41,6 +41,7 @@ extern "C" using namespace OpenRCT2; using namespace OpenRCT2::Drawing; +using namespace OpenRCT2::Ui; class SoftwareDrawingEngine; @@ -191,8 +192,10 @@ public: class SoftwareDrawingEngine final : public IDrawingEngine { private: - bool _hardwareDisplay; + IUiContext * const _uiContext; + bool const _hardwareDisplay; + SDL_Window * _window = nullptr; SDL_Surface * _surface = nullptr; SDL_Surface * _RGBASurface = nullptr; SDL_Palette * _palette = nullptr; @@ -227,9 +230,10 @@ private: SoftwareDrawingContext * _drawingContext; public: - explicit SoftwareDrawingEngine(bool hardwareDisplay) + explicit SoftwareDrawingEngine(IUiContext * uiContext, bool hardwareDisplay) + : _uiContext(uiContext), + _hardwareDisplay(hardwareDisplay) { - _hardwareDisplay = hardwareDisplay; _drawingContext = new SoftwareDrawingContext(this); #ifdef __ENABLE_LIGHTFX__ _lastLightFXenabled = (gConfigGeneral.enable_light_fx != 0); @@ -251,6 +255,7 @@ public: void Initialise(SDL_Window * window) override { + _window = window; if (_hardwareDisplay) { // Try to create the accelerated renderer. @@ -340,17 +345,14 @@ public: } else { - SDL_Surface * windowSurface = SDL_GetWindowSurface(gWindow); - if (windowSurface == nullptr) + SDL_Surface * windowSurface = SDL_GetWindowSurface(_window); + if (windowSurface != nullptr && _palette != nullptr) { - log_fatal("SDL_GetWindowSurface failed %s", SDL_GetError()); - exit(1); - } - - if (_palette != nullptr && SDL_SetPaletteColors(_palette, palette, 0, 256)) - { - log_fatal("SDL_SetPaletteColors failed %s", SDL_GetError()); - exit(1); + // log_fatal("SDL_GetWindowSurface failed %s", SDL_GetError()); + // exit(1); + // log_fatal("SDL_SetPaletteColors failed %s", SDL_GetError()); + // exit(1); + SDL_SetPaletteColors(_palette, palette, 0, 256); } } } @@ -688,7 +690,7 @@ private: // Copy the surface to the window if (gConfigGeneral.window_scale == 1 || gConfigGeneral.window_scale <= 0) { - SDL_Surface * windowSurface = SDL_GetWindowSurface(gWindow); + SDL_Surface * windowSurface = SDL_GetWindowSurface(_window); if (SDL_BlitSurface(_surface, nullptr, windowSurface, nullptr)) { log_fatal("SDL_BlitSurface %s", SDL_GetError()); @@ -706,13 +708,13 @@ private: // then scale to window size. Without changing to RGBA first, SDL complains // about blit configurations being incompatible. - if (SDL_BlitScaled(_RGBASurface, nullptr, SDL_GetWindowSurface(gWindow), nullptr)) + if (SDL_BlitScaled(_RGBASurface, nullptr, SDL_GetWindowSurface(_window), nullptr)) { log_fatal("SDL_BlitScaled %s", SDL_GetError()); exit(1); } } - if (SDL_UpdateWindowSurface(gWindow)) + if (SDL_UpdateWindowSurface(_window)) { log_fatal("SDL_UpdateWindowSurface %s", SDL_GetError()); exit(1); @@ -834,14 +836,14 @@ private: } }; -IDrawingEngine * OpenRCT2::Ui::CreateSoftwareDrawingEngine() +IDrawingEngine * OpenRCT2::Ui::CreateSoftwareDrawingEngine(IUiContext * uiContext) { - return new SoftwareDrawingEngine(false); + return new SoftwareDrawingEngine(uiContext, false); } -IDrawingEngine * OpenRCT2::Ui::CreateHardwareDisplayDrawingEngine() +IDrawingEngine * OpenRCT2::Ui::CreateHardwareDisplayDrawingEngine(IUiContext * uiContext) { - return new SoftwareDrawingEngine(true); + return new SoftwareDrawingEngine(uiContext, true); } SoftwareDrawingContext::SoftwareDrawingContext(SoftwareDrawingEngine * engine) diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp index 7fecda0e9d..b515015beb 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -29,6 +29,7 @@ #include #include #include +#include extern "C" { @@ -225,8 +226,9 @@ public: class OpenGLDrawingEngine : public IDrawingEngine { private: - SDL_Window * _window = nullptr; - SDL_GLContext _context = nullptr; + IUiContext * const _uiContext = nullptr; + SDL_Window * _window = nullptr; + SDL_GLContext _context = nullptr; uint32 _width = 0; uint32 _height = 0; @@ -246,7 +248,8 @@ public: SDL_Color Palette[256]; vec4f GLPalette[256]; - OpenGLDrawingEngine() + OpenGLDrawingEngine(IUiContext * uiContext) + : _uiContext(uiContext) { _drawingContext = new OpenGLDrawingContext(this); } @@ -494,9 +497,9 @@ private: } }; -IDrawingEngine * OpenRCT2::Ui::CreateOpenGLDrawingEngine() +IDrawingEngine * OpenRCT2::Ui::CreateOpenGLDrawingEngine(IUiContext * uiContext) { - return new OpenGLDrawingEngine(); + return new OpenGLDrawingEngine(uiContext); } OpenGLDrawingContext::OpenGLDrawingContext(OpenGLDrawingEngine * engine) diff --git a/src/openrct2-ui/libopenrct2ui.vcxproj b/src/openrct2-ui/libopenrct2ui.vcxproj index 15bdf61d77..ca3247308d 100644 --- a/src/openrct2-ui/libopenrct2ui.vcxproj +++ b/src/openrct2-ui/libopenrct2ui.vcxproj @@ -54,6 +54,7 @@ + diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index c84d8d2c8f..d64c649b32 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -15,11 +15,44 @@ #pragma endregion #include -#include "core/Memory.hpp" -#include "config/Config.h" +#include +#include #include "Context.h" -#include "OpenRCT2.h" #include "ui/UiContext.h" +#include "core/Console.hpp" +#include "core/File.h" +#include "core/FileStream.hpp" +#include "core/Guard.hpp" +#include "core/String.hpp" +#include "FileClassifier.h" +#include "network/network.h" +#include "object/ObjectRepository.h" +#include "OpenRCT2.h" +#include "ParkImporter.h" +#include "platform/crash.h" +#include "PlatformEnvironment.h" +#include "ride/TrackDesignRepository.h" +#include "scenario/ScenarioRepository.h" +#include "title/TitleScreen.h" +#include "title/TitleSequenceManager.h" +#include "Version.h" + +extern "C" +{ + #include "audio/audio.h" + #include "config/Config.h" + #include "editor.h" + #include "game.h" + #include "interface/chat.h" + #include "interface/themes.h" + #include "intro.h" + #include "localisation/localisation.h" + #include "network/http.h" + #include "object_list.h" + #include "rct1.h" + #include "rct2.h" + #include "rct2/interop.h" +} using namespace OpenRCT2; using namespace OpenRCT2::Ui; @@ -29,7 +62,25 @@ namespace OpenRCT2 class Context : public IContext { private: - IUiContext * const _uiContext; + // The game update inverval in milliseconds, (1000 / 40fps) = 25ms + constexpr static uint32 UPDATE_TIME_MS = 25; + + // Dependencies + IUiContext * const _uiContext = nullptr; + + // Services + IPlatformEnvironment * _env = nullptr; + IObjectRepository * _objectRepository = nullptr; + ITrackDesignRepository * _trackDesignRepository = nullptr; + IScenarioRepository * _scenarioRepository = nullptr; + + bool _isWindowMinimised = false; + uint32 _isWindowMinimisedLastCheckTick = 0; + uint32 _lastTick = 0; + uint32 _uncapTick = 0; + + /** If set, will end the OpenRCT2 game loop. Intentially private to this module so that the flag can not be set back to false. */ + bool _finished = false; public: // Singleton of Context. @@ -46,6 +97,16 @@ namespace OpenRCT2 ~Context() override { + network_close(); + http_dispose(); + language_close_all(); + rct2_dispose(); + config_release(); +#ifndef DISABLE_NETWORK + EVP_MD_CTX_destroy(gHashCTX); +#endif // DISABLE_NETWORK + rct2_interop_dispose(); + platform_free(); Instance = nullptr; } @@ -60,10 +121,435 @@ namespace OpenRCT2 int runGame = cmdline_run((const char * *)argv, argc); if (runGame == 1) { - openrct2_launch(); + Initialise(); + Launch(); } return gExitCode; } + + /** + * Causes the OpenRCT2 game loop to finish. + */ + void Finish() + { + _finished = true; + } + + private: + bool Initialise() + { +#ifndef DISABLE_NETWORK + gHashCTX = EVP_MD_CTX_create(); + Guard::Assert(gHashCTX != nullptr, "EVP_MD_CTX_create failed"); +#endif // DISABLE_NETWORK + + crash_init(); + + // Sets up the environment OpenRCT2 is running in, e.g. directory paths + _env = SetupEnvironment(); + if (_env == nullptr) + { + return false; + } + + if (!rct2_interop_setup_segment()) + { + log_fatal("Unable to load RCT2 data sector"); + return false; + } + + if (gConfigGeneral.last_run_version != nullptr && String::Equals(gConfigGeneral.last_run_version, OPENRCT2_VERSION)) + { + gOpenRCT2ShowChangelog = false; + } + else + { + gOpenRCT2ShowChangelog = true; + gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION); + config_save_default(); + } + + if (!gOpenRCT2Headless) + { + GetContext()->GetUiContext()->CreateWindow(); + } + + // TODO add configuration option to allow multiple instances + // if (!gOpenRCT2Headless && !platform_lock_single_instance()) { + // log_fatal("OpenRCT2 is already running."); + // return false; + // } + + _objectRepository = CreateObjectRepository(_env); + _trackDesignRepository = CreateTrackDesignRepository(_env); + _scenarioRepository = CreateScenarioRepository(_env); + + if (!language_open(gConfigGeneral.language)) + { + log_error("Failed to open configured language..."); + if (!language_open(LANGUAGE_ENGLISH_UK)) + { + log_fatal("Failed to open fallback language..."); + return false; + } + } + + // 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(); + + // 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(); + + _scenarioRepository->Scan(); + TitleSequenceManager::Scan(); + + if (!gOpenRCT2Headless) + { + audio_init(); + audio_populate_devices(); + } + + http_init(); + theme_manager_initialise(); + + rct2_interop_setup_hooks(); + + if (!rct2_init()) + { + return false; + } + + chat_init(); + + rct2_copy_original_user_files_over(); + return true; + } + + IPlatformEnvironment * SetupEnvironment() + { + utf8 userPath[MAX_PATH]; + platform_resolve_openrct_data_path(); + platform_resolve_user_data_path(); + platform_get_user_directory(userPath, NULL, sizeof(userPath)); + if (!platform_ensure_directory_exists(userPath)) + { + Console::Error::WriteLine("Could not create user directory (do you have write access to your documents folder?)"); + return nullptr; + } + platform_get_exe_path(gExePath, sizeof(gExePath)); + log_verbose("Setting exe path to %s", gExePath); + + config_set_defaults(); + if (!config_open_default()) + { + if (!config_find_or_browse_install_directory()) + { + gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION); + config_save_default(); + utf8 path[MAX_PATH]; + config_get_default_path(path, sizeof(path)); + Console::Error::WriteLine("An RCT2 install directory must be specified! Please edit \"game_path\" in %s.", path); + return nullptr; + } + config_save_default(); + } + + if (!rct2_init_directories()) + { + return nullptr; + } + if (!rct2_startup_checks()) + { + return nullptr; + } + + utf8 path[260]; + std::string basePaths[4]; + basePaths[(size_t)DIRBASE::RCT1] = String::ToStd(gConfigGeneral.rct1_path); + basePaths[(size_t)DIRBASE::RCT2] = String::ToStd(gConfigGeneral.rct2_path); + platform_get_openrct_data_path(path, sizeof(path)); + basePaths[(size_t)DIRBASE::OPENRCT2] = std::string(path); + platform_get_user_directory(path, nullptr, sizeof(path)); + basePaths[(size_t)DIRBASE::USER] = std::string(path); + + IPlatformEnvironment * env = CreatePlatformEnvironment(basePaths); + return env; + } + + /** + * Launches the game, after command line arguments have been parsed and processed. + */ + void Launch() + { + gIntroState = INTRO_STATE_NONE; + if ((gOpenRCT2StartupAction == STARTUP_ACTION_TITLE) && gConfigGeneral.play_intro) + { + gOpenRCT2StartupAction = STARTUP_ACTION_INTRO; + } + + switch (gOpenRCT2StartupAction) { + case STARTUP_ACTION_INTRO: + gIntroState = INTRO_STATE_PUBLISHER_BEGIN; + title_load(); + break; + case STARTUP_ACTION_TITLE: + title_load(); + break; + case STARTUP_ACTION_OPEN: + { + bool parkLoaded = false; + // A path that includes "://" is illegal with all common filesystems, so it is almost certainly a URL + // This way all cURL supported protocols, like http, ftp, scp and smb are automatically handled + if (strstr(gOpenRCT2StartupActionPath, "://") != nullptr) + { +#ifndef DISABLE_HTTP + // Download park and open it using its temporary filename + char tmpPath[MAX_PATH]; + if (!http_download_park(gOpenRCT2StartupActionPath, tmpPath)) + { + title_load(); + break; + } + + parkLoaded = OpenParkAutoDetectFormat(tmpPath); +#endif + } + else + { + parkLoaded = rct2_open_file(gOpenRCT2StartupActionPath); + } + + if (!parkLoaded) + { + Console::Error::WriteLine("Failed to load '%s'", gOpenRCT2StartupActionPath); + title_load(); + break; + } + + gScreenFlags = SCREEN_FLAGS_PLAYING; + +#ifndef DISABLE_NETWORK + if (gNetworkStart == NETWORK_MODE_SERVER) + { + if (gNetworkStartPort == 0) + { + gNetworkStartPort = gConfigNetwork.default_port; + } + + if (String::IsNullOrEmpty(gCustomPassword)) + { + network_set_password(gConfigNetwork.default_password); + } + else + { + network_set_password(gCustomPassword); + } + network_begin_server(gNetworkStartPort); + } +#endif // DISABLE_NETWORK + break; + } + case STARTUP_ACTION_EDIT: + if (String::SizeOf(gOpenRCT2StartupActionPath) == 0) + { + editor_load(); + } + else if (!editor_load_landscape(gOpenRCT2StartupActionPath)) + { + title_load(); + } + break; + } + +#ifndef DISABLE_NETWORK + if (gNetworkStart == NETWORK_MODE_CLIENT) + { + if (gNetworkStartPort == 0) + { + gNetworkStartPort = gConfigNetwork.default_port; + } + network_begin_client(gNetworkStartHost, gNetworkStartPort); + } +#endif // DISABLE_NETWORK + + RunGameLoop(); + } + + /** + * Run the main game loop until the finished flag is set. + */ + void RunGameLoop() + { + log_verbose("begin openrct2 loop"); + _finished = false; + do + { + if (ShouldRunVariableFrame()) + { + RunVariableFrame(); + } + else + { + RunFixedFrame(); + } + } while (!_finished); + log_verbose("finish openrct2 loop"); + } + + bool IsMinimised() + { + // Don't check if window is minimised too frequently (every second is fine) + if (_lastTick > _isWindowMinimisedLastCheckTick + 1000) + { + uint32 windowFlags = SDL_GetWindowFlags(gWindow); + _isWindowMinimised = (windowFlags & SDL_WINDOW_MINIMIZED) || + (windowFlags & SDL_WINDOW_HIDDEN); + } + return _isWindowMinimised; + } + + bool ShouldRunVariableFrame() + { + if (!gConfigGeneral.uncap_fps) return false; + if (gGameSpeed > 4) return false; + if (gOpenRCT2Headless) return false; + if (IsMinimised()) return false; + return true; + } + + void RunFixedFrame() + { + _uncapTick = 0; + uint32 currentTick = SDL_GetTicks(); + uint32 ticksElapsed = currentTick - _lastTick; + if (ticksElapsed < UPDATE_TIME_MS) + { + SDL_Delay(UPDATE_TIME_MS - ticksElapsed); + _lastTick += UPDATE_TIME_MS; + } + else + { + _lastTick = currentTick; + } + GetContext()->GetUiContext()->ProcessMessages(); + rct2_update(); + if (!_isWindowMinimised) + { + platform_draw(); + } + } + + void RunVariableFrame() + { + uint32 currentTick = SDL_GetTicks(); + if (_uncapTick == 0) + { + _uncapTick = currentTick; + sprite_position_tween_reset(); + } + + // Limit number of updates per loop (any long pauses or debugging can make this update for a very long time) + if (currentTick - _uncapTick > UPDATE_TIME_MS * 60) + { + _uncapTick = currentTick - UPDATE_TIME_MS - 1; + } + + GetContext()->GetUiContext()->ProcessMessages(); + + while (_uncapTick <= currentTick && currentTick - _uncapTick > UPDATE_TIME_MS) + { + // Get the original position of each sprite + sprite_position_tween_store_a(); + + // Update the game so the sprite positions update + rct2_update(); + + // Get the next position of each sprite + sprite_position_tween_store_b(); + + _uncapTick += UPDATE_TIME_MS; + } + + // Tween the position of each sprite from the last position to the new position based on the time between the last + // tick and the next tick. + float nudge = 1 - ((float)(currentTick - _uncapTick) / UPDATE_TIME_MS); + sprite_position_tween_all(nudge); + + platform_draw(); + + sprite_position_tween_restore(); + } + + bool OpenParkAutoDetectFormat(const utf8 * path) + { + ClassifiedFile info; + if (TryClassifyFile(path, &info)) + { + if (info.Type == FILE_TYPE::SAVED_GAME || + info.Type == FILE_TYPE::SCENARIO) + { + std::unique_ptr parkImporter; + if (info.Version <= 2) + { + parkImporter.reset(ParkImporter::CreateS4()); + } + else + { + parkImporter.reset(ParkImporter::CreateS6()); + } + + if (info.Type == FILE_TYPE::SAVED_GAME) + { + try + { + parkImporter->LoadSavedGame(path); + parkImporter->Import(); + game_fix_save_vars(); + sprite_position_tween_reset(); + gScreenAge = 0; + gLastAutoSaveUpdate = AUTOSAVE_PAUSE; + game_load_init(); + return true; + } + catch (const Exception &) + { + Console::Error::WriteLine("Error loading saved game."); + } + } + else + { + try + { + parkImporter->LoadScenario(path); + parkImporter->Import(); + game_fix_save_vars(); + sprite_position_tween_reset(); + gScreenAge = 0; + gLastAutoSaveUpdate = AUTOSAVE_PAUSE; + scenario_begin(); + return true; + } + catch (const Exception &) + { + Console::Error::WriteLine("Error loading scenario."); + } + } + } + else + { + Console::Error::WriteLine("Invalid file type."); + } + } + else + { + Console::Error::WriteLine("Unable to detect file type."); + } + return false; + } }; Context * Context::Instance = nullptr; @@ -86,6 +572,16 @@ namespace OpenRCT2 extern "C" { + void openrct2_write_full_version_info(utf8 * buffer, size_t bufferSize) + { + String::Set(buffer, bufferSize, Version::GetInfo().c_str()); + } + + void openrct2_finish() + { + GetContext()->Finish(); + } + void context_setcurrentcursor(sint32 cursor) { GetContext()->GetUiContext()->SetCursor((CURSOR_ID)cursor); diff --git a/src/openrct2/Context.h b/src/openrct2/Context.h index 7a4ddb6ea6..02f3d59317 100644 --- a/src/openrct2/Context.h +++ b/src/openrct2/Context.h @@ -71,6 +71,7 @@ namespace OpenRCT2 virtual Ui::IUiContext * GetUiContext() abstract; virtual sint32 RunOpenRCT2(int argc, char * * argv) abstract; + virtual void Finish() abstract; }; IContext * CreateContext(); diff --git a/src/openrct2/OpenRCT2.cpp b/src/openrct2/OpenRCT2.cpp index 56657be733..de4bcc0110 100644 --- a/src/openrct2/OpenRCT2.cpp +++ b/src/openrct2/OpenRCT2.cpp @@ -14,49 +14,17 @@ *****************************************************************************/ #pragma endregion -#include -#include -#include "Context.h" -#include "ui/UiContext.h" #include "core/Console.hpp" #include "core/File.h" #include "core/FileStream.hpp" -#include "core/Guard.hpp" -#include "core/String.hpp" -#include "FileClassifier.h" -#include "network/network.h" -#include "object/ObjectRepository.h" #include "OpenRCT2.h" -#include "ParkImporter.h" -#include "platform/crash.h" -#include "PlatformEnvironment.h" -#include "ride/TrackDesignRepository.h" -#include "scenario/ScenarioRepository.h" -#include "title/TitleScreen.h" -#include "title/TitleSequenceManager.h" -#include "Version.h" extern "C" { #include "audio/audio.h" - #include "config/Config.h" - #include "editor.h" - #include "game.h" - #include "interface/chat.h" - #include "interface/themes.h" - #include "intro.h" - #include "localisation/localisation.h" - #include "network/http.h" - #include "object_list.h" #include "platform/platform.h" - #include "rct1.h" - #include "rct2.h" - #include "rct2/interop.h" } -// The game update interval in milliseconds, (1000 / 40fps) = 25ms -constexpr uint32 UPDATE_TIME_MS = 25; - extern "C" { sint32 gExitCode; @@ -78,252 +46,6 @@ extern "C" // OpenSSL's message digest context used for calculating sprite checksums EVP_MD_CTX * gHashCTX = nullptr; #endif // DISABLE_NETWORK -} - -namespace OpenRCT2 -{ - static IPlatformEnvironment * _env = nullptr; - static bool _isWindowMinimised; - static uint32 _isWindowMinimisedLastCheckTick; - static uint32 _lastTick; - static uint32 _uncapTick; - - /** If set, will end the OpenRCT2 game loop. Intentionally private to this module so that the flag can not be set back to false. */ - static bool _finished; - - static bool ShouldRunVariableFrame(); - static void RunGameLoop(); - static void RunFixedFrame(); - static void RunVariableFrame(); - - static bool OpenParkAutoDetectFormat(const utf8 * path); -} - -extern "C" -{ - void openrct2_write_full_version_info(utf8 * buffer, size_t bufferSize) - { - String::Set(buffer, bufferSize, gVersionInfoFull); - } - - static void openrct2_set_exe_path() - { - platform_get_exe_path(gExePath, sizeof(gExePath)); - log_verbose("Setting exe path to %s", gExePath); - } - - bool openrct2_initialise() - { -#ifndef DISABLE_NETWORK - gHashCTX = EVP_MD_CTX_create(); - Guard::Assert(gHashCTX != nullptr, "EVP_MD_CTX_create failed"); -#endif // DISABLE_NETWORK - - crash_init(); - - // Sets up the environment OpenRCT2 is running in, e.g. directory paths - OpenRCT2::_env = OpenRCT2::SetupEnvironment(); - if (OpenRCT2::_env == nullptr) - { - return false; - } - - if (!rct2_interop_setup_segment()) - { - log_fatal("Unable to load RCT2 data sector"); - return false; - } - - if (gConfigGeneral.last_run_version != nullptr && String::Equals(gConfigGeneral.last_run_version, OPENRCT2_VERSION)) - { - gOpenRCT2ShowChangelog = false; - } - else - { - gOpenRCT2ShowChangelog = true; - gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION); - config_save_default(); - } - - // TODO add configuration option to allow multiple instances - // if (!gOpenRCT2Headless && !platform_lock_single_instance()) { - // log_fatal("OpenRCT2 is already running."); - // return false; - // } - - IObjectRepository * objRepo = CreateObjectRepository(OpenRCT2::_env); - ITrackDesignRepository * tdRepo = CreateTrackDesignRepository(OpenRCT2::_env); - IScenarioRepository * scenarioRepo = CreateScenarioRepository(OpenRCT2::_env); - - if (!language_open(gConfigGeneral.language)) - { - log_error("Failed to open configured language..."); - if (!language_open(LANGUAGE_ENGLISH_UK)) - { - log_fatal("Failed to open fallback language..."); - return false; - } - } - - // 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. - objRepo->LoadOrConstruct(); - - // TODO Like objects, this can take a while if there are a lot of track designs - // it's also really something we might want to do in the background - // as it's not required until the player wants to place a new ride. - tdRepo->Scan(); - - scenarioRepo->Scan(); - TitleSequenceManager::Scan(); - - if (!gOpenRCT2Headless) - { - audio_init(); - audio_populate_devices(); - } - - http_init(); - theme_manager_initialise(); - - rct2_interop_setup_hooks(); - - if (!rct2_init()) - { - return false; - } - - chat_init(); - - rct2_copy_original_user_files_over(); - return true; - } - - /** - * Launches the game, after command line arguments have been parsed and processed. - */ - void openrct2_launch() - { - if (openrct2_initialise()) - { - gIntroState = INTRO_STATE_NONE; - if ((gOpenRCT2StartupAction == STARTUP_ACTION_TITLE) && gConfigGeneral.play_intro) - { - gOpenRCT2StartupAction = STARTUP_ACTION_INTRO; - } - - switch (gOpenRCT2StartupAction) { - case STARTUP_ACTION_INTRO: - gIntroState = INTRO_STATE_PUBLISHER_BEGIN; - title_load(); - break; - case STARTUP_ACTION_TITLE: - title_load(); - break; - case STARTUP_ACTION_OPEN: - { - bool parkLoaded = false; - // A path that includes "://" is illegal with all common filesystems, so it is almost certainly a URL - // This way all cURL supported protocols, like http, ftp, scp and smb are automatically handled - if (strstr(gOpenRCT2StartupActionPath, "://") != nullptr) - { -#ifndef DISABLE_HTTP - // Download park and open it using its temporary filename - char tmpPath[MAX_PATH]; - if (!http_download_park(gOpenRCT2StartupActionPath, tmpPath)) - { - title_load(); - break; - } - - parkLoaded = OpenRCT2::OpenParkAutoDetectFormat(tmpPath); -#endif - } - else - { - parkLoaded = rct2_open_file(gOpenRCT2StartupActionPath); - } - - if (!parkLoaded) - { - Console::Error::WriteLine("Failed to load '%s'", gOpenRCT2StartupActionPath); - title_load(); - break; - } - - gScreenFlags = SCREEN_FLAGS_PLAYING; - -#ifndef DISABLE_NETWORK - if (gNetworkStart == NETWORK_MODE_SERVER) - { - if (gNetworkStartPort == 0) - { - gNetworkStartPort = gConfigNetwork.default_port; - } - - if (String::IsNullOrEmpty(gCustomPassword)) - { - network_set_password(gConfigNetwork.default_password); - } - else - { - network_set_password(gCustomPassword); - } - network_begin_server(gNetworkStartPort); - } -#endif // DISABLE_NETWORK - break; - } - case STARTUP_ACTION_EDIT: - if (String::SizeOf(gOpenRCT2StartupActionPath) == 0) - { - editor_load(); - } - else if (!editor_load_landscape(gOpenRCT2StartupActionPath)) - { - title_load(); - } - break; - } - -#ifndef DISABLE_NETWORK - if (gNetworkStart == NETWORK_MODE_CLIENT) - { - if (gNetworkStartPort == 0) - { - gNetworkStartPort = gConfigNetwork.default_port; - } - network_begin_client(gNetworkStartHost, gNetworkStartPort); - } -#endif // DISABLE_NETWORK - - OpenRCT2::RunGameLoop(); - } - openrct2_dispose(); - } - - void openrct2_dispose() - { - network_close(); - http_dispose(); - language_close_all(); - rct2_dispose(); - config_release(); -#ifndef DISABLE_NETWORK - EVP_MD_CTX_destroy(gHashCTX); -#endif // DISABLE_NETWORK - rct2_interop_dispose(); - platform_free(); - } - - /** - * Causes the OpenRCT2 game loop to finish. - */ - void openrct2_finish() - { - OpenRCT2::_finished = true; - } bool check_file_path(sint32 pathId) { @@ -355,229 +77,3 @@ extern "C" return true; } } - -namespace OpenRCT2 -{ - IPlatformEnvironment * SetupEnvironment() - { - utf8 userPath[MAX_PATH]; - platform_resolve_openrct_data_path(); - platform_resolve_user_data_path(); - platform_get_user_directory(userPath, NULL, sizeof(userPath)); - if (!platform_ensure_directory_exists(userPath)) - { - Console::Error::WriteLine("Could not create user directory (do you have write access to your documents folder?)"); - return nullptr; - } - openrct2_set_exe_path(); - - config_set_defaults(); - if (!config_open_default()) - { - if (!config_find_or_browse_install_directory()) - { - gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION); - config_save_default(); - utf8 path[MAX_PATH]; - config_get_default_path(path, sizeof(path)); - Console::Error::WriteLine("An RCT2 install directory must be specified! Please edit \"game_path\" in %s.", path); - return nullptr; - } - config_save_default(); - } - - if (!rct2_init_directories()) - { - return nullptr; - } - if (!rct2_startup_checks()) - { - return nullptr; - } - - utf8 path[260]; - std::string basePaths[4]; - basePaths[(size_t)DIRBASE::RCT1] = String::ToStd(gConfigGeneral.rct1_path); - basePaths[(size_t)DIRBASE::RCT2] = String::ToStd(gConfigGeneral.rct2_path); - platform_get_openrct_data_path(path, sizeof(path)); - basePaths[(size_t)DIRBASE::OPENRCT2] = std::string(path); - platform_get_user_directory(path, nullptr, sizeof(path)); - basePaths[(size_t)DIRBASE::USER] = std::string(path); - - IPlatformEnvironment * env = CreatePlatformEnvironment(basePaths); - return env; - } - - /** - * Run the main game loop until the finished flag is set. - */ - static void RunGameLoop() - { - log_verbose("begin openrct2 loop"); - _finished = false; - do - { - if (ShouldRunVariableFrame()) - { - RunVariableFrame(); - } - else - { - RunFixedFrame(); - } - } - while (!_finished); - log_verbose("finish openrct2 loop"); - } - - static bool IsMinimised() - { - // Don't check if window is minimised too frequently (every second is fine) - if (_lastTick > _isWindowMinimisedLastCheckTick + 1000) - { - uint32 windowFlags = SDL_GetWindowFlags(gWindow); - _isWindowMinimised = (windowFlags & SDL_WINDOW_MINIMIZED) || - (windowFlags & SDL_WINDOW_HIDDEN); - } - return _isWindowMinimised; - } - - static bool ShouldRunVariableFrame() - { - if (!gConfigGeneral.uncap_fps) return false; - if (gGameSpeed > 4) return false; - if (gOpenRCT2Headless) return false; - if (IsMinimised()) return false; - return true; - } - - static void RunFixedFrame() - { - _uncapTick = 0; - uint32 currentTick = SDL_GetTicks(); - uint32 ticksElapsed = currentTick - _lastTick; - if (ticksElapsed < UPDATE_TIME_MS) - { - SDL_Delay(UPDATE_TIME_MS - ticksElapsed); - _lastTick += UPDATE_TIME_MS; - } - else - { - _lastTick = currentTick; - } - GetContext()->GetUiContext()->ProcessMessages(); - rct2_update(); - if (!_isWindowMinimised) - { - platform_draw(); - } - } - - static void RunVariableFrame() - { - uint32 currentTick = SDL_GetTicks(); - if (_uncapTick == 0) - { - _uncapTick = currentTick; - sprite_position_tween_reset(); - } - - // Limit number of updates per loop (any long pauses or debugging can make this update for a very long time) - if (currentTick - _uncapTick > UPDATE_TIME_MS * 60) - { - _uncapTick = currentTick - UPDATE_TIME_MS - 1; - } - - GetContext()->GetUiContext()->ProcessMessages(); - - while (_uncapTick <= currentTick && currentTick - _uncapTick > UPDATE_TIME_MS) - { - // Get the original position of each sprite - sprite_position_tween_store_a(); - - // Update the game so the sprite positions update - rct2_update(); - - // Get the next position of each sprite - sprite_position_tween_store_b(); - - _uncapTick += UPDATE_TIME_MS; - } - - // Tween the position of each sprite from the last position to the new position based on the time between the last - // tick and the next tick. - float nudge = 1 - ((float)(currentTick - _uncapTick) / UPDATE_TIME_MS); - sprite_position_tween_all(nudge); - - platform_draw(); - - sprite_position_tween_restore(); - } - - static bool OpenParkAutoDetectFormat(const utf8 * path) - { - ClassifiedFile info; - if (TryClassifyFile(path, &info)) - { - if (info.Type == FILE_TYPE::SAVED_GAME || - info.Type == FILE_TYPE::SCENARIO) - { - std::unique_ptr parkImporter; - if (info.Version <= 2) - { - parkImporter.reset(ParkImporter::CreateS4()); - } - else - { - parkImporter.reset(ParkImporter::CreateS6()); - } - - if (info.Type == FILE_TYPE::SAVED_GAME) - { - try - { - parkImporter->LoadSavedGame(path); - parkImporter->Import(); - game_fix_save_vars(); - sprite_position_tween_reset(); - gScreenAge = 0; - gLastAutoSaveUpdate = AUTOSAVE_PAUSE; - game_load_init(); - return true; - } - catch (const Exception &) - { - Console::Error::WriteLine("Error loading saved game."); - } - } - else - { - try - { - parkImporter->LoadScenario(path); - parkImporter->Import(); - game_fix_save_vars(); - sprite_position_tween_reset(); - gScreenAge = 0; - gLastAutoSaveUpdate = AUTOSAVE_PAUSE; - scenario_begin(); - return true; - } - catch (const Exception &) - { - Console::Error::WriteLine("Error loading scenario."); - } - } - } - else - { - Console::Error::WriteLine("Invalid file type."); - } - } - else - { - Console::Error::WriteLine("Unable to detect file type."); - } - return false; - } -} diff --git a/src/openrct2/OpenRCT2.h b/src/openrct2/OpenRCT2.h index ba45e17730..f1305c8c40 100644 --- a/src/openrct2/OpenRCT2.h +++ b/src/openrct2/OpenRCT2.h @@ -24,6 +24,7 @@ extern "C" { #endif #include "platform/platform.h" + #undef CreateWindow #ifdef __cplusplus } #endif @@ -40,17 +41,6 @@ enum STARTUP_ACTION STARTUP_ACTION_EDIT }; -#ifdef __cplusplus - -interface IPlatformEnvironment; - -namespace OpenRCT2 -{ - IPlatformEnvironment * SetupEnvironment(); -} - -#endif - #ifdef __cplusplus extern "C" { @@ -80,9 +70,6 @@ extern "C" #endif void openrct2_write_full_version_info(utf8 * buffer, size_t bufferSize); - bool openrct2_initialise(); - void openrct2_launch(); - void openrct2_dispose(); void openrct2_finish(); sint32 cmdline_run(const char * * argv, sint32 argc); diff --git a/src/openrct2/cmdline/ConvertCommand.cpp b/src/openrct2/cmdline/ConvertCommand.cpp index 6387a3e10b..c93c723407 100644 --- a/src/openrct2/cmdline/ConvertCommand.cpp +++ b/src/openrct2/cmdline/ConvertCommand.cpp @@ -103,11 +103,11 @@ exitcode_t CommandLine::HandleCommandConvert(CommandLineArgEnumerator * enumerat WriteConvertFromAndToMessage(sourceFileType, destinationFileType); gOpenRCT2Headless = true; - if (!openrct2_initialise()) - { - Console::Error::WriteLine("Error while initialising OpenRCT2."); - return EXITCODE_FAIL; - } + // if (!openrct2_initialise()) + // { + // Console::Error::WriteLine("Error while initialising OpenRCT2."); + // return EXITCODE_FAIL; + // } try { diff --git a/src/openrct2/cmdline/RootCommands.cpp b/src/openrct2/cmdline/RootCommands.cpp index 1d64d017b8..1eb9bc722d 100644 --- a/src/openrct2/cmdline/RootCommands.cpp +++ b/src/openrct2/cmdline/RootCommands.cpp @@ -389,9 +389,9 @@ static exitcode_t HandleCommandScanObjects(CommandLineArgEnumerator * enumerator return result; } - IPlatformEnvironment * env = OpenRCT2::SetupEnvironment(); - IObjectRepository * objectRepository = CreateObjectRepository(env); - objectRepository->Construct(); + // IPlatformEnvironment * env = OpenRCT2::SetupEnvironment(); + // IObjectRepository * objectRepository = CreateObjectRepository(env); + // objectRepository->Construct(); return EXITCODE_OK; } diff --git a/src/openrct2/drawing/NewDrawing.cpp b/src/openrct2/drawing/NewDrawing.cpp index 0ba3e5148a..1ad69e3a22 100644 --- a/src/openrct2/drawing/NewDrawing.cpp +++ b/src/openrct2/drawing/NewDrawing.cpp @@ -104,7 +104,7 @@ extern "C" { try { - drawingEngine->Initialise(gWindow); + drawingEngine->Initialise(uiContext->GetWindow()); drawingEngine->SetUncappedFrameRate(gConfigGeneral.uncap_fps == 1); _drawingEngine = drawingEngine; } diff --git a/src/openrct2/interface/screenshot.c b/src/openrct2/interface/screenshot.c index 30d517feb5..6661d11b8a 100644 --- a/src/openrct2/interface/screenshot.c +++ b/src/openrct2/interface/screenshot.c @@ -288,7 +288,7 @@ sint32 cmdline_for_screenshot(const char **argv, sint32 argc) } gOpenRCT2Headless = true; - if (openrct2_initialise()) { + // if (openrct2_initialise()) { drawing_engine_init(); rct2_open_file(inputPath); @@ -373,7 +373,7 @@ sint32 cmdline_for_screenshot(const char **argv, sint32 argc) free(dpi.bits); drawing_engine_dispose(); - } - openrct2_dispose(); + // } + // openrct2_dispose(); return 1; } diff --git a/src/openrct2/rct2.c b/src/openrct2/rct2.c index b24d470d6f..2fc34fdfd9 100644 --- a/src/openrct2/rct2.c +++ b/src/openrct2/rct2.c @@ -167,7 +167,7 @@ bool rct2_init() font_sprite_initialise_characters(); if (!gOpenRCT2Headless) { - platform_init(); + // platform_init(); audio_init_ride_sounds_and_info(); } viewport_init_all(); diff --git a/src/openrct2/ui/UiContext.h b/src/openrct2/ui/UiContext.h index e762dc2642..68678070e0 100644 --- a/src/openrct2/ui/UiContext.h +++ b/src/openrct2/ui/UiContext.h @@ -21,6 +21,8 @@ #include "../Context.h" #include "../interface/Cursors.h" +struct SDL_Window; + namespace OpenRCT2 { namespace Drawing @@ -68,7 +70,9 @@ namespace OpenRCT2 virtual ~IUiContext() = default; // Window - virtual void * GetWindow() abstract; + virtual void CreateWindow() abstract; + virtual void CloseWindow() abstract; + virtual SDL_Window * GetWindow() abstract; virtual sint32 GetWidth() abstract; virtual sint32 GetHeight() abstract; virtual void SetFullscreenMode(FULLSCREEN_MODE mode) abstract;