From 486bf2719b49820d3bb1c34c6bb0e47dc82c4e62 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 25 Oct 2020 02:37:22 -0700 Subject: [PATCH] persist global buildingplan settings this ensures that a player's preferences are saved across map reloads. this is particularly important for the building material filters, since item fulfillment tasks are regenerated on map load, and any changes in settings when buildingplan is reset will change the item vectors that will be searched for planned buildings. if the settings were allowed to reset, then a player who thought all walls would be made out of blocks would be surprised to find boulders and logs being used after the map is reloaded. --- plugins/buildingplan-planner.cpp | 63 +++++++++++++++++++++++++++++++- plugins/buildingplan-planner.h | 1 + plugins/buildingplan.cpp | 36 +++++++++++++++++- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/plugins/buildingplan-planner.cpp b/plugins/buildingplan-planner.cpp index 42c84f4dc..963d13153 100644 --- a/plugins/buildingplan-planner.cpp +++ b/plugins/buildingplan-planner.cpp @@ -20,6 +20,7 @@ static const std::string planned_building_persistence_key_v1 = "buildingplan/constraints"; static const std::string planned_building_persistence_key_v2 = "buildingplan/constraints2"; +static const std::string global_settings_persistence_key = "buildingplan/global"; /* * ItemFilter @@ -501,13 +502,69 @@ void migrateV1ToV2() } } -static void init_global_settings(std::map & settings) +// assumes no setting has '=' or '|' characters +static std::string serialize_settings(std::map & settings) +{ + std::ostringstream ser; + for (auto & entry : settings) + { + ser << entry.first << "=" << (entry.second ? "1" : "0") << "|"; + } + return ser.str(); +} + +static void deserialize_settings(std::map & settings, + std::string ser) +{ + std::vector tokens; + split_string(&tokens, ser, "|"); + for (auto token : tokens) + { + if (token.empty()) + continue; + + std::vector parts; + split_string(&parts, token, "="); + if (parts.size() != 2) + { + debug("invalid serialized setting format: '%s'", token.c_str()); + continue; + } + std::string key = parts[0]; + if (settings.count(key) == 0) + { + debug("unknown serialized setting: '%s", key.c_str()); + continue; + } + settings[key] = static_cast(atoi(parts[1].c_str())); + debug("deserialized setting: %s = %d", key.c_str(), settings[key]); + } +} + +static DFHack::PersistentDataItem init_global_settings( + std::map & settings) { settings.clear(); settings["blocks"] = true; settings["boulders"] = true; settings["logs"] = true; settings["bars"] = false; + + // load persistent global settings if they exist; otherwise create them + std::vector items; + DFHack::World::GetPersistentData(&items, global_settings_persistence_key); + if (items.size() == 1) + { + DFHack::PersistentDataItem & config = items[0]; + deserialize_settings(settings, config.val()); + return config; + } + + debug("initializing persistent global settings"); + DFHack::PersistentDataItem config = + DFHack::World::AddPersistentData(global_settings_persistence_key); + config.val() = serialize_settings(settings); + return config; } const std::map & Planner::getGlobalSettings() const @@ -525,6 +582,8 @@ bool Planner::setGlobalSetting(std::string name, bool value) debug("global setting '%s' %d -> %d", name.c_str(), global_settings[name], value); global_settings[name] = value; + if (config.isValid()) + config.val() = serialize_settings(global_settings); return true; } @@ -535,7 +594,7 @@ void Planner::reset() planned_buildings.clear(); tasks.clear(); - init_global_settings(global_settings); + config = init_global_settings(global_settings); migrateV1ToV2(); diff --git a/plugins/buildingplan-planner.h b/plugins/buildingplan-planner.h index 18cfaf0b1..7b1615704 100644 --- a/plugins/buildingplan-planner.h +++ b/plugins/buildingplan-planner.h @@ -120,6 +120,7 @@ public: void doCycle(); private: + DFHack::PersistentDataItem config; std::map global_settings; std::unordered_map, diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 52bd5118f..a89c6c8fd 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -7,6 +7,7 @@ #include "modules/Maps.h" #include "modules/World.h" +#include "Core.h" #include "LuaTools.h" #include "PluginManager.h" @@ -814,6 +815,33 @@ IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_query_hook, render); IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_place_hook, render); IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_room_hook, render); +static DFHack::PersistentDataItem get_ui_settings_config() +{ + static const std::string settings_persistence_key = + "buildingplan/ui_settings"; + + bool added; + DFHack::PersistentDataItem config = + DFHack::World::GetPersistentData(settings_persistence_key, &added); + if (added) + { + config.ival(0) = quickfort_mode; + } + return config; +} + +static void load_settings() +{ + DFHack::PersistentDataItem config = get_ui_settings_config(); + quickfort_mode = config.ival(0); +} + +static void save_settings() +{ + DFHack::PersistentDataItem config = get_ui_settings_config(); + config.ival(0) = quickfort_mode; +} + static command_result buildingplan_cmd(color_ostream &out, vector & parameters) { if (!parameters.empty()) @@ -841,7 +869,11 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) if (enable != is_enabled) { - planner.reset(); + if (DFHack::Core::getInstance().isMapLoaded()) + { + load_settings(); + planner.reset(); + } if (!INTERPOSE_HOOK(buildingplan_query_hook, feed).apply(enable) || !INTERPOSE_HOOK(buildingplan_place_hook, feed).apply(enable) || @@ -871,6 +903,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan { switch (event) { case SC_MAP_LOADED: + load_settings(); planner.reset(); roomMonitor.reset(out); break; @@ -928,6 +961,7 @@ static void setSetting(std::string name, bool value) { { debug("setting quickfort_mode %d -> %d", quickfort_mode, value); quickfort_mode = value; + save_settings(); return; } planner.setGlobalSetting(name, value);