Refactor preferences

This commit is contained in:
Tamás Bálint Misius 2023-01-18 15:07:51 +01:00
parent 220844521a
commit afda2826bf
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
26 changed files with 669 additions and 707 deletions

View File

@ -32,6 +32,7 @@
#include "X86KillDenormals.h" #include "X86KillDenormals.h"
#include "Misc.h" #include "Misc.h"
#include "prefs/GlobalPrefs.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/GameSave.h" #include "client/GameSave.h"
#include "client/SaveFile.h" #include "client/SaveFile.h"
@ -97,8 +98,9 @@ int GetModifiers()
void LoadWindowPosition() void LoadWindowPosition()
{ {
int savedWindowX = Client::Ref().GetPrefInteger("WindowX", INT_MAX); auto &prefs = GlobalPrefs::Ref();
int savedWindowY = Client::Ref().GetPrefInteger("WindowY", INT_MAX); int savedWindowX = prefs.Get("WindowX", INT_MAX);
int savedWindowY = prefs.Get("WindowY", INT_MAX);
int borderTop, borderLeft; int borderTop, borderLeft;
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr); SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
@ -133,8 +135,9 @@ void SaveWindowPosition()
int borderTop, borderLeft; int borderTop, borderLeft;
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr); SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
Client::Ref().SetPref("WindowX", x - borderLeft); auto &prefs = GlobalPrefs::Ref();
Client::Ref().SetPref("WindowY", y - borderTop); prefs.Set("WindowX", x - borderLeft);
prefs.Set("WindowY", y - borderTop);
} }
void CalculateMousePosition(int *x, int *y) void CalculateMousePosition(int *x, int *y)
@ -453,7 +456,7 @@ void LargeScreenDialog()
message << "\nTo undo this, hit Cancel. You can change this in settings at any time."; message << "\nTo undo this, hit Cancel. You can change this in settings at any time.";
if (!ConfirmPrompt::Blocking("Large screen detected", message.Build())) if (!ConfirmPrompt::Blocking("Large screen detected", message.Build()))
{ {
Client::Ref().SetPref("Scale", 1); GlobalPrefs::Ref().Set("Scale", 1);
engine->SetScale(1); engine->SetScale(1);
} }
} }
@ -603,8 +606,21 @@ int GuessBestScale()
return guess; return guess;
} }
struct ExplicitSingletons
{
// These need to be listed in the order they are populated in main.
std::unique_ptr<GlobalPrefs> globalPrefs;
std::unique_ptr<Client> client;
http::RequestManagerPtr requestManager;
};
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
atexit([]() {
explicitSingletons.reset();
});
explicitSingletons = std::make_unique<ExplicitSingletons>();
#ifdef WIN #ifdef WIN
if constexpr (DEBUG) if constexpr (DEBUG)
{ {
@ -710,14 +726,17 @@ int main(int argc, char * argv[])
SDL_free(ddir); SDL_free(ddir);
} }
} }
// We're now in the correct directory, time to get prefs.
explicitSingletons->globalPrefs = std::make_unique<GlobalPrefs>();
scale = Client::Ref().GetPrefInteger("Scale", 1); auto &prefs = GlobalPrefs::Ref();
resizable = Client::Ref().GetPrefBool("Resizable", false); scale = prefs.Get("Scale", 1);
fullscreen = Client::Ref().GetPrefBool("Fullscreen", false); resizable = prefs.Get("Resizable", false);
altFullscreen = Client::Ref().GetPrefBool("AltFullscreen", false); fullscreen = prefs.Get("Fullscreen", false);
forceIntegerScaling = Client::Ref().GetPrefBool("ForceIntegerScaling", true); altFullscreen = prefs.Get("AltFullscreen", false);
momentumScroll = Client::Ref().GetPrefBool("MomentumScroll", true); forceIntegerScaling = prefs.Get("ForceIntegerScaling", true);
showAvatars = Client::Ref().GetPrefBool("ShowAvatars", true); momentumScroll = prefs.Get("MomentumScroll", true);
showAvatars = prefs.Get("ShowAvatars", true);
auto true_string = [](ByteString str) { auto true_string = [](ByteString str) {
str = str.ToLower(); str = str.ToLower();
@ -736,7 +755,7 @@ int main(int argc, char * argv[])
if (kioskArg.has_value()) if (kioskArg.has_value())
{ {
fullscreen = true_string(kioskArg.value()); fullscreen = true_string(kioskArg.value());
Client::Ref().SetPref("Fullscreen", fullscreen); prefs.Set("Fullscreen", fullscreen);
} }
if (true_arg(arguments["redirect"])) if (true_arg(arguments["redirect"]))
@ -755,7 +774,7 @@ int main(int argc, char * argv[])
try try
{ {
scale = scaleArg.value().ToNumber<int>(); scale = scaleArg.value().ToNumber<int>();
Client::Ref().SetPref("Scale", scale); prefs.Set("Scale", scale);
} }
catch (const std::runtime_error &e) catch (const std::runtime_error &e)
{ {
@ -763,7 +782,7 @@ int main(int argc, char * argv[])
} }
} }
auto clientConfig = [](Argument arg, ByteString name, ByteString defaultValue) { auto clientConfig = [&prefs](Argument arg, ByteString name, ByteString defaultValue) {
ByteString value; ByteString value;
if (arg.has_value()) if (arg.has_value())
{ {
@ -772,11 +791,11 @@ int main(int argc, char * argv[])
{ {
value = defaultValue; value = defaultValue;
} }
Client::Ref().SetPref(name, value); prefs.Set(name, value);
} }
else else
{ {
value = Client::Ref().GetPrefByteString(name, defaultValue); value = prefs.Get(name, defaultValue);
} }
return value; return value;
}; };
@ -784,8 +803,9 @@ int main(int argc, char * argv[])
ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", ""); ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", "");
ByteString capathString = clientConfig(arguments["capath"], "CAPath", ""); ByteString capathString = clientConfig(arguments["capath"], "CAPath", "");
bool disableNetwork = true_arg(arguments["disable-network"]); bool disableNetwork = true_arg(arguments["disable-network"]);
auto requestManager = http::RequestManager::Create(proxyString, cafileString, capathString, disableNetwork); explicitSingletons->requestManager = http::RequestManager::Create(proxyString, cafileString, capathString, disableNetwork);
explicitSingletons->client = std::make_unique<Client>();
Client::Ref().Initialize(); Client::Ref().Initialize();
// TODO: maybe bind the maximum allowed scale to screen size somehow // TODO: maybe bind the maximum allowed scale to screen size somehow
@ -799,7 +819,7 @@ int main(int argc, char * argv[])
scale = GuessBestScale(); scale = GuessBestScale();
if (scale > 1) if (scale > 1)
{ {
Client::Ref().SetPref("Scale", scale); prefs.Set("Scale", scale);
SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale); SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
showLargeScreenDialog = true; showLargeScreenDialog = true;
} }
@ -819,7 +839,7 @@ int main(int argc, char * argv[])
engine = &ui::Engine::Ref(); engine = &ui::Engine::Ref();
engine->SetMaxSize(desktopWidth, desktopHeight); engine->SetMaxSize(desktopWidth, desktopHeight);
engine->Begin(WINDOWW, WINDOWH); engine->Begin(WINDOWW, WINDOWH);
engine->SetFastQuit(Client::Ref().GetPrefBool("FastQuit", true)); engine->SetFastQuit(prefs.Get("FastQuit", true));
bool enableBluescreen = !DEBUG && !true_arg(arguments["disable-bluescreen"]); bool enableBluescreen = !DEBUG && !true_arg(arguments["disable-bluescreen"]);
if (enableBluescreen) if (enableBluescreen)
@ -949,7 +969,6 @@ int main(int argc, char * argv[])
ui::Engine::Ref().CloseWindow(); ui::Engine::Ref().CloseWindow();
delete gameController; delete gameController;
delete ui::Engine::Ref().g; delete ui::Engine::Ref().g;
Client::Ref().Shutdown();
if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL) if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL)
{ {
// * nvidia-460 egl registers callbacks with x11 that end up being called // * nvidia-460 egl registers callbacks with x11 that end up being called

View File

@ -1,5 +1,6 @@
#include "Client.h" #include "Client.h"
#include "prefs/GlobalPrefs.h"
#include "client/http/Request.h" // includes curl.h, needs to come first to silence a warning on windows #include "client/http/Request.h" // includes curl.h, needs to come first to silence a warning on windows
#include <cstring> #include <cstring>
@ -65,48 +66,30 @@ Client::Client():
updateAvailable(false), updateAvailable(false),
authUser(0, "") authUser(0, "")
{ {
//Read config auto &prefs = GlobalPrefs::Ref();
std::ifstream configFile; authUser.UserID = prefs.Get("User.ID", 0);
configFile.open("powder.pref", std::ios::binary); authUser.Username = prefs.Get("User.Username", ByteString(""));
if (configFile) authUser.SessionID = prefs.Get("User.SessionID", ByteString(""));
authUser.SessionKey = prefs.Get("User.SessionKey", ByteString(""));
auto elevation = prefs.Get("User.Elevation", ByteString(""));
authUser.UserElevation = User::ElevationNone;
if (elevation == "Admin")
{ {
try authUser.UserElevation = User::ElevationAdmin;
{
preferences.clear();
configFile >> preferences;
int ID = preferences["User"]["ID"].asInt();
ByteString Username = preferences["User"]["Username"].asString();
ByteString SessionID = preferences["User"]["SessionID"].asString();
ByteString SessionKey = preferences["User"]["SessionKey"].asString();
ByteString Elevation = preferences["User"]["Elevation"].asString();
authUser.UserID = ID;
authUser.Username = Username;
authUser.SessionID = SessionID;
authUser.SessionKey = SessionKey;
if (Elevation == "Admin")
authUser.UserElevation = User::ElevationAdmin;
else if (Elevation == "Mod")
authUser.UserElevation = User::ElevationModerator;
else
authUser.UserElevation = User::ElevationNone;
}
catch (std::exception &e)
{
}
configFile.close();
firstRun = false;
} }
else if (elevation == "Mod")
firstRun = true; {
authUser.UserElevation = User::ElevationModerator;
}
firstRun = !prefs.BackedByFile();
} }
void Client::Initialize() void Client::Initialize()
{ {
if (GetPrefBool("version.update", false)) auto &prefs = GlobalPrefs::Ref();
if (prefs.Get("version.update", false))
{ {
SetPref("version.update", false); prefs.Set("version.update", false);
update_finish(); update_finish();
} }
@ -412,51 +395,38 @@ void Client::RemoveListener(ClientListener * listener)
} }
} }
void Client::WritePrefs()
{
std::ofstream configFile;
configFile.open("powder.pref", std::ios::trunc);
if (configFile)
{
if (authUser.UserID)
{
preferences["User"]["ID"] = authUser.UserID;
preferences["User"]["SessionID"] = authUser.SessionID;
preferences["User"]["SessionKey"] = authUser.SessionKey;
preferences["User"]["Username"] = authUser.Username;
if (authUser.UserElevation == User::ElevationAdmin)
preferences["User"]["Elevation"] = "Admin";
else if (authUser.UserElevation == User::ElevationModerator)
preferences["User"]["Elevation"] = "Mod";
else
preferences["User"]["Elevation"] = "None";
}
else
{
preferences["User"] = Json::nullValue;
}
configFile << preferences;
configFile.close();
}
}
void Client::Shutdown()
{
//Save config
WritePrefs();
}
Client::~Client() Client::~Client()
{ {
} }
void Client::SetAuthUser(User user) void Client::SetAuthUser(User user)
{ {
authUser = user; authUser = user;
WritePrefs(); {
auto &prefs = GlobalPrefs::Ref();
Prefs::DeferWrite dw(prefs);
if (authUser.UserID)
{
prefs.Set("User.ID", authUser.UserID);
prefs.Set("User.SessionID", authUser.SessionID);
prefs.Set("User.SessionKey", authUser.SessionKey);
prefs.Set("User.Username", authUser.Username);
ByteString elevation = "None";
if (authUser.UserElevation == User::ElevationAdmin)
{
elevation = "Admin";
}
if (authUser.UserElevation == User::ElevationModerator)
{
elevation = "Mod";
}
prefs.Set("User.Elevation", elevation);
}
else
{
prefs.Clear("User");
}
}
notifyAuthUserChanged(); notifyAuthUserChanged();
} }
@ -1264,254 +1234,6 @@ void Client::SaveAuthorInfo(Json::Value *saveInto)
} }
} }
// powder.pref preference getting / setting functions
// Recursively go down the json to get the setting we want
Json::Value Client::GetPref(Json::Value root, ByteString prop, Json::Value defaultValue)
{
try
{
if(ByteString::Split split = prop.SplitBy('.'))
return GetPref(root[split.Before()], split.After(), defaultValue);
else
return root.get(prop, defaultValue);
}
catch (std::exception & e)
{
return defaultValue;
}
}
ByteString Client::GetPrefByteString(ByteString prop, ByteString defaultValue)
{
try
{
return GetPref(preferences, prop, defaultValue).asString();
}
catch (std::exception & e)
{
return defaultValue;
}
}
String Client::GetPrefString(ByteString prop, String defaultValue)
{
try
{
return ByteString(GetPref(preferences, prop, defaultValue.ToUtf8()).asString()).FromUtf8(false);
}
catch (std::exception & e)
{
return defaultValue;
}
}
double Client::GetPrefNumber(ByteString prop, double defaultValue)
{
try
{
return GetPref(preferences, prop, defaultValue).asDouble();
}
catch (std::exception & e)
{
return defaultValue;
}
}
int Client::GetPrefInteger(ByteString prop, int defaultValue)
{
try
{
return GetPref(preferences, prop, defaultValue).asInt();
}
catch (std::exception & e)
{
return defaultValue;
}
}
unsigned int Client::GetPrefUInteger(ByteString prop, unsigned int defaultValue)
{
try
{
return GetPref(preferences, prop, defaultValue).asUInt();
}
catch (std::exception & e)
{
return defaultValue;
}
}
bool Client::GetPrefBool(ByteString prop, bool defaultValue)
{
try
{
return GetPref(preferences, prop, defaultValue).asBool();
}
catch (std::exception & e)
{
return defaultValue;
}
}
std::vector<ByteString> Client::GetPrefByteStringArray(ByteString prop)
{
try
{
std::vector<ByteString> ret;
Json::Value arr = GetPref(preferences, prop);
for (int i = 0; i < (int)arr.size(); i++)
ret.push_back(arr[i].asString());
return ret;
}
catch (std::exception & e)
{
}
return std::vector<ByteString>();
}
std::vector<String> Client::GetPrefStringArray(ByteString prop)
{
try
{
std::vector<String> ret;
Json::Value arr = GetPref(preferences, prop);
for (int i = 0; i < (int)arr.size(); i++)
ret.push_back(ByteString(arr[i].asString()).FromUtf8(false));
return ret;
}
catch (std::exception & e)
{
}
return std::vector<String>();
}
std::vector<double> Client::GetPrefNumberArray(ByteString prop)
{
try
{
std::vector<double> ret;
Json::Value arr = GetPref(preferences, prop);
for (int i = 0; i < (int)arr.size(); i++)
ret.push_back(arr[i].asDouble());
return ret;
}
catch (std::exception & e)
{
}
return std::vector<double>();
}
std::vector<int> Client::GetPrefIntegerArray(ByteString prop)
{
try
{
std::vector<int> ret;
Json::Value arr = GetPref(preferences, prop);
for (int i = 0; i < (int)arr.size(); i++)
ret.push_back(arr[i].asInt());
return ret;
}
catch (std::exception & e)
{
}
return std::vector<int>();
}
std::vector<unsigned int> Client::GetPrefUIntegerArray(ByteString prop)
{
try
{
std::vector<unsigned int> ret;
Json::Value arr = GetPref(preferences, prop);
for (int i = 0; i < (int)arr.size(); i++)
ret.push_back(arr[i].asUInt());
return ret;
}
catch (std::exception & e)
{
}
return std::vector<unsigned int>();
}
std::vector<bool> Client::GetPrefBoolArray(ByteString prop)
{
try
{
std::vector<bool> ret;
Json::Value arr = GetPref(preferences, prop);
for (int i = 0; i < (int)arr.size(); i++)
ret.push_back(arr[i].asBool());
return ret;
}
catch (std::exception & e)
{
}
return std::vector<bool>();
}
// Helper preference setting function.
// To actually save any changes to preferences, we need to directly do preferences[property] = thing
// any other way will set the value of a copy of preferences, not the original
// This function will recursively go through and create an object with the property we wanted set,
// and return it to SetPref to do the actual setting
Json::Value Client::SetPrefHelper(Json::Value root, ByteString prop, Json::Value value)
{
if(ByteString::Split split = prop.SplitBy('.'))
{
Json::Value toSet = GetPref(root, split.Before());
toSet = SetPrefHelper(toSet, split.After(), value);
root[split.Before()] = toSet;
}
else
root[prop] = value;
return root;
}
void Client::SetPref(ByteString prop, Json::Value value)
{
try
{
if(ByteString::Split split = prop.SplitBy('.'))
preferences[split.Before()] = SetPrefHelper(preferences[split.Before()], split.After(), value);
else
preferences[prop] = value;
WritePrefs();
}
catch (std::exception & e)
{
}
}
void Client::SetPref(ByteString prop, std::vector<Json::Value> value)
{
try
{
Json::Value arr;
for (int i = 0; i < (int)value.size(); i++)
{
arr.append(value[i]);
}
SetPref(prop, arr);
}
catch (std::exception & e)
{
}
}
void Client::SetPrefUnicode(ByteString prop, String value)
{
SetPref(prop, value.ToUtf8());
}
bool Client::DoInstallation() bool Client::DoInstallation()
{ {
bool ok = true; bool ok = true;
@ -1652,8 +1374,9 @@ bool Client::DoInstallation()
bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor) bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor)
{ {
auto customGOLTypes = Client::Ref().GetPrefByteStringArray("CustomGOL.Types"); auto &prefs = GlobalPrefs::Ref();
Json::Value newCustomGOLTypes(Json::arrayValue); auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector<ByteString>{});
std::vector<ByteString> newCustomGOLTypes;
bool nameTaken = false; bool nameTaken = false;
for (auto gol : customGOLTypes) for (auto gol : customGOLTypes)
{ {
@ -1665,14 +1388,147 @@ bool AddCustomGol(String ruleString, String nameString, unsigned int highColor,
nameTaken = true; nameTaken = true;
} }
} }
newCustomGOLTypes.append(gol); newCustomGOLTypes.push_back(gol);
} }
if (nameTaken) if (nameTaken)
return false; return false;
StringBuilder sb; StringBuilder sb;
sb << nameString << " " << ruleString << " " << highColor << " " << lowColor; sb << nameString << " " << ruleString << " " << highColor << " " << lowColor;
newCustomGOLTypes.append(sb.Build().ToUtf8()); newCustomGOLTypes.push_back(sb.Build().ToUtf8());
Client::Ref().SetPref("CustomGOL.Types", newCustomGOLTypes); prefs.Set("CustomGOL.Types", newCustomGOLTypes);
return true; return true;
} }
String Client::DoMigration(ByteString fromDir, ByteString toDir)
{
if (fromDir.at(fromDir.length() - 1) != '/')
fromDir = fromDir + '/';
if (toDir.at(toDir.length() - 1) != '/')
toDir = toDir + '/';
std::ofstream logFile(fromDir + "/migrationlog.txt", std::ios::out);
logFile << "Running migration of data from " << fromDir + " to " << toDir << std::endl;
// Get lists of files to migrate
auto stamps = Platform::DirectorySearch(fromDir + "stamps", "", { ".stm" });
auto saves = Platform::DirectorySearch(fromDir + "Saves", "", { ".cps", ".stm" });
auto scripts = Platform::DirectorySearch(fromDir + "scripts", "", { ".lua", ".txt" });
auto downloadedScripts = Platform::DirectorySearch(fromDir + "scripts/downloaded", "", { ".lua" });
bool hasScriptinfo = Platform::FileExists(toDir + "scripts/downloaded/scriptinfo");
auto screenshots = Platform::DirectorySearch(fromDir, "screenshot", { ".png" });
bool hasAutorun = Platform::FileExists(fromDir + "autorun.lua");
bool hasPref = Platform::FileExists(fromDir + "powder.pref");
if (stamps.empty() && saves.empty() && scripts.empty() && downloadedScripts.empty() && screenshots.empty() && !hasAutorun && !hasPref)
{
logFile << "Nothing to migrate.";
return "Nothing to migrate. This button is used to migrate data from pre-96.0 TPT installations to the shared directory";
}
StringBuilder result;
std::stack<ByteString> dirsToDelete;
// Migrate a list of files
auto migrateList = [&](std::vector<ByteString> list, ByteString directory, String niceName) {
result << '\n' << niceName << ": ";
if (!list.empty() && !directory.empty())
Platform::MakeDirectory(toDir + directory);
int migratedCount = 0, failedCount = 0;
for (auto &item : list)
{
std::string from = fromDir + directory + "/" + item;
std::string to = toDir + directory + "/" + item;
if (!Platform::FileExists(to))
{
if (rename(from.c_str(), to.c_str()))
{
failedCount++;
logFile << "failed to move " << from << " to " << to << std::endl;
}
else
{
migratedCount++;
logFile << "moved " << from << " to " << to << std::endl;
}
}
else
{
logFile << "skipping " << from << "(already exists)" << std::endl;
}
}
dirsToDelete.push(directory);
result << "\bt" << migratedCount << " migratated\x0E, \br" << failedCount << " failed\x0E";
int duplicates = list.size() - migratedCount - failedCount;
if (duplicates)
result << ", " << list.size() - migratedCount - failedCount << " skipped (duplicate)";
};
// Migrate a single file
auto migrateFile = [&fromDir, &toDir, &result, &logFile](ByteString filename) {
ByteString from = fromDir + filename;
ByteString to = toDir + filename;
if (!Platform::FileExists(to))
{
if (rename(from.c_str(), to.c_str()))
{
logFile << "failed to move " << from << " to " << to << std::endl;
result << "\n\br" << filename.FromUtf8() << " migration failed\x0E";
}
else
{
logFile << "moved " << from << " to " << to << std::endl;
result << '\n' << filename.FromUtf8() << " migrated";
}
}
else
{
logFile << "skipping " << from << "(already exists)" << std::endl;
result << '\n' << filename.FromUtf8() << " skipped (already exists)";
}
if (!Platform::RemoveFile(fromDir + filename)) {
logFile << "failed to delete " << filename << std::endl;
}
};
// Do actual migration
Platform::RemoveFile(fromDir + "stamps/stamps.def");
migrateList(stamps, "stamps", "Stamps");
migrateList(saves, "Saves", "Saves");
if (!scripts.empty())
migrateList(scripts, "scripts", "Scripts");
if (!hasScriptinfo && !downloadedScripts.empty())
{
migrateList(downloadedScripts, "scripts/downloaded", "Downloaded scripts");
migrateFile("scripts/downloaded/scriptinfo");
}
if (!screenshots.empty())
migrateList(screenshots, "", "Screenshots");
if (hasAutorun)
migrateFile("autorun.lua");
if (hasPref)
migrateFile("powder.pref");
// Delete leftover directories
while (!dirsToDelete.empty())
{
ByteString toDelete = dirsToDelete.top();
if (!Platform::DeleteDirectory(fromDir + toDelete)) {
logFile << "failed to delete " << toDelete << std::endl;
}
dirsToDelete.pop();
}
// chdir into the new directory
chdir(toDir.c_str());
if (scripts.size())
RescanStamps();
logFile << std::endl << std::endl << "Migration complete. Results: " << result.Build().ToUtf8();
logFile.close();
return result.Build();
}

View File

@ -6,7 +6,7 @@
#include <memory> #include <memory>
#include "common/String.h" #include "common/String.h"
#include "common/Singleton.h" #include "common/ExplicitSingleton.h"
#include <json/json.h> #include <json/json.h>
#include "User.h" #include "User.h"
@ -47,7 +47,7 @@ namespace http
{ {
class Request; class Request;
} }
class Client: public Singleton<Client> { class Client: public ExplicitSingleton<Client> {
private: private:
String messageOfTheDay; String messageOfTheDay;
std::vector<std::pair<String, ByteString> > serverNotifications; std::vector<std::pair<String, ByteString> > serverNotifications;
@ -73,11 +73,6 @@ private:
void notifyMessageOfTheDay(); void notifyMessageOfTheDay();
void notifyNewNotification(std::pair<String, ByteString> notification); void notifyNewNotification(std::pair<String, ByteString> notification);
// internal preferences handling
Json::Value preferences;
Json::Value GetPref(Json::Value root, ByteString prop, Json::Value defaultValue = Json::nullValue);
Json::Value SetPrefHelper(Json::Value root, ByteString prop, Json::Value value);
// Save stealing info // Save stealing info
Json::Value authors; Json::Value authors;
@ -155,27 +150,8 @@ public:
RequestStatus ParseServerReturn(ByteString &result, int status, bool json); RequestStatus ParseServerReturn(ByteString &result, int status, bool json);
void Tick(); void Tick();
void CheckUpdate(std::unique_ptr<http::Request> &updateRequest, bool checkSession); void CheckUpdate(std::unique_ptr<http::Request> &updateRequest, bool checkSession);
void Shutdown();
// preferences functions String DoMigration(ByteString fromDir, ByteString toDir);
void WritePrefs();
ByteString GetPrefByteString(ByteString prop, ByteString defaultValue);
String GetPrefString(ByteString prop, String defaultValue);
double GetPrefNumber(ByteString prop, double defaultValue);
int GetPrefInteger(ByteString prop, int defaultValue);
unsigned int GetPrefUInteger(ByteString prop, unsigned int defaultValue);
bool GetPrefBool(ByteString prop, bool defaultValue);
std::vector<ByteString> GetPrefByteStringArray(ByteString prop);
std::vector<String> GetPrefStringArray(ByteString prop);
std::vector<double> GetPrefNumberArray(ByteString prop);
std::vector<int> GetPrefIntegerArray(ByteString prop);
std::vector<unsigned int> GetPrefUIntegerArray(ByteString prop);
std::vector<bool> GetPrefBoolArray(ByteString prop);
void SetPref(ByteString prop, Json::Value value);
void SetPref(ByteString property, std::vector<Json::Value> value);
void SetPrefUnicode(ByteString prop, String value);
}; };
bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor); bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor);

View File

@ -1,6 +1,5 @@
#include "ImageRequest.h" #include "ImageRequest.h"
#include "common/Singleton.h"
#include "graphics/Graphics.h" #include "graphics/Graphics.h"
#include "Config.h" #include "Config.h"

View File

@ -318,8 +318,9 @@ bool ReadFile(std::vector<char> &fileData, ByteString filename)
return true; return true;
} }
bool WriteFile(std::vector<char> fileData, ByteString filename) bool WriteFile(std::vector<char> fileData, ByteString filename, bool replaceAtomically)
{ {
// TODO: replaceAtomically
std::ofstream f(filename, std::ios::binary); std::ofstream f(filename, std::ios::binary);
if (f) f.write(&fileData[0], fileData.size()); if (f) f.write(&fileData[0], fileData.size());
if (!f) if (!f)
@ -381,4 +382,31 @@ ByteString ExecutableName()
#endif #endif
} }
void DoRestart()
{
ByteString exename = ExecutableName();
if (exename.length())
{
#ifdef WIN
int ret = int(INT_PTR(ShellExecuteW(NULL, NULL, WinWiden(exename).c_str(), NULL, NULL, SW_SHOWNORMAL)));
if (ret <= 32)
{
fprintf(stderr, "cannot restart: ShellExecute(...) failed: code %i\n", ret);
}
else
{
exit(0);
}
#elif defined(LIN) || defined(MACOSX)
execl(exename.c_str(), exename.c_str(), NULL);
int ret = errno;
fprintf(stderr, "cannot restart: execl(...) failed: code %i\n", ret);
#endif
}
else
{
fprintf(stderr, "cannot restart: no executable name???\n");
}
exit(-1);
}
} }

View File

@ -37,10 +37,9 @@ namespace Platform
*/ */
bool MakeDirectory(ByteString dir); bool MakeDirectory(ByteString dir);
std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search, std::vector<ByteString> extensions); std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search, std::vector<ByteString> extensions);
String DoMigration(ByteString fromDir, ByteString toDir);
bool ReadFile(std::vector<char> &fileData, ByteString filename); bool ReadFile(std::vector<char> &fileData, ByteString filename);
bool WriteFile(std::vector<char> fileData, ByteString filename); bool WriteFile(std::vector<char> fileData, ByteString filename, bool replaceAtomically = false); // TODO: Revisit call sites, remove default.
#ifdef WIN #ifdef WIN
ByteString WinNarrow(const std::wstring &source); ByteString WinNarrow(const std::wstring &source);

View File

@ -1,187 +0,0 @@
#include "Platform.h"
#include "client/Client.h"
#ifdef WIN
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <direct.h>
# include <io.h>
# include <shlobj.h>
# include <shlwapi.h>
# include <shellapi.h>
# include <windows.h>
#else
# include <unistd.h>
# include <ctime>
# include <sys/time.h>
# include <dirent.h>
#endif
#ifdef MACOSX
# include <mach-o/dyld.h>
#endif
#include <fstream>
namespace Platform
{
void DoRestart()
{
ByteString exename = ExecutableName();
if (exename.length())
{
#ifdef WIN
int ret = int(INT_PTR(ShellExecuteW(NULL, NULL, WinWiden(exename).c_str(), NULL, NULL, SW_SHOWNORMAL)));
if (ret <= 32)
{
fprintf(stderr, "cannot restart: ShellExecute(...) failed: code %i\n", ret);
}
else
{
Client::Ref().Shutdown(); // very ugly hack; will fix soon(tm)
exit(0);
}
#elif defined(LIN) || defined(MACOSX)
execl(exename.c_str(), exename.c_str(), NULL);
int ret = errno;
fprintf(stderr, "cannot restart: execl(...) failed: code %i\n", ret);
#endif
}
else
{
fprintf(stderr, "cannot restart: no executable name???\n");
}
exit(-1);
}
String DoMigration(ByteString fromDir, ByteString toDir)
{
if (fromDir.at(fromDir.length() - 1) != '/')
fromDir = fromDir + '/';
if (toDir.at(toDir.length() - 1) != '/')
toDir = toDir + '/';
std::ofstream logFile(fromDir + "/migrationlog.txt", std::ios::out);
logFile << "Running migration of data from " << fromDir + " to " << toDir << std::endl;
// Get lists of files to migrate
auto stamps = DirectorySearch(fromDir + "stamps", "", { ".stm" });
auto saves = DirectorySearch(fromDir + "Saves", "", { ".cps", ".stm" });
auto scripts = DirectorySearch(fromDir + "scripts", "", { ".lua", ".txt" });
auto downloadedScripts = DirectorySearch(fromDir + "scripts/downloaded", "", { ".lua" });
bool hasScriptinfo = FileExists(toDir + "scripts/downloaded/scriptinfo");
auto screenshots = DirectorySearch(fromDir, "screenshot", { ".png" });
bool hasAutorun = FileExists(fromDir + "autorun.lua");
bool hasPref = FileExists(fromDir + "powder.pref");
if (stamps.empty() && saves.empty() && scripts.empty() && downloadedScripts.empty() && screenshots.empty() && !hasAutorun && !hasPref)
{
logFile << "Nothing to migrate.";
return "Nothing to migrate. This button is used to migrate data from pre-96.0 TPT installations to the shared directory";
}
StringBuilder result;
std::stack<ByteString> dirsToDelete;
// Migrate a list of files
auto migrateList = [&](std::vector<ByteString> list, ByteString directory, String niceName) {
result << '\n' << niceName << ": ";
if (!list.empty() && !directory.empty())
MakeDirectory(toDir + directory);
int migratedCount = 0, failedCount = 0;
for (auto &item : list)
{
std::string from = fromDir + directory + "/" + item;
std::string to = toDir + directory + "/" + item;
if (!FileExists(to))
{
if (rename(from.c_str(), to.c_str()))
{
failedCount++;
logFile << "failed to move " << from << " to " << to << std::endl;
}
else
{
migratedCount++;
logFile << "moved " << from << " to " << to << std::endl;
}
}
else
{
logFile << "skipping " << from << "(already exists)" << std::endl;
}
}
dirsToDelete.push(directory);
result << "\bt" << migratedCount << " migratated\x0E, \br" << failedCount << " failed\x0E";
int duplicates = list.size() - migratedCount - failedCount;
if (duplicates)
result << ", " << list.size() - migratedCount - failedCount << " skipped (duplicate)";
};
// Migrate a single file
auto migrateFile = [&fromDir, &toDir, &result, &logFile](ByteString filename) {
ByteString from = fromDir + filename;
ByteString to = toDir + filename;
if (!FileExists(to))
{
if (rename(from.c_str(), to.c_str()))
{
logFile << "failed to move " << from << " to " << to << std::endl;
result << "\n\br" << filename.FromUtf8() << " migration failed\x0E";
}
else
{
logFile << "moved " << from << " to " << to << std::endl;
result << '\n' << filename.FromUtf8() << " migrated";
}
}
else
{
logFile << "skipping " << from << "(already exists)" << std::endl;
result << '\n' << filename.FromUtf8() << " skipped (already exists)";
}
if (!RemoveFile(fromDir + filename)) {
logFile << "failed to delete " << filename << std::endl;
}
};
// Do actual migration
RemoveFile(fromDir + "stamps/stamps.def");
migrateList(stamps, "stamps", "Stamps");
migrateList(saves, "Saves", "Saves");
if (!scripts.empty())
migrateList(scripts, "scripts", "Scripts");
if (!hasScriptinfo && !downloadedScripts.empty())
{
migrateList(downloadedScripts, "scripts/downloaded", "Downloaded scripts");
migrateFile("scripts/downloaded/scriptinfo");
}
if (!screenshots.empty())
migrateList(screenshots, "", "Screenshots");
if (hasAutorun)
migrateFile("autorun.lua");
if (hasPref)
migrateFile("powder.pref");
// Delete leftover directories
while (!dirsToDelete.empty())
{
ByteString toDelete = dirsToDelete.top();
if (!DeleteDirectory(fromDir + toDelete)) {
logFile << "failed to delete " << toDelete << std::endl;
}
dirsToDelete.pop();
}
// chdir into the new directory
chdir(toDir.c_str());
if (scripts.size())
Client::Ref().RescanStamps();
logFile << std::endl << std::endl << "Migration complete. Results: " << result.Build().ToUtf8();
logFile.close();
return result.Build();
}
}

View File

@ -4,7 +4,3 @@ common_files += files(
'tpt-rand.cpp', 'tpt-rand.cpp',
'tpt-thread-local.cpp', 'tpt-thread-local.cpp',
) )
powder_files += files(
'PlatformPowder.cpp',
)

View File

@ -13,9 +13,9 @@ public:
int ReturnStatus; int ReturnStatus;
String ReturnValue; String ReturnValue;
operator ByteString() const operator String() const
{ {
return Command.ToUtf8(); return Command;
} }
}; };

View File

@ -1,11 +1,9 @@
#include "ConsoleModel.h" #include "ConsoleModel.h"
#include "ConsoleView.h" #include "ConsoleView.h"
#include "prefs/GlobalPrefs.h"
#include "client/Client.h"
ConsoleModel::ConsoleModel() { ConsoleModel::ConsoleModel() {
std::vector<String> previousHistory = Client::Ref().GetPrefStringArray("Console.History"); std::vector<String> previousHistory = GlobalPrefs::Ref().Get("Console.History", std::vector<String>{});
for(std::vector<String>::reverse_iterator iter = previousHistory.rbegin(), end = previousHistory.rend(); iter != end; ++iter) for(std::vector<String>::reverse_iterator iter = previousHistory.rbegin(), end = previousHistory.rend(); iter != end; ++iter)
{ {
if(previousCommands.size()<25) if(previousCommands.size()<25)
@ -48,7 +46,7 @@ void ConsoleModel::AddLastCommand(ConsoleCommand command)
if(previousCommands.size()>25) if(previousCommands.size()>25)
previousCommands.pop_front(); previousCommands.pop_front();
currentCommandIndex = previousCommands.size(); currentCommandIndex = previousCommands.size();
Client::Ref().SetPref("Console.History", std::vector<Json::Value>(previousCommands.begin(), previousCommands.end())); GlobalPrefs::Ref().Set("Console.History", std::vector<String>(previousCommands.begin(), previousCommands.end()));
notifyPreviousCommandsChanged(); notifyPreviousCommandsChanged();
} }

View File

@ -1,7 +1,7 @@
#include "Favorite.h" #include "Favorite.h"
#include <json/json.h> #include <json/json.h>
#include "client/Client.h" #include "prefs/GlobalPrefs.h"
#include <algorithm> #include <algorithm>
@ -42,10 +42,10 @@ void Favorite::RemoveFavorite(ByteString identifier)
void Favorite::SaveFavoritesToPrefs() void Favorite::SaveFavoritesToPrefs()
{ {
Client::Ref().SetPref("Favorites", std::vector<Json::Value>(favoritesList.begin(), favoritesList.end())); GlobalPrefs::Ref().Set("Favorites", favoritesList);
} }
void Favorite::LoadFavoritesFromPrefs() void Favorite::LoadFavoritesFromPrefs()
{ {
favoritesList = Client::Ref().GetPrefByteStringArray("Favorites"); favoritesList = GlobalPrefs::Ref().Get("Favorites", std::vector<ByteString>{});
} }

View File

@ -1,5 +1,6 @@
#include "Tool.h" #include "Tool.h"
#include "prefs/GlobalPrefs.h"
#include "client/Client.h" #include "client/Client.h"
#include "common/tpt-rand.h" #include "common/tpt-rand.h"
#include "simulation/GOLString.h" #include "simulation/GOLString.h"
@ -105,8 +106,9 @@ toolSelection(toolSelection)
} }
else else
{ {
ruleField->SetText(Client::Ref().GetPrefString("CustomGOL.Rule", "B3/S23")); auto &prefs = GlobalPrefs::Ref();
nameField->SetText(Client::Ref().GetPrefString("CustomGOL.Name", "CGOL")); ruleField->SetText(prefs.Get("CustomGOL.Rule", String("B3/S23")));
nameField->SetText(prefs.Get("CustomGOL.Name", String("CGOL")));
highColour.Red = RNG::Ref().between(0x80, 0xFF); highColour.Red = RNG::Ref().between(0x80, 0xFF);
highColour.Green = RNG::Ref().between(0x80, 0xFF); highColour.Green = RNG::Ref().between(0x80, 0xFF);
highColour.Blue = RNG::Ref().between(0x80, 0xFF); highColour.Blue = RNG::Ref().between(0x80, 0xFF);
@ -152,8 +154,12 @@ void GOLWindow::Validate()
} }
ruleString = SerialiseGOLRule(rule); // * Make it canonical. ruleString = SerialiseGOLRule(rule); // * Make it canonical.
Client::Ref().SetPrefUnicode("CustomGOL.Name", nameString); {
Client::Ref().SetPrefUnicode("CustomGOL.Rule", ruleString); auto &prefs = GlobalPrefs::Ref();
Prefs::DeferWrite dw(prefs);
prefs.Set("CustomGOL.Name", nameString);
prefs.Set("CustomGOL.Rule", ruleString);
}
auto color1 = (((highColour.Red << 8) | highColour.Green) << 8) | highColour.Blue; auto color1 = (((highColour.Red << 8) | highColour.Green) << 8) | highColour.Blue;
auto color2 = (((lowColour.Red << 8) | lowColour.Green) << 8) | lowColour.Blue; auto color2 = (((lowColour.Red << 8) | lowColour.Green) << 8) | lowColour.Blue;

View File

@ -16,6 +16,7 @@
#include "GameControllerEvents.h" #include "GameControllerEvents.h"
#include "lua/CommandInterface.h" #include "lua/CommandInterface.h"
#include "prefs/GlobalPrefs.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/GameSave.h" #include "client/GameSave.h"
#include "common/Platform.h" #include "common/Platform.h"
@ -88,7 +89,7 @@ GameController::GameController():
gameView->AttachController(this); gameView->AttachController(this);
gameModel->AddObserver(gameView); gameModel->AddObserver(gameView);
gameView->SetDebugHUD(Client::Ref().GetPrefBool("Renderer.DebugMode", false)); gameView->SetDebugHUD(GlobalPrefs::Ref().Get("Renderer.DebugMode", false));
CommandInterface::Create(this, gameModel); CommandInterface::Create(this, gameModel);
@ -1352,7 +1353,6 @@ void GameController::OpenOptions()
{ {
options = new OptionsController(gameModel, [this] { options = new OptionsController(gameModel, [this] {
gameModel->UpdateQuickOptions(); gameModel->UpdateQuickOptions();
Client::Ref().WritePrefs(); // * I don't think there's a reason for this but I'm too lazy to check. -- LBPHacker
}); });
ui::Engine::Ref().ShowWindow(options->GetView()); ui::Engine::Ref().ShowWindow(options->GetView());

View File

@ -16,6 +16,7 @@
#include "QuickOptions.h" #include "QuickOptions.h"
#include "lua/CommandInterface.h" #include "lua/CommandInterface.h"
#include "prefs/GlobalPrefs.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/GameSave.h" #include "client/GameSave.h"
#include "client/SaveFile.h" #include "client/SaveFile.h"
@ -67,56 +68,51 @@ GameModel::GameModel():
std::fill(regularToolset, regularToolset+4, (Tool*)NULL); std::fill(regularToolset, regularToolset+4, (Tool*)NULL);
//Default render prefs //Default render prefs
std::vector<unsigned int> tempArray; ren->SetRenderMode({
tempArray.push_back(RENDER_FIRE); RENDER_FIRE,
tempArray.push_back(RENDER_EFFE); RENDER_EFFE,
tempArray.push_back(RENDER_BASC); RENDER_BASC,
ren->SetRenderMode(tempArray); });
tempArray.clear(); ren->SetDisplayMode({});
ren->SetDisplayMode(tempArray);
ren->SetColourMode(0); ren->SetColourMode(0);
//Load config into renderer //Load config into renderer
ren->SetColourMode(Client::Ref().GetPrefUInteger("Renderer.ColourMode", 0)); auto &prefs = GlobalPrefs::Ref();
ren->SetColourMode(prefs.Get("Renderer.ColourMode", 0U));
tempArray = Client::Ref().GetPrefUIntegerArray("Renderer.DisplayModes"); auto displayModes = prefs.Get("Renderer.DisplayModes", std::vector<unsigned int>{});
if(tempArray.size()) if (displayModes.size())
{ {
std::vector<unsigned int> displayModes(tempArray.begin(), tempArray.end());
ren->SetDisplayMode(displayModes); ren->SetDisplayMode(displayModes);
} }
auto renderModes = prefs.Get("Renderer.RenderModes", std::vector<unsigned int>{});
tempArray = Client::Ref().GetPrefUIntegerArray("Renderer.RenderModes"); if (renderModes.size())
if(tempArray.size())
{ {
std::vector<unsigned int> renderModes(tempArray.begin(), tempArray.end());
ren->SetRenderMode(renderModes); ren->SetRenderMode(renderModes);
} }
ren->gravityFieldEnabled = Client::Ref().GetPrefBool("Renderer.GravityField", false); ren->gravityFieldEnabled = prefs.Get("Renderer.GravityField", false);
ren->decorations_enable = Client::Ref().GetPrefBool("Renderer.Decorations", true); ren->decorations_enable = prefs.Get("Renderer.Decorations", true);
//Load config into simulation //Load config into simulation
edgeMode = Client::Ref().GetPrefInteger("Simulation.EdgeMode", 0); edgeMode = prefs.Get("Simulation.EdgeMode", 0); // TODO: EdgeMode enum
sim->SetEdgeMode(edgeMode); sim->SetEdgeMode(edgeMode);
ambientAirTemp = float(R_TEMP) + 273.15f; ambientAirTemp = float(R_TEMP) + 273.15f;
{ {
auto temp = Client::Ref().GetPrefNumber("Simulation.AmbientAirTemp", ambientAirTemp); auto temp = prefs.Get("Simulation.AmbientAirTemp", ambientAirTemp);
if (MIN_TEMP <= temp && MAX_TEMP >= temp) if (MIN_TEMP <= temp && MAX_TEMP >= temp)
{ {
ambientAirTemp = temp; ambientAirTemp = temp;
} }
} }
sim->air->ambientAirTemp = ambientAirTemp; sim->air->ambientAirTemp = ambientAirTemp;
decoSpace = Client::Ref().GetPrefInteger("Simulation.DecoSpace", 0); decoSpace = prefs.Get("Simulation.DecoSpace", 0); // TODO: DecoSpace enum
sim->SetDecoSpace(decoSpace); sim->SetDecoSpace(decoSpace);
int ngrav_enable = Client::Ref().GetPrefInteger("Simulation.NewtonianGravity", 0); int ngrav_enable = prefs.Get("Simulation.NewtonianGravity", 0); // TODO: NewtonianGravity enum
if (ngrav_enable) if (ngrav_enable)
sim->grav->start_grav_async(); sim->grav->start_grav_async();
sim->aheat_enable = Client::Ref().GetPrefInteger("Simulation.AmbientHeat", 0); sim->aheat_enable = prefs.Get("Simulation.AmbientHeat", 0); // TODO: AmbientHeat enum
sim->pretty_powder = Client::Ref().GetPrefInteger("Simulation.PrettyPowder", 0); sim->pretty_powder = prefs.Get("Simulation.PrettyPowder", 0); // TODO: PrettyPowder enum
Favorite::Ref().LoadFavoritesFromPrefs(); Favorite::Ref().LoadFavoritesFromPrefs();
@ -128,14 +124,14 @@ GameModel::GameModel():
BuildMenus(); BuildMenus();
perfectCircle = Client::Ref().GetPrefBool("PerfectCircleBrush", true); perfectCircle = prefs.Get("PerfectCircleBrush", true);
BuildBrushList(); BuildBrushList();
//Set default decoration colour //Set default decoration colour
unsigned char colourR = std::min(Client::Ref().GetPrefInteger("Decoration.Red", 200), 255); unsigned char colourR = std::max(std::min(prefs.Get("Decoration.Red", 200), 255), 0);
unsigned char colourG = std::min(Client::Ref().GetPrefInteger("Decoration.Green", 100), 255); unsigned char colourG = std::max(std::min(prefs.Get("Decoration.Green", 100), 255), 0);
unsigned char colourB = std::min(Client::Ref().GetPrefInteger("Decoration.Blue", 50), 255); unsigned char colourB = std::max(std::min(prefs.Get("Decoration.Blue", 50), 255), 0);
unsigned char colourA = std::min(Client::Ref().GetPrefInteger("Decoration.Alpha", 255), 255); unsigned char colourA = std::max(std::min(prefs.Get("Decoration.Alpha", 255), 255), 0);
SetColourSelectorColour(ui::Colour(colourR, colourG, colourB, colourA)); SetColourSelectorColour(ui::Colour(colourR, colourG, colourB, colourA));
@ -148,41 +144,38 @@ GameModel::GameModel():
colourPresets.push_back(ui::Colour(0, 0, 255)); colourPresets.push_back(ui::Colour(0, 0, 255));
colourPresets.push_back(ui::Colour(0, 0, 0)); colourPresets.push_back(ui::Colour(0, 0, 0));
undoHistoryLimit = Client::Ref().GetPrefInteger("Simulation.UndoHistoryLimit", 5); undoHistoryLimit = prefs.Get("Simulation.UndoHistoryLimit", 5U);
// cap due to memory usage (this is about 3.4GB of RAM) // cap due to memory usage (this is about 3.4GB of RAM)
if (undoHistoryLimit > 200) if (undoHistoryLimit > 200)
SetUndoHistoryLimit(200); SetUndoHistoryLimit(200);
mouseClickRequired = Client::Ref().GetPrefBool("MouseClickRequired", false); mouseClickRequired = prefs.Get("MouseClickRequired", false);
includePressure = Client::Ref().GetPrefBool("Simulation.IncludePressure", true); includePressure = prefs.Get("Simulation.IncludePressure", true);
temperatureScale = Client::Ref().GetPrefInteger("Renderer.TemperatureScale", 1); temperatureScale = prefs.Get("Renderer.TemperatureScale", 1); // TODO: TemperatureScale enum
ClearSimulation(); ClearSimulation();
} }
GameModel::~GameModel() GameModel::~GameModel()
{ {
//Save to config: auto &prefs = GlobalPrefs::Ref();
Client::Ref().SetPref("Renderer.ColourMode", ren->GetColourMode()); {
//Save to config:
std::vector<unsigned int> displayModes = ren->GetDisplayMode(); Prefs::DeferWrite dw(prefs);
Client::Ref().SetPref("Renderer.DisplayModes", std::vector<Json::Value>(displayModes.begin(), displayModes.end())); prefs.Set("Renderer.ColourMode", ren->GetColourMode());
prefs.Set("Renderer.DisplayModes", ren->GetDisplayMode());
std::vector<unsigned int> renderModes = ren->GetRenderMode(); prefs.Set("Renderer.RenderModes", ren->GetRenderMode());
Client::Ref().SetPref("Renderer.RenderModes", std::vector<Json::Value>(renderModes.begin(), renderModes.end())); prefs.Set("Renderer.GravityField", (bool)ren->gravityFieldEnabled);
prefs.Set("Renderer.Decorations", (bool)ren->decorations_enable);
Client::Ref().SetPref("Renderer.GravityField", (bool)ren->gravityFieldEnabled); prefs.Set("Renderer.DebugMode", ren->debugLines); //These two should always be equivalent, even though they are different things
Client::Ref().SetPref("Renderer.Decorations", (bool)ren->decorations_enable); prefs.Set("Simulation.NewtonianGravity", sim->grav->IsEnabled());
Client::Ref().SetPref("Renderer.DebugMode", ren->debugLines); //These two should always be equivalent, even though they are different things prefs.Set("Simulation.AmbientHeat", sim->aheat_enable);
prefs.Set("Simulation.PrettyPowder", sim->pretty_powder);
Client::Ref().SetPref("Simulation.NewtonianGravity", sim->grav->IsEnabled()); prefs.Set("Decoration.Red", (int)colour.Red);
Client::Ref().SetPref("Simulation.AmbientHeat", sim->aheat_enable); prefs.Set("Decoration.Green", (int)colour.Green);
Client::Ref().SetPref("Simulation.PrettyPowder", sim->pretty_powder); prefs.Set("Decoration.Blue", (int)colour.Blue);
prefs.Set("Decoration.Alpha", (int)colour.Alpha);
Client::Ref().SetPref("Decoration.Red", (int)colour.Red); }
Client::Ref().SetPref("Decoration.Green", (int)colour.Green);
Client::Ref().SetPref("Decoration.Blue", (int)colour.Blue);
Client::Ref().SetPref("Decoration.Alpha", (int)colour.Alpha);
for (size_t i = 0; i < menuList.size(); i++) for (size_t i = 0; i < menuList.size(); i++)
{ {
@ -317,8 +310,9 @@ void GameModel::BuildMenus()
menuList[SC_LIFE]->AddTool(tempTool); menuList[SC_LIFE]->AddTool(tempTool);
} }
{ {
auto customGOLTypes = Client::Ref().GetPrefByteStringArray("CustomGOL.Types"); auto &prefs = GlobalPrefs::Ref();
Json::Value validatedCustomLifeTypes(Json::arrayValue); auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector<ByteString>{});
std::vector<ByteString> validatedCustomLifeTypes;
std::vector<Simulation::CustomGOLData> newCustomGol; std::vector<Simulation::CustomGOLData> newCustomGol;
for (auto gol : customGOLTypes) for (auto gol : customGOLTypes)
{ {
@ -351,10 +345,10 @@ void GameModel::BuildMenus()
continue; continue;
} }
newCustomGol.push_back(gd); newCustomGol.push_back(gd);
validatedCustomLifeTypes.append(gol); validatedCustomLifeTypes.push_back(gol);
} }
// All custom rules that fail validation will be removed // All custom rules that fail validation will be removed
Client::Ref().SetPref("CustomGOL.Types", validatedCustomLifeTypes); prefs.Set("CustomGOL.Types", validatedCustomLifeTypes);
for (auto &gd : newCustomGol) for (auto &gd : newCustomGol)
{ {
Tool * tempTool = new ElementTool(PT_LIFE|PMAPID(gd.rule), gd.nameString, "Custom GOL type: " + gd.ruleString, PIXR(gd.colour1), PIXG(gd.colour1), PIXB(gd.colour1), "DEFAULT_PT_LIFECUST_"+gd.nameString.ToAscii(), NULL); Tool * tempTool = new ElementTool(PT_LIFE|PMAPID(gd.rule), gd.nameString, "Custom GOL type: " + gd.ruleString, PIXR(gd.colour1), PIXG(gd.colour1), PIXB(gd.colour1), "DEFAULT_PT_LIFECUST_"+gd.nameString.ToAscii(), NULL);
@ -795,7 +789,7 @@ unsigned int GameModel::GetUndoHistoryLimit()
void GameModel::SetUndoHistoryLimit(unsigned int undoHistoryLimit_) void GameModel::SetUndoHistoryLimit(unsigned int undoHistoryLimit_)
{ {
undoHistoryLimit = undoHistoryLimit_; undoHistoryLimit = undoHistoryLimit_;
Client::Ref().SetPref("Simulation.UndoHistoryLimit", undoHistoryLimit); GlobalPrefs::Ref().Set("Simulation.UndoHistoryLimit", undoHistoryLimit);
} }
void GameModel::SetVote(int direction) void GameModel::SetVote(int direction)
@ -1653,17 +1647,18 @@ void GameModel::SetPerfectCircle(bool perfectCircle)
bool GameModel::RemoveCustomGOLType(const ByteString &identifier) bool GameModel::RemoveCustomGOLType(const ByteString &identifier)
{ {
bool removedAny = false; bool removedAny = false;
auto customGOLTypes = Client::Ref().GetPrefByteStringArray("CustomGOL.Types"); auto &prefs = GlobalPrefs::Ref();
Json::Value newCustomGOLTypes(Json::arrayValue); auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector<ByteString>{});
std::vector<ByteString> newCustomGOLTypes;
for (auto gol : customGOLTypes) for (auto gol : customGOLTypes)
{ {
auto parts = gol.PartitionBy(' '); auto parts = gol.PartitionBy(' ');
if (parts.size() && "DEFAULT_PT_LIFECUST_" + parts[0] == identifier) if (parts.size() && "DEFAULT_PT_LIFECUST_" + parts[0] == identifier)
removedAny = true; removedAny = true;
else else
newCustomGOLTypes.append(gol); newCustomGOLTypes.push_back(gol);
} }
Client::Ref().SetPref("CustomGOL.Types", newCustomGOLTypes); prefs.Set("CustomGOL.Types", newCustomGOLTypes);
BuildMenus(); BuildMenus();
return removedAny; return removedAny;
} }

View File

@ -1,6 +1,6 @@
#include "Tool.h" #include "Tool.h"
#include "client/Client.h" #include "prefs/GlobalPrefs.h"
#include "Menu.h" #include "Menu.h"
#include "Format.h" #include "Format.h"
@ -75,12 +75,14 @@ sim(sim_)
{ {
property->AddOption(std::pair<String, int>(properties[i].Name.FromAscii(), i)); property->AddOption(std::pair<String, int>(properties[i].Name.FromAscii(), i));
} }
property->SetOption(Client::Ref().GetPrefInteger("Prop.Type", 0));
auto &prefs = GlobalPrefs::Ref();
property->SetOption(prefs.Get("Prop.Type", 0));
textField = new ui::Textbox(ui::Point(8, 46), ui::Point(Size.X-16, 16), "", "[value]"); textField = new ui::Textbox(ui::Point(8, 46), ui::Point(Size.X-16, 16), "", "[value]");
textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
textField->SetText(Client::Ref().GetPrefString("Prop.Value", "")); textField->SetText(prefs.Get("Prop.Value", String("")));
AddComponent(textField); AddComponent(textField);
FocusComponent(textField); FocusComponent(textField);
SetProperty(false); SetProperty(false);
@ -216,8 +218,12 @@ void PropertyWindow::SetProperty(bool warn)
new ErrorMessage("Could not set property", "Invalid value provided"); new ErrorMessage("Could not set property", "Invalid value provided");
return; return;
} }
Client::Ref().SetPref("Prop.Type", property->GetOption().second); {
Client::Ref().SetPrefUnicode("Prop.Value", textField->GetText()); auto &prefs = GlobalPrefs::Ref();
Prefs::DeferWrite dw(prefs);
prefs.Set("Prop.Type", property->GetOption().second);
prefs.Set("Prop.Value", textField->GetText());
}
} }
} }

View File

@ -6,7 +6,7 @@
#include "simulation/Air.h" #include "simulation/Air.h"
#include "simulation/Gravity.h" #include "simulation/Gravity.h"
#include "client/Client.h" #include "prefs/GlobalPrefs.h"
#include "gui/interface/Engine.h" #include "gui/interface/Engine.h"
#include "gui/game/GameModel.h" #include "gui/game/GameModel.h"
@ -85,7 +85,7 @@ int OptionsModel::GetEdgeMode()
} }
void OptionsModel::SetEdgeMode(int edgeMode) void OptionsModel::SetEdgeMode(int edgeMode)
{ {
Client::Ref().SetPref("Simulation.EdgeMode", edgeMode); GlobalPrefs::Ref().Set("Simulation.EdgeMode", edgeMode);
gModel->SetEdgeMode(edgeMode); gModel->SetEdgeMode(edgeMode);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -96,7 +96,7 @@ int OptionsModel::GetTemperatureScale()
} }
void OptionsModel::SetTemperatureScale(int temperatureScale) void OptionsModel::SetTemperatureScale(int temperatureScale)
{ {
Client::Ref().SetPref("Renderer.TemperatureScale", temperatureScale); GlobalPrefs::Ref().Set("Renderer.TemperatureScale", temperatureScale);
gModel->SetTemperatureScale(temperatureScale); gModel->SetTemperatureScale(temperatureScale);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -107,7 +107,7 @@ float OptionsModel::GetAmbientAirTemperature()
} }
void OptionsModel::SetAmbientAirTemperature(float ambientAirTemp) void OptionsModel::SetAmbientAirTemperature(float ambientAirTemp)
{ {
Client::Ref().SetPref("Simulation.AmbientAirTemp", ambientAirTemp); GlobalPrefs::Ref().Set("Simulation.AmbientAirTemp", ambientAirTemp);
gModel->SetAmbientAirTemperature(ambientAirTemp); gModel->SetAmbientAirTemperature(ambientAirTemp);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -152,7 +152,7 @@ int OptionsModel::GetScale()
void OptionsModel::SetScale(int scale) void OptionsModel::SetScale(int scale)
{ {
ui::Engine::Ref().SetScale(scale); ui::Engine::Ref().SetScale(scale);
Client::Ref().SetPref("Scale", int(scale)); GlobalPrefs::Ref().Set("Scale", int(scale));
notifySettingsChanged(); notifySettingsChanged();
} }
@ -164,7 +164,7 @@ bool OptionsModel::GetResizable()
void OptionsModel::SetResizable(bool resizable) void OptionsModel::SetResizable(bool resizable)
{ {
ui::Engine::Ref().SetResizable(resizable); ui::Engine::Ref().SetResizable(resizable);
Client::Ref().SetPref("Resizable", resizable); GlobalPrefs::Ref().Set("Resizable", resizable);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -175,7 +175,7 @@ bool OptionsModel::GetFullscreen()
void OptionsModel::SetFullscreen(bool fullscreen) void OptionsModel::SetFullscreen(bool fullscreen)
{ {
ui::Engine::Ref().SetFullscreen(fullscreen); ui::Engine::Ref().SetFullscreen(fullscreen);
Client::Ref().SetPref("Fullscreen", fullscreen); GlobalPrefs::Ref().Set("Fullscreen", fullscreen);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -187,7 +187,7 @@ bool OptionsModel::GetAltFullscreen()
void OptionsModel::SetAltFullscreen(bool altFullscreen) void OptionsModel::SetAltFullscreen(bool altFullscreen)
{ {
ui::Engine::Ref().SetAltFullscreen(altFullscreen); ui::Engine::Ref().SetAltFullscreen(altFullscreen);
Client::Ref().SetPref("AltFullscreen", altFullscreen); GlobalPrefs::Ref().Set("AltFullscreen", altFullscreen);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -199,7 +199,7 @@ bool OptionsModel::GetForceIntegerScaling()
void OptionsModel::SetForceIntegerScaling(bool forceIntegerScaling) void OptionsModel::SetForceIntegerScaling(bool forceIntegerScaling)
{ {
ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling); ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling);
Client::Ref().SetPref("ForceIntegerScaling", forceIntegerScaling); GlobalPrefs::Ref().Set("ForceIntegerScaling", forceIntegerScaling);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -210,7 +210,7 @@ bool OptionsModel::GetFastQuit()
void OptionsModel::SetFastQuit(bool fastquit) void OptionsModel::SetFastQuit(bool fastquit)
{ {
ui::Engine::Ref().SetFastQuit(fastquit); ui::Engine::Ref().SetFastQuit(fastquit);
Client::Ref().SetPref("FastQuit", bool(fastquit)); GlobalPrefs::Ref().Set("FastQuit", bool(fastquit));
notifySettingsChanged(); notifySettingsChanged();
} }
@ -220,7 +220,7 @@ int OptionsModel::GetDecoSpace()
} }
void OptionsModel::SetDecoSpace(int decoSpace) void OptionsModel::SetDecoSpace(int decoSpace)
{ {
Client::Ref().SetPref("Simulation.DecoSpace", decoSpace); GlobalPrefs::Ref().Set("Simulation.DecoSpace", decoSpace);
gModel->SetDecoSpace(decoSpace); gModel->SetDecoSpace(decoSpace);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -233,7 +233,7 @@ bool OptionsModel::GetShowAvatars()
void OptionsModel::SetShowAvatars(bool state) void OptionsModel::SetShowAvatars(bool state)
{ {
ui::Engine::Ref().ShowAvatars = state; ui::Engine::Ref().ShowAvatars = state;
Client::Ref().SetPref("ShowAvatars", state); GlobalPrefs::Ref().Set("ShowAvatars", state);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -244,7 +244,7 @@ bool OptionsModel::GetMouseClickRequired()
void OptionsModel::SetMouseClickRequired(bool mouseClickRequired) void OptionsModel::SetMouseClickRequired(bool mouseClickRequired)
{ {
Client::Ref().SetPref("MouseClickRequired", mouseClickRequired); GlobalPrefs::Ref().Set("MouseClickRequired", mouseClickRequired);
gModel->SetMouseClickRequired(mouseClickRequired); gModel->SetMouseClickRequired(mouseClickRequired);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -256,7 +256,7 @@ bool OptionsModel::GetIncludePressure()
void OptionsModel::SetIncludePressure(bool includePressure) void OptionsModel::SetIncludePressure(bool includePressure)
{ {
Client::Ref().SetPref("Simulation.IncludePressure", includePressure); GlobalPrefs::Ref().Set("Simulation.IncludePressure", includePressure);
gModel->SetIncludePressure(includePressure); gModel->SetIncludePressure(includePressure);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -268,7 +268,7 @@ bool OptionsModel::GetPerfectCircle()
void OptionsModel::SetPerfectCircle(bool perfectCircle) void OptionsModel::SetPerfectCircle(bool perfectCircle)
{ {
Client::Ref().SetPref("PerfectCircleBrush", perfectCircle); GlobalPrefs::Ref().Set("PerfectCircleBrush", perfectCircle);
gModel->SetPerfectCircle(perfectCircle); gModel->SetPerfectCircle(perfectCircle);
notifySettingsChanged(); notifySettingsChanged();
} }
@ -280,7 +280,7 @@ bool OptionsModel::GetMomentumScroll()
void OptionsModel::SetMomentumScroll(bool state) void OptionsModel::SetMomentumScroll(bool state)
{ {
Client::Ref().SetPref("MomentumScroll", state); GlobalPrefs::Ref().Set("MomentumScroll", state);
ui::Engine::Ref().MomentumScroll = state; ui::Engine::Ref().MomentumScroll = state;
notifySettingsChanged(); notifySettingsChanged();
} }

View File

@ -14,6 +14,7 @@
#include "graphics/Renderer.h" #include "graphics/Renderer.h"
#include "gui/Style.h" #include "gui/Style.h"
#include "simulation/ElementDefs.h" #include "simulation/ElementDefs.h"
#include "client/Client.h"
#include "gui/dialogues/ConfirmPrompt.h" #include "gui/dialogues/ConfirmPrompt.h"
#include "gui/dialogues/InformationMessage.h" #include "gui/dialogues/InformationMessage.h"
@ -420,7 +421,7 @@ OptionsView::OptionsView():
ByteString to = Platform::sharedCwd; ByteString to = Platform::sharedCwd;
new ConfirmPrompt("Do Migration?", "This will migrate all stamps, saves, and scripts from\n\bt" + from.FromUtf8() + "\bw\nto the shared data directory at\n\bt" + to.FromUtf8() + "\bw\n\n" + new ConfirmPrompt("Do Migration?", "This will migrate all stamps, saves, and scripts from\n\bt" + from.FromUtf8() + "\bw\nto the shared data directory at\n\bt" + to.FromUtf8() + "\bw\n\n" +
"Files that already exist will not be overwritten.", { [=] () { "Files that already exist will not be overwritten.", { [=] () {
String ret = Platform::DoMigration(from, to); String ret = Client::Ref().DoMigration(from, to);
new InformationMessage("Migration Complete", ret, false); new InformationMessage("Migration Complete", ret, false);
} }); } });
} }); } });

View File

@ -8,6 +8,7 @@
#include "Config.h" #include "Config.h"
#include "Update.h" #include "Update.h"
#include "prefs/GlobalPrefs.h"
#include "client/Client.h" #include "client/Client.h"
#include "common/Platform.h" #include "common/Platform.h"
#include "tasks/Task.h" #include "tasks/Task.h"
@ -32,7 +33,13 @@ private:
} }
bool doWork() override bool doWork() override
{ {
String error; auto &prefs = GlobalPrefs::Ref();
auto niceNotifyError = [this](String error) {
notifyError("Downloaded update is corrupted\n" + error);
return false;
};
auto request = std::make_unique<http::Request>(updateName); auto request = std::make_unique<http::Request>(updateName);
request->Start(); request->Start();
notifyStatus("Downloading update"); notifyStatus("Downloading update");
@ -48,15 +55,11 @@ private:
auto [ status, data ] = request->Finish(); auto [ status, data ] = request->Finish();
if (status!=200) if (status!=200)
{ {
error = String::Build("Server responded with Status ", status); return niceNotifyError("Could not download update: " + String::Build("Server responded with Status ", status));
notifyError("Could not download update: " + error);
return false;
} }
if (!data.size()) if (!data.size())
{ {
error = "Server responded with nothing"; return niceNotifyError("Server did not return any data");
notifyError("Server did not return any data");
return false;
} }
notifyStatus("Unpacking update"); notifyStatus("Unpacking update");
@ -66,13 +69,11 @@ private:
if(data.size()<16) if(data.size()<16)
{ {
error = String::Build("Unsufficient data, got ", data.size(), " bytes"); return niceNotifyError(String::Build("Unsufficient data, got ", data.size(), " bytes"));
goto corrupt;
} }
if (data[0]!=0x42 || data[1]!=0x75 || data[2]!=0x54 || data[3]!=0x54) if (data[0]!=0x42 || data[1]!=0x75 || data[2]!=0x54 || data[3]!=0x54)
{ {
error = "Invalid update format"; return niceNotifyError("Invalid update format");
goto corrupt;
} }
uncompressedLength = (unsigned char)data[4]; uncompressedLength = (unsigned char)data[4];
@ -80,40 +81,28 @@ private:
uncompressedLength |= ((unsigned char)data[6])<<16; uncompressedLength |= ((unsigned char)data[6])<<16;
uncompressedLength |= ((unsigned char)data[7])<<24; uncompressedLength |= ((unsigned char)data[7])<<24;
char * res; std::vector<char> res(uncompressedLength);
res = (char *)malloc(uncompressedLength);
if (!res)
{
error = String::Build("Unable to allocate ", uncompressedLength, " bytes of memory for decompression");
goto corrupt;
}
int dstate; int dstate;
dstate = BZ2_bzBuffToBuffDecompress((char *)res, (unsigned *)&uncompressedLength, &data[8], data.size()-8, 0, 0); dstate = BZ2_bzBuffToBuffDecompress(&res[0], (unsigned *)&uncompressedLength, &data[8], data.size()-8, 0, 0);
if (dstate) if (dstate)
{ {
error = String::Build("Unable to decompress update: ", dstate); return niceNotifyError(String::Build("Unable to decompress update: ", dstate));
free(res);
goto corrupt;
} }
notifyStatus("Applying update"); notifyStatus("Applying update");
notifyProgress(-1); notifyProgress(-1);
Client::Ref().SetPref("version.update", true); prefs.Set("version.update", true);
if (update_start(res, uncompressedLength)) if (update_start(&res[0], uncompressedLength))
{ {
Client::Ref().SetPref("version.update", false); prefs.Set("version.update", false);
update_cleanup(); update_cleanup();
notifyError("Update failed - try downloading a new version."); notifyError("Update failed - try downloading a new version.");
return false; return false;
} }
return true; return true;
corrupt:
notifyError("Downloaded update is corrupted\n" + error);
return false;
} }
}; };

View File

@ -12,7 +12,7 @@
#include "LuaSmartRef.h" #include "LuaSmartRef.h"
#include "PowderToy.h" #include "PowderToy.h"
#include "client/Client.h" #include "prefs/GlobalPrefs.h"
#include "common/Platform.h" #include "common/Platform.h"
#include "graphics/Graphics.h" #include "graphics/Graphics.h"
#include "graphics/Renderer.h" #include "graphics/Renderer.h"
@ -1321,9 +1321,20 @@ int luatpt_setwindowsize(lua_State* l)
int kiosk = luaL_optint(l,2,0); int kiosk = luaL_optint(l,2,0);
// TODO: handle this the same way as it's handled in PowderToySDL.cpp // TODO: handle this the same way as it's handled in PowderToySDL.cpp
// > maybe bind the maximum allowed scale to screen size somehow // > maybe bind the maximum allowed scale to screen size somehow
if (scale < 1 || scale > 10) scale = 1; if (scale < 1 || scale > 10)
if (kiosk!=1) kiosk = 0; {
Client::Ref().SetPref("Scale", scale); scale = 1;
}
if (kiosk!=1)
{
kiosk = 0;
}
{
auto &prefs = GlobalPrefs::Ref();
Prefs::DeferWrite dw(prefs);
prefs.Set("Scale", scale);
prefs.Set("Fullscreen", bool(kiosk));
}
ui::Engine::Ref().SetScale(scale); ui::Engine::Ref().SetScale(scale);
ui::Engine::Ref().SetFullscreen(kiosk); ui::Engine::Ref().SetFullscreen(kiosk);
return 0; return 0;

View File

@ -44,6 +44,7 @@ else
'lua/PlainCommandInterface.cpp', 'lua/PlainCommandInterface.cpp',
) )
endif endif
subdir('prefs')
subdir('resampler') subdir('resampler')
subdir('simulation') subdir('simulation')
subdir('tasks') subdir('tasks')

11
src/prefs/GlobalPrefs.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "Prefs.h"
#include "common/ExplicitSingleton.h"
class GlobalPrefs : public Prefs, public ExplicitSingleton<GlobalPrefs>
{
public:
GlobalPrefs() : Prefs("powder.pref")
{
}
};

153
src/prefs/Prefs.cpp Normal file
View File

@ -0,0 +1,153 @@
#include "Prefs.h"
#include "common/Platform.h"
#include "common/tpt-rand.h"
#include <fstream>
#include <iostream>
Prefs::Prefs(ByteString newPath) : path(newPath)
{
Read();
}
void Prefs::Read()
{
std::vector<char> data;
if (!Platform::ReadFile(data, path))
{
return;
}
Json::CharReaderBuilder rbuilder;
std::unique_ptr<Json::CharReader> const reader(rbuilder.newCharReader());
ByteString errs;
if (!reader->parse(&data[0], &data[0] + data.size(), &root, &errs))
{
std::cerr << errs << std::endl;
return;
}
backedByFile = true;
}
void Prefs::ShouldWrite()
{
shouldWrite = true;
Write();
}
void Prefs::Write()
{
if (deferWriteLevel)
{
return;
}
if (!shouldWrite)
{
return;
}
shouldWrite = false;
Json::StreamWriterBuilder wbuilder;
wbuilder["indentation"] = "\t";
ByteString data = Json::writeString(wbuilder, root);
if (!Platform::WriteFile(std::vector<char>(data.begin(), data.end()), path, true))
{
return;
}
backedByFile = true;
}
void Prefs::GrabDeferWriteLevel(DeferWriteTag)
{
deferWriteLevel += 1;
}
void Prefs::DropDeferWriteLevel(DeferWriteTag)
{
deferWriteLevel -= 1;
Write();
}
Json::Value Prefs::GetJson(const Json::Value &node, ByteString path)
{
if (node.type() != Json::objectValue)
{
return Json::nullValue;
}
auto split = path.SplitBy('.');
if (!split)
{
return node[path];
}
return GetJson(node[split.Before()], split.After());
}
void Prefs::SetJson(Json::Value &node, ByteString path, Json::Value value)
{
if (node.type() != Json::objectValue)
{
node = Json::objectValue;
}
auto split = path.SplitBy('.');
if (!split)
{
node[path] = value;
return;
}
SetJson(node[split.Before()], split.After(), value);
}
template<> Json::Value Prefs::Bipacker<int>::Pack (const int &value) { return Json::Value(value); }
template<> int Prefs::Bipacker<int>::Unpack(const Json::Value &value) { return value.asInt(); }
template<> Json::Value Prefs::Bipacker<unsigned int>::Pack (const unsigned int &value) { return Json::Value(value); }
template<> unsigned int Prefs::Bipacker<unsigned int>::Unpack(const Json::Value &value) { return value.asUInt(); }
template<> Json::Value Prefs::Bipacker<uint64_t>::Pack (const uint64_t &value) { return Json::Value(Json::UInt64(value)); }
template<> uint64_t Prefs::Bipacker<uint64_t>::Unpack(const Json::Value &value) { return value.asUInt64(); }
template<> Json::Value Prefs::Bipacker<float>::Pack (const float &value) { return Json::Value(value); }
template<> float Prefs::Bipacker<float>::Unpack(const Json::Value &value) { return value.asFloat(); }
template<> Json::Value Prefs::Bipacker<bool>::Pack (const bool &value) { return Json::Value(value); }
template<> bool Prefs::Bipacker<bool>::Unpack(const Json::Value &value) { return value.asBool(); }
template<> Json::Value Prefs::Bipacker<ByteString>::Pack (const ByteString &value) { return Json::Value(value); }
template<> ByteString Prefs::Bipacker<ByteString>::Unpack(const Json::Value &value) { return value.asString(); }
template<> Json::Value Prefs::Bipacker<String>::Pack (const String &value) { return Json::Value(value.ToUtf8()); }
template<> String Prefs::Bipacker<String>::Unpack(const Json::Value &value) { return ByteString(value.asString()).FromUtf8(); }
template<class Item>
struct Prefs::Bipacker<std::vector<Item>>
{
static Json::Value Pack(const std::vector<Item> &value);
static std::vector<Item> Unpack(const Json::Value &value);
};
template<class Item>
Json::Value Prefs::Bipacker<std::vector<Item>>::Pack(const std::vector<Item> &value)
{
Json::Value array = Json::arrayValue;
for (auto item : value)
{
array.append(Bipacker<Item>::Pack(item));
}
return array;
}
template<class Item>
std::vector<Item> Prefs::Bipacker<std::vector<Item>>::Unpack(const Json::Value &value)
{
std::vector<Item> array;
if (value.type() != Json::arrayValue)
{
throw std::exception();
}
for (auto &item : value)
{
array.push_back(Bipacker<Item>::Unpack(item));
}
return array;
}
template struct Prefs::Bipacker<std::vector<String>>;
template struct Prefs::Bipacker<std::vector<ByteString>>;
template struct Prefs::Bipacker<std::vector<unsigned int>>;

104
src/prefs/Prefs.h Normal file
View File

@ -0,0 +1,104 @@
#pragma once
#include "Config.h"
#include "common/String.h"
#include <json/json.h>
class Prefs
{
struct DeferWriteTag
{
};
Json::Value root;
static Json::Value GetJson(const Json::Value &node, ByteString path);
static void SetJson(Json::Value &node, ByteString path, Json::Value value);
template<class Type>
struct Bipacker
{
static Json::Value Pack(const Type &value);
static Type Unpack(const Json::Value &value);
};
void Read();
void Write();
void ShouldWrite();
unsigned int deferWriteLevel = 0;
bool backedByFile = false;
bool shouldWrite = false;
ByteString path;
Prefs(const Prefs &) = delete;
Prefs &operator =(const Prefs &) = delete;
public:
Prefs(ByteString path);
template<class Type>
Type Get(ByteString path, Type defaultValue) const
{
auto value = GetJson(root, path);
if (value != Json::nullValue)
{
try
{
return Bipacker<Type>::Unpack(value);
}
catch (const std::exception &e)
{
}
}
return defaultValue;
}
template<class Enum, class EnumBase = int>
Enum Get(ByteString path, Enum maxValue, Enum defaultValue) const
{
EnumBase value = Get(path, EnumBase(defaultValue));
if (value < 0 || value >= EnumBase(maxValue))
{
value = EnumBase(defaultValue);
}
return Enum(value);
}
template<class Type>
void Set(ByteString path, Type value)
{
SetJson(root, path, Bipacker<Type>::Pack(value));
ShouldWrite();
}
void Clear(ByteString path)
{
SetJson(root, path, Json::nullValue);
ShouldWrite();
}
void GrabDeferWriteLevel(DeferWriteTag);
void DropDeferWriteLevel(DeferWriteTag);
struct DeferWrite
{
Prefs &prefs;
DeferWrite(const DeferWrite &) = delete;
DeferWrite &operator =(const DeferWrite &) = delete;
DeferWrite(Prefs &newPrefs) : prefs(newPrefs)
{
prefs.GrabDeferWriteLevel({});
}
~DeferWrite()
{
prefs.DropDeferWriteLevel({});
}
};
bool BackedByFile() const
{
return backedByFile;
}
};

3
src/prefs/meson.build Normal file
View File

@ -0,0 +1,3 @@
powder_files += files(
'Prefs.cpp',
)

View File

@ -1,5 +1,4 @@
#include "Simulation.h" #include "Simulation.h"
#include "client/Client.h"
#include "ElementClasses.h" #include "ElementClasses.h"
#include "graphics/Renderer.h" #include "graphics/Renderer.h"
#include "gui/game/Brush.h" #include "gui/game/Brush.h"

View File

@ -1,5 +1,4 @@
#include "GOLString.h" #include "GOLString.h"
#include "client/Client.h"
int ParseGOLString(const String &value) int ParseGOLString(const String &value)
{ {