Factor out game stuff from PowderToySDL.cpp
This commit is contained in:
parent
2566506e4b
commit
11945ba620
526
src/PowderToy.cpp
Normal file
526
src/PowderToy.cpp
Normal file
@ -0,0 +1,526 @@
|
|||||||
|
#include "PowderToySDL.h"
|
||||||
|
#include "Format.h"
|
||||||
|
#include "X86KillDenormals.h"
|
||||||
|
#include "prefs/GlobalPrefs.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "client/GameSave.h"
|
||||||
|
#include "client/SaveFile.h"
|
||||||
|
#include "client/SaveInfo.h"
|
||||||
|
#include "client/http/RequestManager.h"
|
||||||
|
#include "common/Platform.h"
|
||||||
|
#include "graphics/Graphics.h"
|
||||||
|
#include "simulation/SaveRenderer.h"
|
||||||
|
#include "common/tpt-rand.h"
|
||||||
|
#include "gui/game/Favorite.h"
|
||||||
|
#include "gui/Style.h"
|
||||||
|
#include "gui/game/GameController.h"
|
||||||
|
#include "gui/game/GameView.h"
|
||||||
|
#include "gui/dialogues/ConfirmPrompt.h"
|
||||||
|
#include "gui/dialogues/ErrorMessage.h"
|
||||||
|
#include "gui/interface/Engine.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "SimulationConfig.h"
|
||||||
|
#include <optional>
|
||||||
|
#include <climits>
|
||||||
|
#include <iostream>
|
||||||
|
#include <csignal>
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
void LoadWindowPosition()
|
||||||
|
{
|
||||||
|
if (Client::Ref().IsFirstRun())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &prefs = GlobalPrefs::Ref();
|
||||||
|
int savedWindowX = prefs.Get("WindowX", INT_MAX);
|
||||||
|
int savedWindowY = prefs.Get("WindowY", INT_MAX);
|
||||||
|
|
||||||
|
int borderTop, borderLeft;
|
||||||
|
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
|
||||||
|
// Sometimes (Windows), the border size may not be reported for 200+ frames
|
||||||
|
// So just have a default of 5 to ensure the window doesn't get stuck where it can't be moved
|
||||||
|
if (borderTop == 0)
|
||||||
|
borderTop = 5;
|
||||||
|
|
||||||
|
int numDisplays = SDL_GetNumVideoDisplays();
|
||||||
|
SDL_Rect displayBounds;
|
||||||
|
bool ok = false;
|
||||||
|
for (int i = 0; i < numDisplays; i++)
|
||||||
|
{
|
||||||
|
SDL_GetDisplayBounds(i, &displayBounds);
|
||||||
|
if (savedWindowX + borderTop > displayBounds.x && savedWindowY + borderLeft > displayBounds.y &&
|
||||||
|
savedWindowX + borderTop < displayBounds.x + displayBounds.w &&
|
||||||
|
savedWindowY + borderLeft < displayBounds.y + displayBounds.h)
|
||||||
|
{
|
||||||
|
ok = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok)
|
||||||
|
SDL_SetWindowPosition(sdl_window, savedWindowX + borderLeft, savedWindowY + borderTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveWindowPosition()
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
SDL_GetWindowPosition(sdl_window, &x, &y);
|
||||||
|
|
||||||
|
int borderTop, borderLeft;
|
||||||
|
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
|
||||||
|
|
||||||
|
auto &prefs = GlobalPrefs::Ref();
|
||||||
|
prefs.Set("WindowX", x - borderLeft);
|
||||||
|
prefs.Set("WindowY", y - borderTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LargeScreenDialog()
|
||||||
|
{
|
||||||
|
StringBuilder message;
|
||||||
|
message << "Switching to " << scale << "x size mode since your screen was determined to be large enough: ";
|
||||||
|
message << desktopWidth << "x" << desktopHeight << " detected, " << WINDOWW*scale << "x" << WINDOWH*scale << " required";
|
||||||
|
message << "\nTo undo this, hit Cancel. You can change this in settings at any time.";
|
||||||
|
if (!ConfirmPrompt::Blocking("Large screen detected", message.Build()))
|
||||||
|
{
|
||||||
|
GlobalPrefs::Ref().Set("Scale", 1);
|
||||||
|
ui::Engine::Ref().SetScale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TickClient()
|
||||||
|
{
|
||||||
|
Client::Ref().Tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlueScreen(String detailMessage)
|
||||||
|
{
|
||||||
|
auto &engine = ui::Engine::Ref();
|
||||||
|
engine.g->fillrect(0, 0, engine.GetWidth(), engine.GetHeight(), 17, 114, 169, 210);
|
||||||
|
|
||||||
|
String errorTitle = "ERROR";
|
||||||
|
String errorDetails = "Details: " + detailMessage;
|
||||||
|
String errorHelp = String("An unrecoverable fault has occurred, please report the error by visiting the website below\n") + SCHEME + SERVER;
|
||||||
|
int currentY = 0, width, height;
|
||||||
|
int errorWidth = 0;
|
||||||
|
Graphics::textsize(errorHelp, errorWidth, height);
|
||||||
|
|
||||||
|
engine.g->drawtext((engine.GetWidth()/2)-(errorWidth/2), ((engine.GetHeight()/2)-100) + currentY, errorTitle.c_str(), 255, 255, 255, 255);
|
||||||
|
Graphics::textsize(errorTitle, width, height);
|
||||||
|
currentY += height + 4;
|
||||||
|
|
||||||
|
engine.g->drawtext((engine.GetWidth()/2)-(errorWidth/2), ((engine.GetHeight()/2)-100) + currentY, errorDetails.c_str(), 255, 255, 255, 255);
|
||||||
|
Graphics::textsize(errorTitle, width, height);
|
||||||
|
currentY += height + 4;
|
||||||
|
|
||||||
|
engine.g->drawtext((engine.GetWidth()/2)-(errorWidth/2), ((engine.GetHeight()/2)-100) + currentY, errorHelp.c_str(), 255, 255, 255, 255);
|
||||||
|
Graphics::textsize(errorTitle, width, height);
|
||||||
|
currentY += height + 4;
|
||||||
|
|
||||||
|
//Death loop
|
||||||
|
SDL_Event event;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
while (SDL_PollEvent(&event))
|
||||||
|
if(event.type == SDL_QUIT)
|
||||||
|
exit(-1);
|
||||||
|
blit(engine.g->vid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SigHandler(int signal)
|
||||||
|
{
|
||||||
|
switch(signal){
|
||||||
|
case SIGSEGV:
|
||||||
|
BlueScreen("Memory read/write error");
|
||||||
|
break;
|
||||||
|
case SIGFPE:
|
||||||
|
BlueScreen("Floating point exception");
|
||||||
|
break;
|
||||||
|
case SIGILL:
|
||||||
|
BlueScreen("Program execution exception");
|
||||||
|
break;
|
||||||
|
case SIGABRT:
|
||||||
|
BlueScreen("Unexpected program abort");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int SCALE_MAXIMUM = 10;
|
||||||
|
constexpr int SCALE_MARGIN = 30;
|
||||||
|
|
||||||
|
int GuessBestScale()
|
||||||
|
{
|
||||||
|
const int widthNoMargin = desktopWidth - SCALE_MARGIN;
|
||||||
|
const int widthGuess = widthNoMargin / WINDOWW;
|
||||||
|
|
||||||
|
const int heightNoMargin = desktopHeight - SCALE_MARGIN;
|
||||||
|
const int heightGuess = heightNoMargin / WINDOWH;
|
||||||
|
|
||||||
|
int guess = std::min(widthGuess, heightGuess);
|
||||||
|
if(guess < 1 || guess > SCALE_MAXIMUM)
|
||||||
|
guess = 1;
|
||||||
|
|
||||||
|
return guess;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ExplicitSingletons
|
||||||
|
{
|
||||||
|
// These need to be listed in the order they are populated in main.
|
||||||
|
std::unique_ptr<GlobalPrefs> globalPrefs;
|
||||||
|
http::RequestManagerPtr requestManager;
|
||||||
|
std::unique_ptr<Client> client;
|
||||||
|
std::unique_ptr<SaveRenderer> saveRenderer;
|
||||||
|
std::unique_ptr<RNG> rng;
|
||||||
|
std::unique_ptr<Favorite> favorite;
|
||||||
|
std::unique_ptr<ui::Engine> engine;
|
||||||
|
std::unique_ptr<GameController> gameController;
|
||||||
|
};
|
||||||
|
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
Platform::SetupCrt();
|
||||||
|
atexit([]() {
|
||||||
|
ui::Engine::Ref().CloseWindow();
|
||||||
|
explicitSingletons.reset();
|
||||||
|
if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL)
|
||||||
|
{
|
||||||
|
// * nvidia-460 egl registers callbacks with x11 that end up being called
|
||||||
|
// after egl is unloaded unless we grab it here and release it after
|
||||||
|
// sdl closes the display. this is an nvidia driver weirdness but
|
||||||
|
// technically an sdl bug. glfw has this fixed:
|
||||||
|
// https://github.com/glfw/glfw/commit/9e6c0c747be838d1f3dc38c2924a47a42416c081
|
||||||
|
SDL_GL_LoadLibrary(NULL);
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||||
|
SDL_GL_UnloadLibrary();
|
||||||
|
}
|
||||||
|
SDL_Quit();
|
||||||
|
});
|
||||||
|
explicitSingletons = std::make_unique<ExplicitSingletons>();
|
||||||
|
|
||||||
|
|
||||||
|
// https://bugzilla.libsdl.org/show_bug.cgi?id=3796
|
||||||
|
if (SDL_Init(0) < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform::originalCwd = Platform::GetCwd();
|
||||||
|
|
||||||
|
using Argument = std::optional<ByteString>;
|
||||||
|
std::map<ByteString, Argument> arguments;
|
||||||
|
|
||||||
|
for (auto i = 1; i < argc; ++i)
|
||||||
|
{
|
||||||
|
auto str = ByteString(argv[i]);
|
||||||
|
if (str.BeginsWith("file://"))
|
||||||
|
{
|
||||||
|
arguments.insert({ "open", format::URLDecode(str.substr(7 /* length of the "file://" prefix */)) });
|
||||||
|
}
|
||||||
|
else if (str.BeginsWith("ptsave:"))
|
||||||
|
{
|
||||||
|
arguments.insert({ "ptsave", str });
|
||||||
|
}
|
||||||
|
else if (auto split = str.SplitBy(':'))
|
||||||
|
{
|
||||||
|
arguments.insert({ split.Before(), split.After() });
|
||||||
|
}
|
||||||
|
else if (auto split = str.SplitBy('='))
|
||||||
|
{
|
||||||
|
arguments.insert({ split.Before(), split.After() });
|
||||||
|
}
|
||||||
|
else if (str == "open" || str == "ptsave" || str == "ddir")
|
||||||
|
{
|
||||||
|
if (i + 1 < argc)
|
||||||
|
{
|
||||||
|
arguments.insert({ str, argv[i + 1] });
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "no value provided for command line parameter " << str << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arguments.insert({ str, "" }); // so .has_value() is true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ddirArg = arguments["ddir"];
|
||||||
|
if (ddirArg.has_value())
|
||||||
|
{
|
||||||
|
if (Platform::ChangeDir(ddirArg.value()))
|
||||||
|
Platform::sharedCwd = Platform::GetCwd();
|
||||||
|
else
|
||||||
|
perror("failed to chdir to requested ddir");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *ddir = SDL_GetPrefPath(NULL, APPDATA);
|
||||||
|
if (!Platform::FileExists("powder.pref"))
|
||||||
|
{
|
||||||
|
if (ddir)
|
||||||
|
{
|
||||||
|
if (!Platform::ChangeDir(ddir))
|
||||||
|
{
|
||||||
|
perror("failed to chdir to default ddir");
|
||||||
|
SDL_free(ddir);
|
||||||
|
ddir = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ddir)
|
||||||
|
{
|
||||||
|
Platform::sharedCwd = ddir;
|
||||||
|
SDL_free(ddir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We're now in the correct directory, time to get prefs.
|
||||||
|
explicitSingletons->globalPrefs = std::make_unique<GlobalPrefs>();
|
||||||
|
|
||||||
|
auto &prefs = GlobalPrefs::Ref();
|
||||||
|
scale = prefs.Get("Scale", 1);
|
||||||
|
resizable = prefs.Get("Resizable", false);
|
||||||
|
fullscreen = prefs.Get("Fullscreen", false);
|
||||||
|
altFullscreen = prefs.Get("AltFullscreen", false);
|
||||||
|
forceIntegerScaling = prefs.Get("ForceIntegerScaling", true);
|
||||||
|
momentumScroll = prefs.Get("MomentumScroll", true);
|
||||||
|
showAvatars = prefs.Get("ShowAvatars", true);
|
||||||
|
|
||||||
|
auto true_string = [](ByteString str) {
|
||||||
|
str = str.ToLower();
|
||||||
|
return str == "true" ||
|
||||||
|
str == "t" ||
|
||||||
|
str == "on" ||
|
||||||
|
str == "yes" ||
|
||||||
|
str == "y" ||
|
||||||
|
str == "1" ||
|
||||||
|
str == ""; // standalone "redirect" or "disable-bluescreen" or similar arguments
|
||||||
|
};
|
||||||
|
auto true_arg = [&true_string](Argument arg) {
|
||||||
|
return arg.has_value() && true_string(arg.value());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto kioskArg = arguments["kiosk"];
|
||||||
|
if (kioskArg.has_value())
|
||||||
|
{
|
||||||
|
fullscreen = true_string(kioskArg.value());
|
||||||
|
prefs.Set("Fullscreen", fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true_arg(arguments["redirect"]))
|
||||||
|
{
|
||||||
|
FILE *new_stdout = freopen("stdout.log", "w", stdout);
|
||||||
|
FILE *new_stderr = freopen("stderr.log", "w", stderr);
|
||||||
|
if (!new_stdout || !new_stderr)
|
||||||
|
{
|
||||||
|
exit(42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scaleArg = arguments["scale"];
|
||||||
|
if (scaleArg.has_value())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
scale = scaleArg.value().ToNumber<int>();
|
||||||
|
prefs.Set("Scale", scale);
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error &e)
|
||||||
|
{
|
||||||
|
std::cerr << "failed to set scale: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto clientConfig = [&prefs](Argument arg, ByteString name, ByteString defaultValue) {
|
||||||
|
ByteString value;
|
||||||
|
if (arg.has_value())
|
||||||
|
{
|
||||||
|
value = arg.value();
|
||||||
|
if (value == "")
|
||||||
|
{
|
||||||
|
value = defaultValue;
|
||||||
|
}
|
||||||
|
prefs.Set(name, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = prefs.Get(name, defaultValue);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
ByteString proxyString = clientConfig(arguments["proxy"], "Proxy", "");
|
||||||
|
ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", "");
|
||||||
|
ByteString capathString = clientConfig(arguments["capath"], "CAPath", "");
|
||||||
|
bool disableNetwork = true_arg(arguments["disable-network"]);
|
||||||
|
explicitSingletons->requestManager = http::RequestManager::Create(proxyString, cafileString, capathString, disableNetwork);
|
||||||
|
|
||||||
|
explicitSingletons->client = std::make_unique<Client>();
|
||||||
|
Client::Ref().Initialize();
|
||||||
|
|
||||||
|
explicitSingletons->saveRenderer = std::make_unique<SaveRenderer>();
|
||||||
|
explicitSingletons->rng = std::make_unique<RNG>();
|
||||||
|
explicitSingletons->favorite = std::make_unique<Favorite>();
|
||||||
|
explicitSingletons->engine = std::make_unique<ui::Engine>();
|
||||||
|
|
||||||
|
// TODO: maybe bind the maximum allowed scale to screen size somehow
|
||||||
|
if(scale < 1 || scale > SCALE_MAXIMUM)
|
||||||
|
scale = 1;
|
||||||
|
|
||||||
|
SDLOpen();
|
||||||
|
|
||||||
|
if (Client::Ref().IsFirstRun())
|
||||||
|
{
|
||||||
|
scale = GuessBestScale();
|
||||||
|
if (scale > 1)
|
||||||
|
{
|
||||||
|
prefs.Set("Scale", scale);
|
||||||
|
SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
|
||||||
|
showLargeScreenDialog = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StopTextInput();
|
||||||
|
|
||||||
|
auto &engine = ui::Engine::Ref();
|
||||||
|
engine.g = new Graphics();
|
||||||
|
engine.Scale = scale;
|
||||||
|
engine.SetResizable(resizable);
|
||||||
|
engine.Fullscreen = fullscreen;
|
||||||
|
engine.SetAltFullscreen(altFullscreen);
|
||||||
|
engine.SetForceIntegerScaling(forceIntegerScaling);
|
||||||
|
engine.MomentumScroll = momentumScroll;
|
||||||
|
engine.ShowAvatars = showAvatars;
|
||||||
|
engine.SetMaxSize(desktopWidth, desktopHeight);
|
||||||
|
engine.Begin(WINDOWW, WINDOWH);
|
||||||
|
engine.SetFastQuit(prefs.Get("FastQuit", true));
|
||||||
|
|
||||||
|
bool enableBluescreen = !DEBUG && !true_arg(arguments["disable-bluescreen"]);
|
||||||
|
if (enableBluescreen)
|
||||||
|
{
|
||||||
|
//Get ready to catch any dodgy errors
|
||||||
|
signal(SIGSEGV, SigHandler);
|
||||||
|
signal(SIGFPE, SigHandler);
|
||||||
|
signal(SIGILL, SigHandler);
|
||||||
|
signal(SIGABRT, SigHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (X86)
|
||||||
|
{
|
||||||
|
X86KillDenormals();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wrapWithBluescreen = [&]() {
|
||||||
|
explicitSingletons->gameController = std::make_unique<GameController>();
|
||||||
|
auto *gameController = explicitSingletons->gameController.get();
|
||||||
|
engine.ShowWindow(gameController->GetView());
|
||||||
|
|
||||||
|
auto openArg = arguments["open"];
|
||||||
|
if (openArg.has_value())
|
||||||
|
{
|
||||||
|
if constexpr (DEBUG)
|
||||||
|
{
|
||||||
|
std::cout << "Loading " << openArg.value() << std::endl;
|
||||||
|
}
|
||||||
|
if (Platform::FileExists(openArg.value()))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<char> gameSaveData;
|
||||||
|
if (!Platform::ReadFile(gameSaveData, openArg.value()))
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", "Could not read file");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SaveFile * newFile = new SaveFile(openArg.value());
|
||||||
|
GameSave * newSave = new GameSave(std::move(gameSaveData));
|
||||||
|
newFile->SetGameSave(newSave);
|
||||||
|
gameController->LoadSaveFile(newFile);
|
||||||
|
delete newFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (std::exception & e)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", "Could not open file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ptsaveArg = arguments["ptsave"];
|
||||||
|
if (ptsaveArg.has_value())
|
||||||
|
{
|
||||||
|
engine.g->Clear();
|
||||||
|
engine.g->fillrect((engine.GetWidth()/2)-101, (engine.GetHeight()/2)-26, 202, 52, 0, 0, 0, 210);
|
||||||
|
engine.g->drawrect((engine.GetWidth()/2)-100, (engine.GetHeight()/2)-25, 200, 50, 255, 255, 255, 180);
|
||||||
|
engine.g->drawtext((engine.GetWidth()/2)-(Graphics::textwidth("Loading save...")/2), (engine.GetHeight()/2)-5, "Loading save...", style::Colour::InformationTitle.Red, style::Colour::InformationTitle.Green, style::Colour::InformationTitle.Blue, 255);
|
||||||
|
|
||||||
|
blit(engine.g->vid);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ByteString saveIdPart;
|
||||||
|
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
|
||||||
|
{
|
||||||
|
if (split.Before() != "ptsave")
|
||||||
|
throw std::runtime_error("Not a ptsave link");
|
||||||
|
saveIdPart = split.After().SplitBy('#').Before();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Invalid save link");
|
||||||
|
|
||||||
|
if (!saveIdPart.size())
|
||||||
|
throw std::runtime_error("No Save ID");
|
||||||
|
if constexpr (DEBUG)
|
||||||
|
{
|
||||||
|
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
|
||||||
|
}
|
||||||
|
int saveId = saveIdPart.ToNumber<int>();
|
||||||
|
|
||||||
|
SaveInfo * newSave = Client::Ref().GetSave(saveId, 0);
|
||||||
|
if (!newSave)
|
||||||
|
throw std::runtime_error("Could not load save info");
|
||||||
|
auto saveData = Client::Ref().GetSaveData(saveId, 0);
|
||||||
|
if (!saveData.size())
|
||||||
|
throw std::runtime_error(("Could not load save\n" + Client::Ref().GetLastError()).ToUtf8());
|
||||||
|
GameSave * newGameSave = new GameSave(std::move(saveData));
|
||||||
|
newSave->SetGameSave(newGameSave);
|
||||||
|
|
||||||
|
gameController->LoadSave(newSave);
|
||||||
|
delete newSave;
|
||||||
|
}
|
||||||
|
catch (std::exception & e)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EngineProcess();
|
||||||
|
SaveWindowPosition();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (enableBluescreen)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
wrapWithBluescreen();
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
BlueScreen(ByteString(e.what()).FromUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wrapWithBluescreen();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/String.h"
|
|
||||||
|
|
||||||
void EngineProcess();
|
|
||||||
void ClipboardPush(ByteString text);
|
|
||||||
ByteString ClipboardPull();
|
|
||||||
int GetModifiers();
|
|
||||||
unsigned int GetTicks();
|
|
||||||
void StartTextInput();
|
|
||||||
void StopTextInput();
|
|
||||||
void SetTextInputRect(int x, int y, int w, int h);
|
|
@ -1,408 +1,28 @@
|
|||||||
#include "Format.h"
|
#include "PowderToySDL.h"
|
||||||
#include "Misc.h"
|
|
||||||
#include "WindowIcon.h"
|
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "client/SaveInfo.h"
|
|
||||||
#include "client/GameSave.h"
|
|
||||||
#include "client/SaveFile.h"
|
|
||||||
#include "common/Platform.h"
|
#include "common/Platform.h"
|
||||||
#include "common/tpt-rand.h"
|
#include "common/tpt-rand.h"
|
||||||
#include "gui/game/GameController.h"
|
|
||||||
#include "gui/game/GameView.h"
|
|
||||||
#include "gui/font/FontEditor.h"
|
#include "gui/font/FontEditor.h"
|
||||||
#include "gui/dialogues/ErrorMessage.h"
|
|
||||||
#include "gui/dialogues/ConfirmPrompt.h"
|
|
||||||
#include "gui/Style.h"
|
|
||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include <ctime>
|
#include "SimulationConfig.h"
|
||||||
#include <climits>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <memory>
|
||||||
#include <SDL.h>
|
|
||||||
|
|
||||||
int desktopWidth = 1280, desktopHeight = 1024;
|
void LoadWindowPosition()
|
||||||
|
|
||||||
SDL_Window * sdl_window;
|
|
||||||
SDL_Renderer * sdl_renderer;
|
|
||||||
SDL_Texture * sdl_texture;
|
|
||||||
int scale = 1;
|
|
||||||
bool fullscreen = false;
|
|
||||||
bool altFullscreen = false;
|
|
||||||
bool forceIntegerScaling = true;
|
|
||||||
bool resizable = false;
|
|
||||||
|
|
||||||
|
|
||||||
void StartTextInput()
|
|
||||||
{
|
{
|
||||||
SDL_StartTextInput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopTextInput()
|
void SaveWindowPosition()
|
||||||
{
|
{
|
||||||
SDL_StopTextInput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTextInputRect(int x, int y, int w, int h)
|
void LargeScreenDialog()
|
||||||
{
|
{
|
||||||
SDL_Rect rect;
|
|
||||||
rect.x = x;
|
|
||||||
rect.y = y;
|
|
||||||
rect.w = w;
|
|
||||||
rect.h = h;
|
|
||||||
SDL_SetTextInputRect(&rect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClipboardPush(ByteString text)
|
void TickClient()
|
||||||
{
|
{
|
||||||
SDL_SetClipboardText(text.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteString ClipboardPull()
|
|
||||||
{
|
|
||||||
return ByteString(SDL_GetClipboardText());
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetModifiers()
|
|
||||||
{
|
|
||||||
return SDL_GetModState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CalculateMousePosition(int *x, int *y)
|
|
||||||
{
|
|
||||||
int globalMx, globalMy;
|
|
||||||
SDL_GetGlobalMouseState(&globalMx, &globalMy);
|
|
||||||
int windowX, windowY;
|
|
||||||
SDL_GetWindowPosition(sdl_window, &windowX, &windowY);
|
|
||||||
|
|
||||||
if (x)
|
|
||||||
*x = (globalMx - windowX) / scale;
|
|
||||||
if (y)
|
|
||||||
*y = (globalMy - windowY) / scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
void blit(pixel * vid)
|
|
||||||
{
|
|
||||||
SDL_UpdateTexture(sdl_texture, NULL, vid, WINDOWW * sizeof (Uint32));
|
|
||||||
// need to clear the renderer if there are black edges (fullscreen, or resizable window)
|
|
||||||
if (fullscreen || resizable)
|
|
||||||
SDL_RenderClear(sdl_renderer);
|
|
||||||
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
|
|
||||||
SDL_RenderPresent(sdl_renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RecreateWindow();
|
|
||||||
int SDLOpen()
|
|
||||||
{
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
RecreateWindow();
|
|
||||||
|
|
||||||
int displayIndex = SDL_GetWindowDisplayIndex(sdl_window);
|
|
||||||
if (displayIndex >= 0)
|
|
||||||
{
|
|
||||||
SDL_Rect rect;
|
|
||||||
if (!SDL_GetDisplayUsableBounds(displayIndex, &rect))
|
|
||||||
{
|
|
||||||
desktopWidth = rect.w;
|
|
||||||
desktopHeight = rect.h;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (SET_WINDOW_ICON)
|
|
||||||
{
|
|
||||||
WindowIcon(sdl_window);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_)
|
|
||||||
{
|
|
||||||
// bool changingScale = scale != scale_;
|
|
||||||
bool changingFullscreen = fullscreen_ != fullscreen || (altFullscreen_ != altFullscreen && fullscreen);
|
|
||||||
bool changingResizable = resizable != resizable_;
|
|
||||||
scale = scale_;
|
|
||||||
fullscreen = fullscreen_;
|
|
||||||
altFullscreen = altFullscreen_;
|
|
||||||
resizable = resizable_;
|
|
||||||
forceIntegerScaling = forceIntegerScaling_;
|
|
||||||
// Recreate the window when toggling fullscreen, due to occasional issues
|
|
||||||
// Also recreate it when enabling resizable windows, to fix bugs on windows,
|
|
||||||
// see https://github.com/jacob1/The-Powder-Toy/issues/24
|
|
||||||
if (changingFullscreen || (changingResizable && resizable && !fullscreen))
|
|
||||||
{
|
|
||||||
RecreateWindow();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (changingResizable)
|
|
||||||
SDL_RestoreWindow(sdl_window);
|
|
||||||
|
|
||||||
SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
|
|
||||||
SDL_RenderSetIntegerScale(sdl_renderer, forceIntegerScaling && fullscreen ? SDL_TRUE : SDL_FALSE);
|
|
||||||
unsigned int flags = 0;
|
|
||||||
if (fullscreen)
|
|
||||||
flags = altFullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
||||||
SDL_SetWindowFullscreen(sdl_window, flags);
|
|
||||||
if (fullscreen)
|
|
||||||
SDL_RaiseWindow(sdl_window);
|
|
||||||
SDL_SetWindowResizable(sdl_window, resizable ? SDL_TRUE : SDL_FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RecreateWindow()
|
|
||||||
{
|
|
||||||
unsigned int flags = 0;
|
|
||||||
if (fullscreen)
|
|
||||||
flags = altFullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
||||||
if (resizable && !fullscreen)
|
|
||||||
flags |= SDL_WINDOW_RESIZABLE;
|
|
||||||
|
|
||||||
if (sdl_texture)
|
|
||||||
SDL_DestroyTexture(sdl_texture);
|
|
||||||
if (sdl_renderer)
|
|
||||||
SDL_DestroyRenderer(sdl_renderer);
|
|
||||||
if (sdl_window)
|
|
||||||
{
|
|
||||||
SDL_DestroyWindow(sdl_window);
|
|
||||||
}
|
|
||||||
|
|
||||||
sdl_window = SDL_CreateWindow(APPNAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOWW * scale, WINDOWH * scale,
|
|
||||||
flags);
|
|
||||||
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
|
|
||||||
SDL_RenderSetLogicalSize(sdl_renderer, WINDOWW, WINDOWH);
|
|
||||||
if (forceIntegerScaling && fullscreen)
|
|
||||||
SDL_RenderSetIntegerScale(sdl_renderer, SDL_TRUE);
|
|
||||||
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WINDOWW, WINDOWH);
|
|
||||||
SDL_RaiseWindow(sdl_window);
|
|
||||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
|
||||||
//Uncomment this to enable resizing
|
|
||||||
//SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
|
||||||
//SDL_SetWindowResizable(sdl_window, SDL_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int GetTicks()
|
|
||||||
{
|
|
||||||
return SDL_GetTicks();
|
|
||||||
}
|
|
||||||
|
|
||||||
int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0;
|
|
||||||
unsigned int lastTick = 0;
|
|
||||||
unsigned int lastFpsUpdate = 0;
|
|
||||||
float fps = 0;
|
|
||||||
ui::Engine * engine = NULL;
|
|
||||||
bool showDoubleScreenDialog = false;
|
|
||||||
float currentWidth, currentHeight;
|
|
||||||
|
|
||||||
int mousex = 0, mousey = 0;
|
|
||||||
int mouseButton = 0;
|
|
||||||
bool mouseDown = false;
|
|
||||||
|
|
||||||
bool calculatedInitialMouse = false, delay = false;
|
|
||||||
bool hasMouseMoved = false;
|
|
||||||
|
|
||||||
void EventProcess(SDL_Event event)
|
|
||||||
{
|
|
||||||
switch (event.type)
|
|
||||||
{
|
|
||||||
case SDL_QUIT:
|
|
||||||
if (engine->GetFastQuit() || engine->CloseWindow())
|
|
||||||
engine->Exit();
|
|
||||||
break;
|
|
||||||
case SDL_KEYDOWN:
|
|
||||||
if (SDL_GetModState() & KMOD_GUI)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!event.key.repeat && event.key.keysym.sym == 'q' && (event.key.keysym.mod&KMOD_CTRL))
|
|
||||||
engine->ConfirmExit();
|
|
||||||
else
|
|
||||||
engine->onKeyPress(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT);
|
|
||||||
break;
|
|
||||||
case SDL_KEYUP:
|
|
||||||
if (SDL_GetModState() & KMOD_GUI)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
engine->onKeyRelease(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT);
|
|
||||||
break;
|
|
||||||
case SDL_TEXTINPUT:
|
|
||||||
if (SDL_GetModState() & KMOD_GUI)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
engine->onTextInput(ByteString(event.text.text).FromUtf8());
|
|
||||||
break;
|
|
||||||
case SDL_TEXTEDITING:
|
|
||||||
if (SDL_GetModState() & KMOD_GUI)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
engine->onTextEditing(ByteString(event.edit.text).FromUtf8(), event.edit.start);
|
|
||||||
break;
|
|
||||||
case SDL_MOUSEWHEEL:
|
|
||||||
{
|
|
||||||
// int x = event.wheel.x;
|
|
||||||
int y = event.wheel.y;
|
|
||||||
if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
|
|
||||||
{
|
|
||||||
// x *= -1;
|
|
||||||
y *= -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
engine->onMouseWheel(mousex, mousey, y); // TODO: pass x?
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SDL_MOUSEMOTION:
|
|
||||||
mousex = event.motion.x;
|
|
||||||
mousey = event.motion.y;
|
|
||||||
engine->onMouseMove(mousex, mousey);
|
|
||||||
|
|
||||||
hasMouseMoved = true;
|
|
||||||
break;
|
|
||||||
case SDL_DROPFILE:
|
|
||||||
engine->onFileDrop(event.drop.file);
|
|
||||||
SDL_free(event.drop.file);
|
|
||||||
break;
|
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
|
||||||
// if mouse hasn't moved yet, sdl will send 0,0. We don't want that
|
|
||||||
if (hasMouseMoved)
|
|
||||||
{
|
|
||||||
mousex = event.motion.x;
|
|
||||||
mousey = event.motion.y;
|
|
||||||
}
|
|
||||||
mouseButton = event.button.button;
|
|
||||||
engine->onMouseClick(event.motion.x, event.motion.y, mouseButton);
|
|
||||||
|
|
||||||
mouseDown = true;
|
|
||||||
if constexpr (!DEBUG)
|
|
||||||
{
|
|
||||||
SDL_CaptureMouse(SDL_TRUE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SDL_MOUSEBUTTONUP:
|
|
||||||
// if mouse hasn't moved yet, sdl will send 0,0. We don't want that
|
|
||||||
if (hasMouseMoved)
|
|
||||||
{
|
|
||||||
mousex = event.motion.x;
|
|
||||||
mousey = event.motion.y;
|
|
||||||
}
|
|
||||||
mouseButton = event.button.button;
|
|
||||||
engine->onMouseUnclick(mousex, mousey, mouseButton);
|
|
||||||
|
|
||||||
mouseDown = false;
|
|
||||||
if constexpr (!DEBUG)
|
|
||||||
{
|
|
||||||
SDL_CaptureMouse(SDL_FALSE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SDL_WINDOWEVENT:
|
|
||||||
{
|
|
||||||
switch (event.window.event)
|
|
||||||
{
|
|
||||||
case SDL_WINDOWEVENT_SHOWN:
|
|
||||||
if (!calculatedInitialMouse)
|
|
||||||
{
|
|
||||||
//initial mouse coords, sdl won't tell us this if mouse hasn't moved
|
|
||||||
CalculateMousePosition(&mousex, &mousey);
|
|
||||||
engine->onMouseMove(mousex, mousey);
|
|
||||||
calculatedInitialMouse = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// This event would be needed in certain glitchy cases of window resizing
|
|
||||||
// But for all currently tested cases, it isn't needed
|
|
||||||
/*case SDL_WINDOWEVENT_RESIZED:
|
|
||||||
{
|
|
||||||
float width = event.window.data1;
|
|
||||||
float height = event.window.data2;
|
|
||||||
|
|
||||||
currentWidth = width;
|
|
||||||
currentHeight = height;
|
|
||||||
// this "* scale" thing doesn't really work properly
|
|
||||||
// currently there is a bug where input doesn't scale properly after resizing, only when double scale mode is active
|
|
||||||
inputScaleH = (float)WINDOWW * scale / currentWidth;
|
|
||||||
inputScaleV = (float)WINDOWH * scale / currentHeight;
|
|
||||||
std::cout << "Changing input scale to " << inputScaleH << "x" << inputScaleV << std::endl;
|
|
||||||
break;
|
|
||||||
}*/
|
|
||||||
// This would send a mouse up event when focus is lost
|
|
||||||
// Not even sdl itself will know when the mouse was released if it happens in another window
|
|
||||||
// So it will ignore the next mouse down (after tpt is re-focused) and not send any events at all
|
|
||||||
// This is more unintuitive than pretending the mouse is still down when it's not, so this code is commented out
|
|
||||||
/*case SDL_WINDOWEVENT_FOCUS_LOST:
|
|
||||||
if (mouseDown)
|
|
||||||
{
|
|
||||||
mouseDown = false;
|
|
||||||
engine->onMouseUnclick(mousex, mousey, mouseButton);
|
|
||||||
}
|
|
||||||
break;*/
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EngineProcess()
|
|
||||||
{
|
|
||||||
double frameTimeAvg = 0.0f, correctedFrameTimeAvg = 0.0f;
|
|
||||||
SDL_Event event;
|
|
||||||
while(engine->Running())
|
|
||||||
{
|
|
||||||
int frameStart = SDL_GetTicks();
|
|
||||||
if(engine->Broken()) { engine->UnBreak(); break; }
|
|
||||||
event.type = 0;
|
|
||||||
while (SDL_PollEvent(&event))
|
|
||||||
{
|
|
||||||
EventProcess(event);
|
|
||||||
event.type = 0; //Clear last event
|
|
||||||
}
|
|
||||||
if(engine->Broken()) { engine->UnBreak(); break; }
|
|
||||||
|
|
||||||
engine->Tick();
|
|
||||||
engine->Draw();
|
|
||||||
|
|
||||||
if (scale != engine->Scale || fullscreen != engine->Fullscreen ||
|
|
||||||
altFullscreen != engine->GetAltFullscreen() ||
|
|
||||||
forceIntegerScaling != engine->GetForceIntegerScaling() || resizable != engine->GetResizable())
|
|
||||||
{
|
|
||||||
SDLSetScreen(engine->Scale, engine->GetResizable(), engine->Fullscreen, engine->GetAltFullscreen(),
|
|
||||||
engine->GetForceIntegerScaling());
|
|
||||||
}
|
|
||||||
|
|
||||||
blit(engine->g->vid);
|
|
||||||
|
|
||||||
int frameTime = SDL_GetTicks() - frameStart;
|
|
||||||
frameTimeAvg = frameTimeAvg * 0.8 + frameTime * 0.2;
|
|
||||||
float fpsLimit = ui::Engine::Ref().FpsLimit;
|
|
||||||
if(fpsLimit > 2)
|
|
||||||
{
|
|
||||||
double offset = 1000.0 / fpsLimit - frameTimeAvg;
|
|
||||||
if(offset > 0)
|
|
||||||
SDL_Delay(Uint32(offset + 0.5));
|
|
||||||
}
|
|
||||||
int correctedFrameTime = SDL_GetTicks() - frameStart;
|
|
||||||
correctedFrameTimeAvg = correctedFrameTimeAvg * 0.95 + correctedFrameTime * 0.05;
|
|
||||||
if (frameStart - lastFpsUpdate > 200)
|
|
||||||
{
|
|
||||||
engine->SetFps(1000.0 / correctedFrameTimeAvg);
|
|
||||||
lastFpsUpdate = frameStart;
|
|
||||||
}
|
|
||||||
if (frameStart - lastTick > 100)
|
|
||||||
{
|
|
||||||
lastTick = frameStart;
|
|
||||||
}
|
|
||||||
if (showDoubleScreenDialog)
|
|
||||||
{
|
|
||||||
showDoubleScreenDialog = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if constexpr (DEBUG)
|
|
||||||
{
|
|
||||||
std::cout << "Breaking out of EngineProcess" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExplicitSingletons
|
struct ExplicitSingletons
|
||||||
@ -433,8 +53,6 @@ int main(int argc, char * argv[])
|
|||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
});
|
});
|
||||||
explicitSingletons = std::make_unique<ExplicitSingletons>();
|
explicitSingletons = std::make_unique<ExplicitSingletons>();
|
||||||
currentWidth = WINDOWW;
|
|
||||||
currentHeight = WINDOWH;
|
|
||||||
|
|
||||||
scale = 1;
|
scale = 1;
|
||||||
if (argc >= 3)
|
if (argc >= 3)
|
||||||
@ -469,14 +87,14 @@ int main(int argc, char * argv[])
|
|||||||
ui::Engine::Ref().SetAltFullscreen(altFullscreen);
|
ui::Engine::Ref().SetAltFullscreen(altFullscreen);
|
||||||
ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling);
|
ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling);
|
||||||
|
|
||||||
engine = &ui::Engine::Ref();
|
auto &engine = ui::Engine::Ref();
|
||||||
engine->SetMaxSize(desktopWidth, desktopHeight);
|
engine.SetMaxSize(desktopWidth, desktopHeight);
|
||||||
engine->Begin(WINDOWW, WINDOWH);
|
engine.Begin(WINDOWW, WINDOWH);
|
||||||
engine->SetFastQuit(true);
|
engine.SetFastQuit(true);
|
||||||
|
|
||||||
if (argc >= 2)
|
if (argc >= 2)
|
||||||
{
|
{
|
||||||
engine->ShowWindow(new FontEditor(argv[1]));
|
engine.ShowWindow(new FontEditor(argv[1]));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -6,33 +6,12 @@
|
|||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
#include "client/GameSave.h"
|
#include "client/GameSave.h"
|
||||||
#include "simulation/Simulation.h"
|
#include "simulation/Simulation.h"
|
||||||
|
#include "common/Platform.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
void EngineProcess() {}
|
|
||||||
void ClipboardPush(ByteString) {}
|
|
||||||
ByteString ClipboardPull() { return ""; }
|
|
||||||
int GetModifiers() { return 0; }
|
|
||||||
void SetCursorEnabled(int enabled) {}
|
|
||||||
unsigned int GetTicks() { return 0; }
|
|
||||||
|
|
||||||
static bool ReadFile(std::vector<char> &fileData, ByteString filename)
|
|
||||||
{
|
|
||||||
std::ifstream f(filename, std::ios::binary);
|
|
||||||
if (f) f.seekg(0, std::ios::end);
|
|
||||||
if (f) fileData.resize(f.tellg());
|
|
||||||
if (f) f.seekg(0);
|
|
||||||
if (f) f.read(&fileData[0], fileData.size());
|
|
||||||
if (!f)
|
|
||||||
{
|
|
||||||
std::cerr << "ReadFile: " << filename << ": " << strerror(errno) << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (!argv[1] || !argv[2]) {
|
if (!argv[1] || !argv[2]) {
|
||||||
@ -43,7 +22,7 @@ int main(int argc, char *argv[])
|
|||||||
auto outputFilename = ByteString(argv[2]) + ".png";
|
auto outputFilename = ByteString(argv[2]) + ".png";
|
||||||
|
|
||||||
std::vector<char> fileData;
|
std::vector<char> fileData;
|
||||||
if (!ReadFile(fileData, inputFilename))
|
if (!Platform::ReadFile(fileData, inputFilename))
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,16 @@
|
|||||||
#include "common/tpt-minmax.h"
|
#include "PowderToySDL.h"
|
||||||
#include "SimulationConfig.h"
|
#include "SimulationConfig.h"
|
||||||
#include "Format.h"
|
|
||||||
#include "X86KillDenormals.h"
|
|
||||||
#include "WindowIcon.h"
|
#include "WindowIcon.h"
|
||||||
#include "Misc.h"
|
|
||||||
#include "prefs/GlobalPrefs.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "client/GameSave.h"
|
|
||||||
#include "client/SaveFile.h"
|
|
||||||
#include "client/SaveInfo.h"
|
|
||||||
#include "client/http/RequestManager.h"
|
|
||||||
#include "common/Platform.h"
|
|
||||||
#include "graphics/Graphics.h"
|
|
||||||
#include "simulation/SaveRenderer.h"
|
|
||||||
#include "common/tpt-rand.h"
|
|
||||||
#include "gui/game/Favorite.h"
|
|
||||||
#include "gui/Style.h"
|
|
||||||
#include "gui/game/GameController.h"
|
|
||||||
#include "gui/game/GameView.h"
|
|
||||||
#include "gui/dialogues/ConfirmPrompt.h"
|
|
||||||
#include "gui/dialogues/ErrorMessage.h"
|
|
||||||
#include "gui/interface/Engine.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include <map>
|
#include "gui/interface/Engine.h"
|
||||||
#include <optional>
|
#include "graphics/Graphics.h"
|
||||||
#include <ctime>
|
|
||||||
#include <climits>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <SDL.h>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <csignal>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <SDL.h>
|
|
||||||
|
|
||||||
int desktopWidth = 1280, desktopHeight = 1024;
|
int desktopWidth = 1280;
|
||||||
|
int desktopHeight = 1024;
|
||||||
SDL_Window * sdl_window;
|
SDL_Window *sdl_window = NULL;
|
||||||
SDL_Renderer * sdl_renderer;
|
SDL_Renderer *sdl_renderer = NULL;
|
||||||
SDL_Texture * sdl_texture;
|
SDL_Texture *sdl_texture = NULL;
|
||||||
int scale = 1;
|
int scale = 1;
|
||||||
bool fullscreen = false;
|
bool fullscreen = false;
|
||||||
bool altFullscreen = false;
|
bool altFullscreen = false;
|
||||||
@ -46,6 +18,15 @@ bool forceIntegerScaling = true;
|
|||||||
bool resizable = false;
|
bool resizable = false;
|
||||||
bool momentumScroll = true;
|
bool momentumScroll = true;
|
||||||
bool showAvatars = true;
|
bool showAvatars = true;
|
||||||
|
uint64_t lastTick = 0;
|
||||||
|
uint64_t lastFpsUpdate = 0;
|
||||||
|
bool showLargeScreenDialog = false;
|
||||||
|
int mousex = 0;
|
||||||
|
int mousey = 0;
|
||||||
|
int mouseButton = 0;
|
||||||
|
bool mouseDown = false;
|
||||||
|
bool calculatedInitialMouse = false;
|
||||||
|
bool hasMouseMoved = false;
|
||||||
|
|
||||||
void StartTextInput()
|
void StartTextInput()
|
||||||
{
|
{
|
||||||
@ -82,48 +63,9 @@ int GetModifiers()
|
|||||||
return SDL_GetModState();
|
return SDL_GetModState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadWindowPosition()
|
unsigned int GetTicks()
|
||||||
{
|
{
|
||||||
auto &prefs = GlobalPrefs::Ref();
|
return SDL_GetTicks();
|
||||||
int savedWindowX = prefs.Get("WindowX", INT_MAX);
|
|
||||||
int savedWindowY = prefs.Get("WindowY", INT_MAX);
|
|
||||||
|
|
||||||
int borderTop, borderLeft;
|
|
||||||
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
|
|
||||||
// Sometimes (Windows), the border size may not be reported for 200+ frames
|
|
||||||
// So just have a default of 5 to ensure the window doesn't get stuck where it can't be moved
|
|
||||||
if (borderTop == 0)
|
|
||||||
borderTop = 5;
|
|
||||||
|
|
||||||
int numDisplays = SDL_GetNumVideoDisplays();
|
|
||||||
SDL_Rect displayBounds;
|
|
||||||
bool ok = false;
|
|
||||||
for (int i = 0; i < numDisplays; i++)
|
|
||||||
{
|
|
||||||
SDL_GetDisplayBounds(i, &displayBounds);
|
|
||||||
if (savedWindowX + borderTop > displayBounds.x && savedWindowY + borderLeft > displayBounds.y &&
|
|
||||||
savedWindowX + borderTop < displayBounds.x + displayBounds.w &&
|
|
||||||
savedWindowY + borderLeft < displayBounds.y + displayBounds.h)
|
|
||||||
{
|
|
||||||
ok = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ok)
|
|
||||||
SDL_SetWindowPosition(sdl_window, savedWindowX + borderLeft, savedWindowY + borderTop);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveWindowPosition()
|
|
||||||
{
|
|
||||||
int x, y;
|
|
||||||
SDL_GetWindowPosition(sdl_window, &x, &y);
|
|
||||||
|
|
||||||
int borderTop, borderLeft;
|
|
||||||
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
|
|
||||||
|
|
||||||
auto &prefs = GlobalPrefs::Ref();
|
|
||||||
prefs.Set("WindowX", x - borderLeft);
|
|
||||||
prefs.Set("WindowY", y - borderTop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalculateMousePosition(int *x, int *y)
|
void CalculateMousePosition(int *x, int *y)
|
||||||
@ -139,7 +81,7 @@ void CalculateMousePosition(int *x, int *y)
|
|||||||
*y = (globalMy - windowY) / scale;
|
*y = (globalMy - windowY) / scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blit(pixel * vid)
|
void blit(pixel *vid)
|
||||||
{
|
{
|
||||||
SDL_UpdateTexture(sdl_texture, NULL, vid, WINDOWW * sizeof (Uint32));
|
SDL_UpdateTexture(sdl_texture, NULL, vid, WINDOWW * sizeof (Uint32));
|
||||||
// need to clear the renderer if there are black edges (fullscreen, or resizable window)
|
// need to clear the renderer if there are black edges (fullscreen, or resizable window)
|
||||||
@ -149,7 +91,6 @@ void blit(pixel * vid)
|
|||||||
SDL_RenderPresent(sdl_renderer);
|
SDL_RenderPresent(sdl_renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecreateWindow();
|
|
||||||
void SDLOpen()
|
void SDLOpen()
|
||||||
{
|
{
|
||||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
||||||
@ -260,32 +201,12 @@ bool RecreateWindow()
|
|||||||
//SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
//SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
||||||
//SDL_SetWindowResizable(sdl_window, SDL_TRUE);
|
//SDL_SetWindowResizable(sdl_window, SDL_TRUE);
|
||||||
|
|
||||||
if (!Client::Ref().IsFirstRun())
|
LoadWindowPosition();
|
||||||
LoadWindowPosition();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int GetTicks()
|
void EventProcess(const SDL_Event &event)
|
||||||
{
|
|
||||||
return SDL_GetTicks();
|
|
||||||
}
|
|
||||||
|
|
||||||
int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0;
|
|
||||||
uint64_t lastTick = 0;
|
|
||||||
uint64_t lastFpsUpdate = 0;
|
|
||||||
float fps = 0;
|
|
||||||
bool showLargeScreenDialog = false;
|
|
||||||
float currentWidth, currentHeight;
|
|
||||||
|
|
||||||
int mousex = 0, mousey = 0;
|
|
||||||
int mouseButton = 0;
|
|
||||||
bool mouseDown = false;
|
|
||||||
|
|
||||||
bool calculatedInitialMouse = false, delay = false;
|
|
||||||
bool hasMouseMoved = false;
|
|
||||||
|
|
||||||
void EventProcess(SDL_Event event)
|
|
||||||
{
|
{
|
||||||
auto &engine = ui::Engine::Ref();
|
auto &engine = ui::Engine::Ref();
|
||||||
switch (event.type)
|
switch (event.type)
|
||||||
@ -395,52 +316,12 @@ void EventProcess(SDL_Event event)
|
|||||||
calculatedInitialMouse = true;
|
calculatedInitialMouse = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// This event would be needed in certain glitchy cases of window resizing
|
|
||||||
// But for all currently tested cases, it isn't needed
|
|
||||||
/*case SDL_WINDOWEVENT_RESIZED:
|
|
||||||
{
|
|
||||||
float width = event.window.data1;
|
|
||||||
float height = event.window.data2;
|
|
||||||
|
|
||||||
currentWidth = width;
|
|
||||||
currentHeight = height;
|
|
||||||
// this "* scale" thing doesn't really work properly
|
|
||||||
// currently there is a bug where input doesn't scale properly after resizing, only when double scale mode is active
|
|
||||||
inputScaleH = (float)WINDOWW * scale / currentWidth;
|
|
||||||
inputScaleV = (float)WINDOWH * scale / currentHeight;
|
|
||||||
std::cout << "Changing input scale to " << inputScaleH << "x" << inputScaleV << std::endl;
|
|
||||||
break;
|
|
||||||
}*/
|
|
||||||
// This would send a mouse up event when focus is lost
|
|
||||||
// Not even sdl itself will know when the mouse was released if it happens in another window
|
|
||||||
// So it will ignore the next mouse down (after tpt is re-focused) and not send any events at all
|
|
||||||
// This is more unintuitive than pretending the mouse is still down when it's not, so this code is commented out
|
|
||||||
/*case SDL_WINDOWEVENT_FOCUS_LOST:
|
|
||||||
if (mouseDown)
|
|
||||||
{
|
|
||||||
mouseDown = false;
|
|
||||||
engine.onMouseUnclick(mousex, mousey, mouseButton);
|
|
||||||
}
|
|
||||||
break;*/
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LargeScreenDialog()
|
|
||||||
{
|
|
||||||
StringBuilder message;
|
|
||||||
message << "Switching to " << scale << "x size mode since your screen was determined to be large enough: ";
|
|
||||||
message << desktopWidth << "x" << desktopHeight << " detected, " << WINDOWW*scale << "x" << WINDOWH*scale << " required";
|
|
||||||
message << "\nTo undo this, hit Cancel. You can change this in settings at any time.";
|
|
||||||
if (!ConfirmPrompt::Blocking("Large screen detected", message.Build()))
|
|
||||||
{
|
|
||||||
GlobalPrefs::Ref().Set("Scale", 1);
|
|
||||||
ui::Engine::Ref().SetScale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EngineProcess()
|
void EngineProcess()
|
||||||
{
|
{
|
||||||
double correctedFrameTimeAvg = 0;
|
double correctedFrameTimeAvg = 0;
|
||||||
@ -502,7 +383,7 @@ void EngineProcess()
|
|||||||
if (frameStart - lastTick > UINT64_C(100'000'000))
|
if (frameStart - lastTick > UINT64_C(100'000'000))
|
||||||
{
|
{
|
||||||
lastTick = frameStart;
|
lastTick = frameStart;
|
||||||
Client::Ref().Tick();
|
TickClient();
|
||||||
}
|
}
|
||||||
if (showLargeScreenDialog)
|
if (showLargeScreenDialog)
|
||||||
{
|
{
|
||||||
@ -515,437 +396,3 @@ void EngineProcess()
|
|||||||
std::cout << "Breaking out of EngineProcess" << std::endl;
|
std::cout << "Breaking out of EngineProcess" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlueScreen(String detailMessage)
|
|
||||||
{
|
|
||||||
auto &engine = ui::Engine::Ref();
|
|
||||||
engine.g->fillrect(0, 0, engine.GetWidth(), engine.GetHeight(), 17, 114, 169, 210);
|
|
||||||
|
|
||||||
String errorTitle = "ERROR";
|
|
||||||
String errorDetails = "Details: " + detailMessage;
|
|
||||||
String errorHelp = String("An unrecoverable fault has occurred, please report the error by visiting the website below\n") + SCHEME + SERVER;
|
|
||||||
int currentY = 0, width, height;
|
|
||||||
int errorWidth = 0;
|
|
||||||
Graphics::textsize(errorHelp, errorWidth, height);
|
|
||||||
|
|
||||||
engine.g->drawtext((engine.GetWidth()/2)-(errorWidth/2), ((engine.GetHeight()/2)-100) + currentY, errorTitle.c_str(), 255, 255, 255, 255);
|
|
||||||
Graphics::textsize(errorTitle, width, height);
|
|
||||||
currentY += height + 4;
|
|
||||||
|
|
||||||
engine.g->drawtext((engine.GetWidth()/2)-(errorWidth/2), ((engine.GetHeight()/2)-100) + currentY, errorDetails.c_str(), 255, 255, 255, 255);
|
|
||||||
Graphics::textsize(errorTitle, width, height);
|
|
||||||
currentY += height + 4;
|
|
||||||
|
|
||||||
engine.g->drawtext((engine.GetWidth()/2)-(errorWidth/2), ((engine.GetHeight()/2)-100) + currentY, errorHelp.c_str(), 255, 255, 255, 255);
|
|
||||||
Graphics::textsize(errorTitle, width, height);
|
|
||||||
currentY += height + 4;
|
|
||||||
|
|
||||||
//Death loop
|
|
||||||
SDL_Event event;
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
while (SDL_PollEvent(&event))
|
|
||||||
if(event.type == SDL_QUIT)
|
|
||||||
exit(-1);
|
|
||||||
blit(engine.g->vid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SigHandler(int signal)
|
|
||||||
{
|
|
||||||
switch(signal){
|
|
||||||
case SIGSEGV:
|
|
||||||
BlueScreen("Memory read/write error");
|
|
||||||
break;
|
|
||||||
case SIGFPE:
|
|
||||||
BlueScreen("Floating point exception");
|
|
||||||
break;
|
|
||||||
case SIGILL:
|
|
||||||
BlueScreen("Program execution exception");
|
|
||||||
break;
|
|
||||||
case SIGABRT:
|
|
||||||
BlueScreen("Unexpected program abort");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int SCALE_MAXIMUM = 10;
|
|
||||||
constexpr int SCALE_MARGIN = 30;
|
|
||||||
|
|
||||||
int GuessBestScale()
|
|
||||||
{
|
|
||||||
const int widthNoMargin = desktopWidth - SCALE_MARGIN;
|
|
||||||
const int widthGuess = widthNoMargin / WINDOWW;
|
|
||||||
|
|
||||||
const int heightNoMargin = desktopHeight - SCALE_MARGIN;
|
|
||||||
const int heightGuess = heightNoMargin / WINDOWH;
|
|
||||||
|
|
||||||
int guess = std::min(widthGuess, heightGuess);
|
|
||||||
if(guess < 1 || guess > SCALE_MAXIMUM)
|
|
||||||
guess = 1;
|
|
||||||
|
|
||||||
return guess;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ExplicitSingletons
|
|
||||||
{
|
|
||||||
// These need to be listed in the order they are populated in main.
|
|
||||||
std::unique_ptr<GlobalPrefs> globalPrefs;
|
|
||||||
http::RequestManagerPtr requestManager;
|
|
||||||
std::unique_ptr<Client> client;
|
|
||||||
std::unique_ptr<SaveRenderer> saveRenderer;
|
|
||||||
std::unique_ptr<RNG> rng;
|
|
||||||
std::unique_ptr<Favorite> favorite;
|
|
||||||
std::unique_ptr<ui::Engine> engine;
|
|
||||||
std::unique_ptr<GameController> gameController;
|
|
||||||
};
|
|
||||||
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
|
||||||
{
|
|
||||||
Platform::SetupCrt();
|
|
||||||
atexit([]() {
|
|
||||||
ui::Engine::Ref().CloseWindow();
|
|
||||||
explicitSingletons.reset();
|
|
||||||
if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL)
|
|
||||||
{
|
|
||||||
// * nvidia-460 egl registers callbacks with x11 that end up being called
|
|
||||||
// after egl is unloaded unless we grab it here and release it after
|
|
||||||
// sdl closes the display. this is an nvidia driver weirdness but
|
|
||||||
// technically an sdl bug. glfw has this fixed:
|
|
||||||
// https://github.com/glfw/glfw/commit/9e6c0c747be838d1f3dc38c2924a47a42416c081
|
|
||||||
SDL_GL_LoadLibrary(NULL);
|
|
||||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
||||||
SDL_GL_UnloadLibrary();
|
|
||||||
}
|
|
||||||
SDL_Quit();
|
|
||||||
});
|
|
||||||
explicitSingletons = std::make_unique<ExplicitSingletons>();
|
|
||||||
currentWidth = WINDOWW;
|
|
||||||
currentHeight = WINDOWH;
|
|
||||||
|
|
||||||
|
|
||||||
// https://bugzilla.libsdl.org/show_bug.cgi?id=3796
|
|
||||||
if (SDL_Init(0) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform::originalCwd = Platform::GetCwd();
|
|
||||||
|
|
||||||
using Argument = std::optional<ByteString>;
|
|
||||||
std::map<ByteString, Argument> arguments;
|
|
||||||
|
|
||||||
for (auto i = 1; i < argc; ++i)
|
|
||||||
{
|
|
||||||
auto str = ByteString(argv[i]);
|
|
||||||
if (str.BeginsWith("file://"))
|
|
||||||
{
|
|
||||||
arguments.insert({ "open", format::URLDecode(str.substr(7 /* length of the "file://" prefix */)) });
|
|
||||||
}
|
|
||||||
else if (str.BeginsWith("ptsave:"))
|
|
||||||
{
|
|
||||||
arguments.insert({ "ptsave", str });
|
|
||||||
}
|
|
||||||
else if (auto split = str.SplitBy(':'))
|
|
||||||
{
|
|
||||||
arguments.insert({ split.Before(), split.After() });
|
|
||||||
}
|
|
||||||
else if (auto split = str.SplitBy('='))
|
|
||||||
{
|
|
||||||
arguments.insert({ split.Before(), split.After() });
|
|
||||||
}
|
|
||||||
else if (str == "open" || str == "ptsave" || str == "ddir")
|
|
||||||
{
|
|
||||||
if (i + 1 < argc)
|
|
||||||
{
|
|
||||||
arguments.insert({ str, argv[i + 1] });
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cerr << "no value provided for command line parameter " << str << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
arguments.insert({ str, "" }); // so .has_value() is true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ddirArg = arguments["ddir"];
|
|
||||||
if (ddirArg.has_value())
|
|
||||||
{
|
|
||||||
if (Platform::ChangeDir(ddirArg.value()))
|
|
||||||
Platform::sharedCwd = Platform::GetCwd();
|
|
||||||
else
|
|
||||||
perror("failed to chdir to requested ddir");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char *ddir = SDL_GetPrefPath(NULL, APPDATA);
|
|
||||||
if (!Platform::FileExists("powder.pref"))
|
|
||||||
{
|
|
||||||
if (ddir)
|
|
||||||
{
|
|
||||||
if (!Platform::ChangeDir(ddir))
|
|
||||||
{
|
|
||||||
perror("failed to chdir to default ddir");
|
|
||||||
SDL_free(ddir);
|
|
||||||
ddir = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ddir)
|
|
||||||
{
|
|
||||||
Platform::sharedCwd = ddir;
|
|
||||||
SDL_free(ddir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We're now in the correct directory, time to get prefs.
|
|
||||||
explicitSingletons->globalPrefs = std::make_unique<GlobalPrefs>();
|
|
||||||
|
|
||||||
auto &prefs = GlobalPrefs::Ref();
|
|
||||||
scale = prefs.Get("Scale", 1);
|
|
||||||
resizable = prefs.Get("Resizable", false);
|
|
||||||
fullscreen = prefs.Get("Fullscreen", false);
|
|
||||||
altFullscreen = prefs.Get("AltFullscreen", false);
|
|
||||||
forceIntegerScaling = prefs.Get("ForceIntegerScaling", true);
|
|
||||||
momentumScroll = prefs.Get("MomentumScroll", true);
|
|
||||||
showAvatars = prefs.Get("ShowAvatars", true);
|
|
||||||
|
|
||||||
auto true_string = [](ByteString str) {
|
|
||||||
str = str.ToLower();
|
|
||||||
return str == "true" ||
|
|
||||||
str == "t" ||
|
|
||||||
str == "on" ||
|
|
||||||
str == "yes" ||
|
|
||||||
str == "y" ||
|
|
||||||
str == "1" ||
|
|
||||||
str == ""; // standalone "redirect" or "disable-bluescreen" or similar arguments
|
|
||||||
};
|
|
||||||
auto true_arg = [&true_string](Argument arg) {
|
|
||||||
return arg.has_value() && true_string(arg.value());
|
|
||||||
};
|
|
||||||
|
|
||||||
auto kioskArg = arguments["kiosk"];
|
|
||||||
if (kioskArg.has_value())
|
|
||||||
{
|
|
||||||
fullscreen = true_string(kioskArg.value());
|
|
||||||
prefs.Set("Fullscreen", fullscreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (true_arg(arguments["redirect"]))
|
|
||||||
{
|
|
||||||
FILE *new_stdout = freopen("stdout.log", "w", stdout);
|
|
||||||
FILE *new_stderr = freopen("stderr.log", "w", stderr);
|
|
||||||
if (!new_stdout || !new_stderr)
|
|
||||||
{
|
|
||||||
exit(42);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto scaleArg = arguments["scale"];
|
|
||||||
if (scaleArg.has_value())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
scale = scaleArg.value().ToNumber<int>();
|
|
||||||
prefs.Set("Scale", scale);
|
|
||||||
}
|
|
||||||
catch (const std::runtime_error &e)
|
|
||||||
{
|
|
||||||
std::cerr << "failed to set scale: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto clientConfig = [&prefs](Argument arg, ByteString name, ByteString defaultValue) {
|
|
||||||
ByteString value;
|
|
||||||
if (arg.has_value())
|
|
||||||
{
|
|
||||||
value = arg.value();
|
|
||||||
if (value == "")
|
|
||||||
{
|
|
||||||
value = defaultValue;
|
|
||||||
}
|
|
||||||
prefs.Set(name, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
value = prefs.Get(name, defaultValue);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
ByteString proxyString = clientConfig(arguments["proxy"], "Proxy", "");
|
|
||||||
ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", "");
|
|
||||||
ByteString capathString = clientConfig(arguments["capath"], "CAPath", "");
|
|
||||||
bool disableNetwork = true_arg(arguments["disable-network"]);
|
|
||||||
explicitSingletons->requestManager = http::RequestManager::Create(proxyString, cafileString, capathString, disableNetwork);
|
|
||||||
|
|
||||||
explicitSingletons->client = std::make_unique<Client>();
|
|
||||||
Client::Ref().Initialize();
|
|
||||||
|
|
||||||
explicitSingletons->saveRenderer = std::make_unique<SaveRenderer>();
|
|
||||||
explicitSingletons->rng = std::make_unique<RNG>();
|
|
||||||
explicitSingletons->favorite = std::make_unique<Favorite>();
|
|
||||||
explicitSingletons->engine = std::make_unique<ui::Engine>();
|
|
||||||
|
|
||||||
// TODO: maybe bind the maximum allowed scale to screen size somehow
|
|
||||||
if(scale < 1 || scale > SCALE_MAXIMUM)
|
|
||||||
scale = 1;
|
|
||||||
|
|
||||||
SDLOpen();
|
|
||||||
|
|
||||||
if (Client::Ref().IsFirstRun())
|
|
||||||
{
|
|
||||||
scale = GuessBestScale();
|
|
||||||
if (scale > 1)
|
|
||||||
{
|
|
||||||
prefs.Set("Scale", scale);
|
|
||||||
SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
|
|
||||||
showLargeScreenDialog = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StopTextInput();
|
|
||||||
|
|
||||||
auto &engine = ui::Engine::Ref();
|
|
||||||
engine.g = new Graphics();
|
|
||||||
engine.Scale = scale;
|
|
||||||
engine.SetResizable(resizable);
|
|
||||||
engine.Fullscreen = fullscreen;
|
|
||||||
engine.SetAltFullscreen(altFullscreen);
|
|
||||||
engine.SetForceIntegerScaling(forceIntegerScaling);
|
|
||||||
engine.MomentumScroll = momentumScroll;
|
|
||||||
engine.ShowAvatars = showAvatars;
|
|
||||||
engine.SetMaxSize(desktopWidth, desktopHeight);
|
|
||||||
engine.Begin(WINDOWW, WINDOWH);
|
|
||||||
engine.SetFastQuit(prefs.Get("FastQuit", true));
|
|
||||||
|
|
||||||
bool enableBluescreen = !DEBUG && !true_arg(arguments["disable-bluescreen"]);
|
|
||||||
if (enableBluescreen)
|
|
||||||
{
|
|
||||||
//Get ready to catch any dodgy errors
|
|
||||||
signal(SIGSEGV, SigHandler);
|
|
||||||
signal(SIGFPE, SigHandler);
|
|
||||||
signal(SIGILL, SigHandler);
|
|
||||||
signal(SIGABRT, SigHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (X86)
|
|
||||||
{
|
|
||||||
X86KillDenormals();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto wrapWithBluescreen = [&]() {
|
|
||||||
explicitSingletons->gameController = std::make_unique<GameController>();
|
|
||||||
auto *gameController = explicitSingletons->gameController.get();
|
|
||||||
engine.ShowWindow(gameController->GetView());
|
|
||||||
|
|
||||||
auto openArg = arguments["open"];
|
|
||||||
if (openArg.has_value())
|
|
||||||
{
|
|
||||||
if constexpr (DEBUG)
|
|
||||||
{
|
|
||||||
std::cout << "Loading " << openArg.value() << std::endl;
|
|
||||||
}
|
|
||||||
if (Platform::FileExists(openArg.value()))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::vector<char> gameSaveData;
|
|
||||||
if (!Platform::ReadFile(gameSaveData, openArg.value()))
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", "Could not read file");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SaveFile * newFile = new SaveFile(openArg.value());
|
|
||||||
GameSave * newSave = new GameSave(std::move(gameSaveData));
|
|
||||||
newFile->SetGameSave(newSave);
|
|
||||||
gameController->LoadSaveFile(newFile);
|
|
||||||
delete newFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (std::exception & e)
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", "Could not open file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ptsaveArg = arguments["ptsave"];
|
|
||||||
if (ptsaveArg.has_value())
|
|
||||||
{
|
|
||||||
engine.g->Clear();
|
|
||||||
engine.g->fillrect((engine.GetWidth()/2)-101, (engine.GetHeight()/2)-26, 202, 52, 0, 0, 0, 210);
|
|
||||||
engine.g->drawrect((engine.GetWidth()/2)-100, (engine.GetHeight()/2)-25, 200, 50, 255, 255, 255, 180);
|
|
||||||
engine.g->drawtext((engine.GetWidth()/2)-(Graphics::textwidth("Loading save...")/2), (engine.GetHeight()/2)-5, "Loading save...", style::Colour::InformationTitle.Red, style::Colour::InformationTitle.Green, style::Colour::InformationTitle.Blue, 255);
|
|
||||||
|
|
||||||
blit(engine.g->vid);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ByteString saveIdPart;
|
|
||||||
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
|
|
||||||
{
|
|
||||||
if (split.Before() != "ptsave")
|
|
||||||
throw std::runtime_error("Not a ptsave link");
|
|
||||||
saveIdPart = split.After().SplitBy('#').Before();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error("Invalid save link");
|
|
||||||
|
|
||||||
if (!saveIdPart.size())
|
|
||||||
throw std::runtime_error("No Save ID");
|
|
||||||
if constexpr (DEBUG)
|
|
||||||
{
|
|
||||||
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
|
|
||||||
}
|
|
||||||
int saveId = saveIdPart.ToNumber<int>();
|
|
||||||
|
|
||||||
SaveInfo * newSave = Client::Ref().GetSave(saveId, 0);
|
|
||||||
if (!newSave)
|
|
||||||
throw std::runtime_error("Could not load save info");
|
|
||||||
auto saveData = Client::Ref().GetSaveData(saveId, 0);
|
|
||||||
if (!saveData.size())
|
|
||||||
throw std::runtime_error(("Could not load save\n" + Client::Ref().GetLastError()).ToUtf8());
|
|
||||||
GameSave * newGameSave = new GameSave(std::move(saveData));
|
|
||||||
newSave->SetGameSave(newGameSave);
|
|
||||||
|
|
||||||
gameController->LoadSave(newSave);
|
|
||||||
delete newSave;
|
|
||||||
}
|
|
||||||
catch (std::exception & e)
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EngineProcess();
|
|
||||||
SaveWindowPosition();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (enableBluescreen)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
wrapWithBluescreen();
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
BlueScreen(ByteString(e.what()).FromUtf8());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wrapWithBluescreen();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
46
src/PowderToySDL.h
Normal file
46
src/PowderToySDL.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common/String.h"
|
||||||
|
#include "graphics/Pixel.h"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
extern int desktopWidth;
|
||||||
|
extern int desktopHeight;
|
||||||
|
extern SDL_Window *sdl_window;
|
||||||
|
extern SDL_Renderer *sdl_renderer;
|
||||||
|
extern SDL_Texture *sdl_texture;
|
||||||
|
extern int scale;
|
||||||
|
extern bool fullscreen;
|
||||||
|
extern bool altFullscreen;
|
||||||
|
extern bool forceIntegerScaling;
|
||||||
|
extern bool resizable;
|
||||||
|
extern bool momentumScroll;
|
||||||
|
extern bool showAvatars;
|
||||||
|
extern uint64_t lastTick;
|
||||||
|
extern uint64_t lastFpsUpdate;
|
||||||
|
extern bool showLargeScreenDialog;
|
||||||
|
extern int mousex;
|
||||||
|
extern int mousey;
|
||||||
|
extern int mouseButton;
|
||||||
|
extern bool mouseDown;
|
||||||
|
extern bool calculatedInitialMouse;
|
||||||
|
extern bool hasMouseMoved;
|
||||||
|
|
||||||
|
void EngineProcess();
|
||||||
|
void StartTextInput();
|
||||||
|
void StopTextInput();
|
||||||
|
void SetTextInputRect(int x, int y, int w, int h);
|
||||||
|
void ClipboardPush(ByteString text);
|
||||||
|
ByteString ClipboardPull();
|
||||||
|
int GetModifiers();
|
||||||
|
unsigned int GetTicks();
|
||||||
|
void CalculateMousePosition(int *x, int *y);
|
||||||
|
void blit(pixel *vid);
|
||||||
|
void SDLOpen();
|
||||||
|
void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_);
|
||||||
|
bool RecreateWindow();
|
||||||
|
void LoadWindowPosition();
|
||||||
|
void SaveWindowPosition();
|
||||||
|
void LargeScreenDialog();
|
||||||
|
void TickClient();
|
||||||
|
void EventProcess(const SDL_Event &event);
|
@ -7,7 +7,7 @@
|
|||||||
#include "gui/interface/Label.h"
|
#include "gui/interface/Label.h"
|
||||||
#include "gui/interface/ScrollPanel.h"
|
#include "gui/interface/ScrollPanel.h"
|
||||||
|
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
|
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
#include "gui/interface/Label.h"
|
#include "gui/interface/Label.h"
|
||||||
|
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
|
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "gui/interface/Textbox.h"
|
#include "gui/interface/Textbox.h"
|
||||||
|
|
||||||
#include "gui/Style.h"
|
#include "gui/Style.h"
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
|
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "gui/Style.h"
|
#include "gui/Style.h"
|
||||||
#include "Label.h"
|
#include "Label.h"
|
||||||
|
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
|
|
||||||
namespace ui
|
namespace ui
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
#include "common/Platform.h"
|
#include "common/Platform.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "Label.h"
|
#include "Label.h"
|
||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
#include "Point.h"
|
#include "Point.h"
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
#include "ContextMenu.h"
|
#include "ContextMenu.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "graphics/FontReader.h"
|
#include "graphics/FontReader.h"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "Textbox.h"
|
#include "Textbox.h"
|
||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
#include "common/Platform.h"
|
#include "common/Platform.h"
|
||||||
#include "graphics/FontReader.h"
|
#include "graphics/FontReader.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "gui/interface/Textbox.h"
|
#include "gui/interface/Textbox.h"
|
||||||
#include "gui/interface/Label.h"
|
#include "gui/interface/Label.h"
|
||||||
#include "gui/interface/SaveButton.h"
|
#include "gui/interface/SaveButton.h"
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
#include "client/SaveFile.h"
|
#include "client/SaveFile.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "SimulationConfig.h"
|
#include "SimulationConfig.h"
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include "gui/interface/RichLabel.h"
|
#include "gui/interface/RichLabel.h"
|
||||||
#include "gui/interface/Textbox.h"
|
#include "gui/interface/Textbox.h"
|
||||||
#include "gui/interface/Spinner.h"
|
#include "gui/interface/Spinner.h"
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "SimulationConfig.h"
|
#include "SimulationConfig.h"
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "LuaScriptHelper.h"
|
#include "LuaScriptHelper.h"
|
||||||
#include "LuaScriptInterface.h"
|
#include "LuaScriptInterface.h"
|
||||||
#include "LuaSmartRef.h"
|
#include "LuaSmartRef.h"
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
#include "prefs/GlobalPrefs.h"
|
#include "prefs/GlobalPrefs.h"
|
||||||
#include "common/Platform.h"
|
#include "common/Platform.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
#include "LuaSocket.h"
|
#include "LuaSocket.h"
|
||||||
#include "LuaHttp.h"
|
#include "LuaHttp.h"
|
||||||
#include "LuaSDLKeys.h"
|
#include "LuaSDLKeys.h"
|
||||||
#include "PowderToy.h"
|
#include "PowderToySDL.h"
|
||||||
#include "TPTScriptInterface.h"
|
#include "TPTScriptInterface.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
@ -39,6 +39,7 @@ conf_data.set('APPVENDOR', get_option('app_vendor'))
|
|||||||
powder_files = files(
|
powder_files = files(
|
||||||
'SDLCompat.cpp',
|
'SDLCompat.cpp',
|
||||||
'PowderToySDL.cpp',
|
'PowderToySDL.cpp',
|
||||||
|
'PowderToy.cpp',
|
||||||
'lua/CommandInterface.cpp',
|
'lua/CommandInterface.cpp',
|
||||||
'lua/TPTScriptInterface.cpp',
|
'lua/TPTScriptInterface.cpp',
|
||||||
'lua/TPTSTypes.cpp',
|
'lua/TPTSTypes.cpp',
|
||||||
@ -53,6 +54,7 @@ render_files = files(
|
|||||||
|
|
||||||
font_files = files(
|
font_files = files(
|
||||||
'PowderToyFontEditor.cpp',
|
'PowderToyFontEditor.cpp',
|
||||||
|
'PowderToySDL.cpp',
|
||||||
)
|
)
|
||||||
|
|
||||||
common_files = files(
|
common_files = files(
|
||||||
|
Reference in New Issue
Block a user