]> git.lizzy.rs Git - minetest.git/commitdiff
Rework Settings to support arbitrary hierarchies (#11352)
authorsfan5 <sfan5@live.de>
Wed, 23 Jun 2021 13:22:31 +0000 (15:22 +0200)
committerGitHub <noreply@github.com>
Wed, 23 Jun 2021 13:22:31 +0000 (15:22 +0200)
src/httpfetch.cpp
src/main.cpp
src/map_settings_manager.cpp
src/map_settings_manager.h
src/settings.cpp
src/settings.h
src/unittest/test_map_settings_manager.cpp

index 6137782ffdbc34839e1052504fe483a692d58176..1307bfec3af0072bfd94bddcb64a3a406fdfdef5 100644 (file)
@@ -761,10 +761,12 @@ void httpfetch_cleanup()
 {
        verbosestream<<"httpfetch_cleanup: cleaning up"<<std::endl;
 
-       g_httpfetch_thread->stop();
-       g_httpfetch_thread->requestWakeUp();
-       g_httpfetch_thread->wait();
-       delete g_httpfetch_thread;
+       if (g_httpfetch_thread) {
+               g_httpfetch_thread->stop();
+               g_httpfetch_thread->requestWakeUp();
+               g_httpfetch_thread->wait();
+               delete g_httpfetch_thread;
+       }
 
        curl_global_cleanup();
 }
index 4a69f83b56f83ebc9f5b6fcefaf56dfaac7488e5..ffbdb7b5ba07a6aefe01fd45b17ed8362b9419c5 100644 (file)
@@ -91,6 +91,7 @@ static void list_worlds(bool print_name, bool print_path);
 static bool setup_log_params(const Settings &cmd_args);
 static bool create_userdata_path();
 static bool init_common(const Settings &cmd_args, int argc, char *argv[]);
+static void uninit_common();
 static void startup_message();
 static bool read_config_file(const Settings &cmd_args);
 static void init_log_streams(const Settings &cmd_args);
@@ -201,6 +202,7 @@ int main(int argc, char *argv[])
                errorstream << "Unittest support is not enabled in this binary. "
                        << "If you want to enable it, compile project with BUILD_UNITTESTS=1 flag."
                        << std::endl;
+               return 1;
 #endif
        }
 #endif
@@ -236,9 +238,6 @@ int main(int argc, char *argv[])
 
        print_modified_quicktune_values();
 
-       // Stop httpfetch thread (if started)
-       httpfetch_cleanup();
-
        END_DEBUG_EXCEPTION_HANDLER
 
        return retval;
@@ -486,13 +485,14 @@ static bool init_common(const Settings &cmd_args, int argc, char *argv[])
        startup_message();
        set_default_settings();
 
-       // Initialize sockets
        sockets_init();
-       atexit(sockets_cleanup);
 
        // Initialize g_settings
        Settings::createLayer(SL_GLOBAL);
 
+       // Set cleanup callback(s) to run at process exit
+       atexit(uninit_common);
+
        if (!read_config_file(cmd_args))
                return false;
 
@@ -511,6 +511,17 @@ static bool init_common(const Settings &cmd_args, int argc, char *argv[])
        return true;
 }
 
+static void uninit_common()
+{
+       httpfetch_cleanup();
+
+       sockets_cleanup();
+
+       // It'd actually be okay to leak these but we want to please valgrind...
+       for (int i = 0; i < (int)SL_TOTAL_COUNT; i++)
+               delete Settings::getLayer((SettingsLayer)i);
+}
+
 static void startup_message()
 {
        infostream << PROJECT_NAME << " " << _("with")
index 99e3cb0e6c3e59c23ad357164dfbb544c56374b3..7e86a99379153e9e85c0b7305122ee39bf1f153b 100644 (file)
@@ -26,15 +26,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "map_settings_manager.h"
 
 MapSettingsManager::MapSettingsManager(const std::string &map_meta_path):
-       m_map_meta_path(map_meta_path)
+       m_map_meta_path(map_meta_path),
+       m_hierarchy(g_settings)
 {
-       m_map_settings = Settings::createLayer(SL_MAP, "[end_of_params]");
-       Mapgen::setDefaultSettings(Settings::getLayer(SL_DEFAULTS));
+       /*
+        * We build our own hierarchy which falls back to the global one.
+        * It looks as follows: (lowest prio first)
+        * 0: whatever is picked up from g_settings (incl. engine defaults)
+        * 1: defaults set by scripts (override_meta = false)
+        * 2: settings present in map_meta.txt or overriden by scripts
+        */
+       m_defaults = new Settings("", &m_hierarchy, 1);
+       m_map_settings = new Settings("[end_of_params]", &m_hierarchy, 2);
 }
 
 
 MapSettingsManager::~MapSettingsManager()
 {
+       delete m_defaults;
        delete m_map_settings;
        delete mapgen_params;
 }
@@ -43,14 +52,13 @@ MapSettingsManager::~MapSettingsManager()
 bool MapSettingsManager::getMapSetting(
        const std::string &name, std::string *value_out)
 {
-       // Get from map_meta.txt, then try from all other sources
+       // Try getting it normally first
        if (m_map_settings->getNoEx(name, *value_out))
                return true;
 
-       // Compatibility kludge
+       // If not we may have to resolve some compatibility kludges
        if (name == "seed")
                return Settings::getLayer(SL_GLOBAL)->getNoEx("fixed_map_seed", *value_out);
-
        return false;
 }
 
@@ -72,7 +80,7 @@ bool MapSettingsManager::setMapSetting(
        if (override_meta)
                m_map_settings->set(name, value);
        else
-               Settings::getLayer(SL_GLOBAL)->set(name, value);
+               m_defaults->set(name, value);
 
        return true;
 }
@@ -87,7 +95,7 @@ bool MapSettingsManager::setMapSettingNoiseParams(
        if (override_meta)
                m_map_settings->setNoiseParams(name, *value);
        else
-               Settings::getLayer(SL_GLOBAL)->setNoiseParams(name, *value);
+               m_defaults->setNoiseParams(name, *value);
 
        return true;
 }
@@ -146,15 +154,8 @@ MapgenParams *MapSettingsManager::makeMapgenParams()
        if (mapgen_params)
                return mapgen_params;
 
-       assert(m_map_settings != NULL);
-
-       // At this point, we have (in order of precedence):
-       // 1). SL_MAP containing map_meta.txt settings or
-       //     explicit overrides from scripts
-       // 2). SL_GLOBAL containing all user-specified config file
-       //     settings
-       // 3). SL_DEFAULTS containing any low-priority settings from
-       //     scripts, e.g. mods using Lua as an enhanced config file)
+       assert(m_map_settings);
+       assert(m_defaults);
 
        // Now, get the mapgen type so we can create the appropriate MapgenParams
        std::string mg_name;
index 9258d3032c062a1d3ad7f614a71e4d3d984605e4..fa271268d30364e1a845c346e7481f7c871b4877 100644 (file)
@@ -20,8 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #pragma once
 
 #include <string>
+#include "settings.h"
 
-class Settings;
 struct NoiseParams;
 struct MapgenParams;
 
@@ -70,6 +70,8 @@ class MapSettingsManager {
 
 private:
        std::string m_map_meta_path;
-       // TODO: Rename to "m_settings"
+
+       SettingsHierarchy m_hierarchy;
+       Settings *m_defaults;
        Settings *m_map_settings;
 };
index cff393e5f485fbbd3979c60b5dbab5527235b490..0a942499408eccc86904ec62c007e16dfe1f4ada 100644 (file)
@@ -33,35 +33,90 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <cctype>
 #include <algorithm>
 
-Settings *g_settings = nullptr; // Populated in main()
+Settings *g_settings = nullptr;
+static SettingsHierarchy g_hierarchy;
 std::string g_settings_path;
 
-Settings *Settings::s_layers[SL_TOTAL_COUNT] = {0}; // Zeroed by compiler
 std::unordered_map<std::string, const FlagDesc *> Settings::s_flags;
 
+/* Settings hierarchy implementation */
 
-Settings *Settings::createLayer(SettingsLayer sl, const std::string &end_tag)
+SettingsHierarchy::SettingsHierarchy(Settings *fallback)
+{
+       layers.push_back(fallback);
+}
+
+
+Settings *SettingsHierarchy::getLayer(int layer) const
 {
-       if ((int)sl < 0 || sl >= SL_TOTAL_COUNT)
+       if (layer < 0 || layer >= layers.size())
                throw BaseException("Invalid settings layer");
+       return layers[layer];
+}
+
 
-       Settings *&pos = s_layers[(size_t)sl];
+Settings *SettingsHierarchy::getParent(int layer) const
+{
+       assert(layer >= 0 && layer < layers.size());
+       // iterate towards the origin (0) to find the next fallback layer
+       for (int i = layer - 1; i >= 0; --i) {
+               if (layers[i])
+                       return layers[i];
+       }
+
+       return nullptr;
+}
+
+
+void SettingsHierarchy::onLayerCreated(int layer, Settings *obj)
+{
+       if (layer < 0)
+               throw BaseException("Invalid settings layer");
+       if (layers.size() < layer+1)
+               layers.resize(layer+1);
+
+       Settings *&pos = layers[layer];
        if (pos)
-               throw BaseException("Setting layer " + std::to_string(sl) + " already exists");
+               throw BaseException("Setting layer " + itos(layer) + " already exists");
+
+       pos = obj;
+       // This feels bad
+       if (this == &g_hierarchy && layer == (int)SL_GLOBAL)
+               g_settings = obj;
+}
+
+
+void SettingsHierarchy::onLayerRemoved(int layer)
+{
+       assert(layer >= 0 && layer < layers.size());
+       layers[layer] = nullptr;
+       if (this == &g_hierarchy && layer == (int)SL_GLOBAL)
+               g_settings = nullptr;
+}
 
-       pos = new Settings(end_tag);
-       pos->m_settingslayer = sl;
+/* Settings implementation */
 
-       if (sl == SL_GLOBAL)
-               g_settings = pos;
-       return pos;
+Settings *Settings::createLayer(SettingsLayer sl, const std::string &end_tag)
+{
+       return new Settings(end_tag, &g_hierarchy, (int)sl);
 }
 
 
 Settings *Settings::getLayer(SettingsLayer sl)
 {
        sanity_check((int)sl >= 0 && sl < SL_TOTAL_COUNT);
-       return s_layers[(size_t)sl];
+       return g_hierarchy.layers[(int)sl];
+}
+
+
+Settings::Settings(const std::string &end_tag, SettingsHierarchy *h,
+               int settings_layer) :
+       m_end_tag(end_tag),
+       m_hierarchy(h),
+       m_settingslayer(settings_layer)
+{
+       if (m_hierarchy)
+               m_hierarchy->onLayerCreated(m_settingslayer, this);
 }
 
 
@@ -69,12 +124,8 @@ Settings::~Settings()
 {
        MutexAutoLock lock(m_mutex);
 
-       if (m_settingslayer < SL_TOTAL_COUNT)
-               s_layers[(size_t)m_settingslayer] = nullptr;
-
-       // Compatibility
-       if (m_settingslayer == SL_GLOBAL)
-               g_settings = nullptr;
+       if (m_hierarchy)
+               m_hierarchy->onLayerRemoved(m_settingslayer);
 
        clearNoLock();
 }
@@ -86,8 +137,8 @@ Settings & Settings::operator = (const Settings &other)
                return *this;
 
        // TODO: Avoid copying Settings objects. Make this private.
-       FATAL_ERROR_IF(m_settingslayer != SL_TOTAL_COUNT && other.m_settingslayer != SL_TOTAL_COUNT,
-               ("Tried to copy unique Setting layer " + std::to_string(m_settingslayer)).c_str());
+       FATAL_ERROR_IF(m_hierarchy || other.m_hierarchy,
+               "Cannot copy or overwrite Settings object that belongs to a hierarchy");
 
        MutexAutoLock lock(m_mutex);
        MutexAutoLock lock2(other.m_mutex);
@@ -410,18 +461,7 @@ bool Settings::parseCommandLine(int argc, char *argv[],
 
 Settings *Settings::getParent() const
 {
-       // If the Settings object is within the hierarchy structure,
-       // iterate towards the origin (0) to find the next fallback layer
-       if (m_settingslayer >= SL_TOTAL_COUNT)
-               return nullptr;
-
-       for (int i = (int)m_settingslayer - 1; i >= 0; --i) {
-               if (s_layers[i])
-                       return s_layers[i];
-       }
-
-       // No parent
-       return nullptr;
+       return m_hierarchy ? m_hierarchy->getParent(m_settingslayer) : nullptr;
 }
 
 
@@ -823,6 +863,8 @@ bool Settings::set(const std::string &name, const std::string &value)
 // TODO: Remove this function
 bool Settings::setDefault(const std::string &name, const std::string &value)
 {
+       FATAL_ERROR_IF(m_hierarchy != &g_hierarchy, "setDefault is only valid on "
+               "global settings");
        return getLayer(SL_DEFAULTS)->set(name, value);
 }
 
index e22d949d32e9cd817c1b4eba5b73aad8ab8ad361..7791413b9859207e4a5c4f0a213706e271a18cc3 100644 (file)
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "irrlichttypes_bloated.h"
 #include "util/string.h"
+#include "util/basic_macros.h"
 #include <string>
 #include <list>
 #include <set>
@@ -60,14 +61,36 @@ enum SettingsParseEvent {
        SPE_MULTILINE,
 };
 
+// Describes the global setting layers, SL_GLOBAL is where settings are read from
 enum SettingsLayer {
        SL_DEFAULTS,
        SL_GAME,
        SL_GLOBAL,
-       SL_MAP,
        SL_TOTAL_COUNT
 };
 
+// Implements the hierarchy a settings object may be part of
+class SettingsHierarchy {
+public:
+       /*
+        * A settings object that may be part of another hierarchy can
+        * occupy the index 0 as a fallback. If not set you can use 0 on your own.
+        */
+       SettingsHierarchy(Settings *fallback = nullptr);
+
+       DISABLE_CLASS_COPY(SettingsHierarchy)
+
+       Settings *getLayer(int layer) const;
+
+private:
+       friend class Settings;
+       Settings *getParent(int layer) const;
+       void onLayerCreated(int layer, Settings *obj);
+       void onLayerRemoved(int layer);
+
+       std::vector<Settings*> layers;
+};
+
 struct ValueSpec {
        ValueSpec(ValueType a_type, const char *a_help=NULL)
        {
@@ -100,13 +123,15 @@ typedef std::unordered_map<std::string, SettingsEntry> SettingEntries;
 
 class Settings {
 public:
+       /* These functions operate on the global hierarchy! */
        static Settings *createLayer(SettingsLayer sl, const std::string &end_tag = "");
        static Settings *getLayer(SettingsLayer sl);
-       SettingsLayer getLayerType() const { return m_settingslayer; }
+       /**/
 
        Settings(const std::string &end_tag = "") :
                m_end_tag(end_tag)
        {}
+       Settings(const std::string &end_tag, SettingsHierarchy *h, int settings_layer);
        ~Settings();
 
        Settings & operator += (const Settings &other);
@@ -200,9 +225,9 @@ class Settings {
        // remove a setting
        bool remove(const std::string &name);
 
-       /**************
-        * Miscellany *
-        **************/
+       /*****************
+        * Miscellaneous *
+        *****************/
 
        void setDefault(const std::string &name, const FlagDesc *flagdesc, u32 flags);
        const FlagDesc *getFlagDescFallback(const std::string &name) const;
@@ -214,6 +239,10 @@ class Settings {
 
        void removeSecureSettings();
 
+       // Returns the settings layer this object is.
+       // If within the global hierarchy you can cast this to enum SettingsLayer
+       inline int getLayer() const { return m_settingslayer; }
+
 private:
        /***********************
         * Reading and writing *
@@ -257,7 +286,8 @@ class Settings {
        // All methods that access m_settings/m_defaults directly should lock this.
        mutable std::mutex m_mutex;
 
-       static Settings *s_layers[SL_TOTAL_COUNT];
-       SettingsLayer m_settingslayer = SL_TOTAL_COUNT;
+       SettingsHierarchy *m_hierarchy = nullptr;
+       int m_settingslayer = -1;
+
        static std::unordered_map<std::string, const FlagDesc *> s_flags;
 };
index 81ca687055c1a374fe4bb5a8e0d0b47c7414b565..17c31fe7924cda0a7599286304fec6fee581f9c0 100644 (file)
@@ -148,6 +148,11 @@ void TestMapSettingsManager::testMapSettingsManager()
                check_noise_params(&dummy, &script_np_factor);
        }
 
+       // The settings manager MUST leave user settings alone
+       mgr.setMapSetting("testname", "1");
+       mgr.setMapSetting("testname", "1", true);
+       UASSERT(!Settings::getLayer(SL_GLOBAL)->exists("testname"));
+
        // Now make our Params and see if the values are correctly sourced
        MapgenParams *params = mgr.makeMapgenParams();
        UASSERT(params->mgtype == MAPGEN_V5);