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.
develop
Myk Taylor 2020-10-25 02:37:22 -07:00
parent 7e78d8802e
commit 486bf2719b
3 changed files with 97 additions and 3 deletions

@ -20,6 +20,7 @@
static const std::string planned_building_persistence_key_v1 = "buildingplan/constraints"; 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 planned_building_persistence_key_v2 = "buildingplan/constraints2";
static const std::string global_settings_persistence_key = "buildingplan/global";
/* /*
* ItemFilter * ItemFilter
@ -501,13 +502,69 @@ void migrateV1ToV2()
} }
} }
static void init_global_settings(std::map<std::string, bool> & settings) // assumes no setting has '=' or '|' characters
static std::string serialize_settings(std::map<std::string, bool> & settings)
{
std::ostringstream ser;
for (auto & entry : settings)
{
ser << entry.first << "=" << (entry.second ? "1" : "0") << "|";
}
return ser.str();
}
static void deserialize_settings(std::map<std::string, bool> & settings,
std::string ser)
{
std::vector<std::string> tokens;
split_string(&tokens, ser, "|");
for (auto token : tokens)
{
if (token.empty())
continue;
std::vector<std::string> 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<bool>(atoi(parts[1].c_str()));
debug("deserialized setting: %s = %d", key.c_str(), settings[key]);
}
}
static DFHack::PersistentDataItem init_global_settings(
std::map<std::string, bool> & settings)
{ {
settings.clear(); settings.clear();
settings["blocks"] = true; settings["blocks"] = true;
settings["boulders"] = true; settings["boulders"] = true;
settings["logs"] = true; settings["logs"] = true;
settings["bars"] = false; settings["bars"] = false;
// load persistent global settings if they exist; otherwise create them
std::vector<PersistentDataItem> 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<std::string, bool> & Planner::getGlobalSettings() const const std::map<std::string, bool> & Planner::getGlobalSettings() const
@ -525,6 +582,8 @@ bool Planner::setGlobalSetting(std::string name, bool value)
debug("global setting '%s' %d -> %d", debug("global setting '%s' %d -> %d",
name.c_str(), global_settings[name], value); name.c_str(), global_settings[name], value);
global_settings[name] = value; global_settings[name] = value;
if (config.isValid())
config.val() = serialize_settings(global_settings);
return true; return true;
} }
@ -535,7 +594,7 @@ void Planner::reset()
planned_buildings.clear(); planned_buildings.clear();
tasks.clear(); tasks.clear();
init_global_settings(global_settings); config = init_global_settings(global_settings);
migrateV1ToV2(); migrateV1ToV2();

@ -120,6 +120,7 @@ public:
void doCycle(); void doCycle();
private: private:
DFHack::PersistentDataItem config;
std::map<std::string, bool> global_settings; std::map<std::string, bool> global_settings;
std::unordered_map<BuildingTypeKey, std::unordered_map<BuildingTypeKey,
std::vector<ItemFilter>, std::vector<ItemFilter>,

@ -7,6 +7,7 @@
#include "modules/Maps.h" #include "modules/Maps.h"
#include "modules/World.h" #include "modules/World.h"
#include "Core.h"
#include "LuaTools.h" #include "LuaTools.h"
#include "PluginManager.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_place_hook, render);
IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_room_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 <string> & parameters) static command_result buildingplan_cmd(color_ostream &out, vector <string> & parameters)
{ {
if (!parameters.empty()) if (!parameters.empty())
@ -841,7 +869,11 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
if (enable != is_enabled) if (enable != is_enabled)
{ {
if (DFHack::Core::getInstance().isMapLoaded())
{
load_settings();
planner.reset(); planner.reset();
}
if (!INTERPOSE_HOOK(buildingplan_query_hook, feed).apply(enable) || if (!INTERPOSE_HOOK(buildingplan_query_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(buildingplan_place_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) { switch (event) {
case SC_MAP_LOADED: case SC_MAP_LOADED:
load_settings();
planner.reset(); planner.reset();
roomMonitor.reset(out); roomMonitor.reset(out);
break; break;
@ -928,6 +961,7 @@ static void setSetting(std::string name, bool value) {
{ {
debug("setting quickfort_mode %d -> %d", quickfort_mode, value); debug("setting quickfort_mode %d -> %d", quickfort_mode, value);
quickfort_mode = value; quickfort_mode = value;
save_settings();
return; return;
} }
planner.setGlobalSetting(name, value); planner.setGlobalSetting(name, value);