mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-24 00:03:11 +01:00
Start moving code from OpenRCT2 to Context
This commit is contained in:
48
src/openrct2-ui/SDLException.h
Normal file
48
src/openrct2-ui/SDLException.h
Normal file
@@ -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 <SDL.h>
|
||||
#include <stdexcept>
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <openrct2/Version.h>
|
||||
#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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <openrct2/drawing/IDrawingEngine.h>
|
||||
#include <openrct2/drawing/Rain.h>
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
|
||||
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)
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
<ClInclude Include="drawing\engines\opengl\OpenGLShaderProgram.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\SwapFramebuffer.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\TextureCache.h" />
|
||||
<ClInclude Include="SDLException.h" />
|
||||
<ClInclude Include="TextComposition.h" />
|
||||
<ClInclude Include="UiContext.h" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -15,11 +15,44 @@
|
||||
#pragma endregion
|
||||
|
||||
#include <exception>
|
||||
#include "core/Memory.hpp"
|
||||
#include "config/Config.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#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<IParkImporter> 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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -14,49 +14,17 @@
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#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<IParkImporter> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ extern "C"
|
||||
{
|
||||
try
|
||||
{
|
||||
drawingEngine->Initialise(gWindow);
|
||||
drawingEngine->Initialise(uiContext->GetWindow());
|
||||
drawingEngine->SetUncappedFrameRate(gConfigGeneral.uncap_fps == 1);
|
||||
_drawingEngine = drawingEngine;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user