[CSM] implement client side mod loading (#5123)
authorLoïc Blot <nerzhul@users.noreply.github.com>
Fri, 27 Jan 2017 06:41:10 +0000 (07:41 +0100)
committerLoïc Blot <nerzhul@users.noreply.github.com>
Mon, 13 Mar 2017 22:56:05 +0000 (23:56 +0100)
* client side mods are located in clientmods/
* move builtin/preview.lua to clientmods/preview/init.lua as a preview mod
* refactor ModConfiguration class to work properly with client and server using child objects
* move some Server constructor mod load code to ModConfiguration to reduce code duplication between client and server
* remove mods.{cpp,h} unused functions
* use UNORDERED_SET instead of std::set in some modspec storages

CMakeLists.txt
builtin/client/chatcommands.lua
builtin/client/init.lua
builtin/client/preview.lua [deleted file]
clientmods/preview/init.lua [new file with mode: 0644]
src/client.cpp
src/client.h
src/mods.cpp
src/mods.h
src/server.cpp

index d2568a9ae07d0d8f37855153ec411164a2976c07..7fe950c8182c819df354e9132873323cc1df1ee0 100644 (file)
@@ -152,6 +152,7 @@ endif()
 
 install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/builtin" DESTINATION "${SHAREDIR}")
 install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/client" DESTINATION "${SHAREDIR}")
+install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/clientmods" DESTINATION "${SHAREDIR}")
 install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games" DESTINATION "${SHAREDIR}" PATTERN ".git*" EXCLUDE)
 
 if(BUILD_CLIENT)
index b49c222ef3408ced28d9d6395c3adc7df5619513..43b4d9a72c1cce548c960354fc67a54b18b11559 100644 (file)
@@ -5,24 +5,24 @@ core.register_on_sending_chat_messages(function(message)
        if not (message:sub(1,1) == "/") then
                return false
        end
-       
+
        core.display_chat_message("issued command: " .. message)
-       
+
        local cmd, param = string.match(message, "^/([^ ]+) *(.*)")
        if not param then
                param = ""
        end
-       
+
        local cmd_def = core.registered_chatcommands[cmd]
-       
+
        if cmd_def then
                core.set_last_run_mod(cmd_def.mod_origin)
-               local success, message = cmd_def.func(param)
+               local _, message = cmd_def.func(param)
                if message then
                        core.display_chat_message(message)
                end
                return true
        end
-       
+
        return false
-end)
\ No newline at end of file
+end)
index b204ee5e6c4388beeaf9aa51d00ff6ab082ceaf6..592274540cb80945cf21363d67f0dbb779de63d3 100644 (file)
@@ -7,7 +7,6 @@ dofile(clientpath .. "register.lua")
 dofile(commonpath .. "after.lua")
 dofile(commonpath .. "chatcommands.lua")
 dofile(clientpath .. "chatcommands.lua")
-dofile(clientpath .. "preview.lua")
 
 core.register_on_death(function()
        core.display_chat_message("You died.")
diff --git a/builtin/client/preview.lua b/builtin/client/preview.lua
deleted file mode 100644 (file)
index 4c01d66..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
--- This is an example function to ensure it's working properly, should be removed before merge
-core.register_on_shutdown(function()
-       print("[PREVIEW] shutdown client")
-end)
-
--- This is an example function to ensure it's working properly, should be removed before merge
-core.register_on_receiving_chat_messages(function(message)
-       print("[PREVIEW] Received message " .. message)
-       return false
-end)
-
--- This is an example function to ensure it's working properly, should be removed before merge
-core.register_on_sending_chat_messages(function(message)
-       print("[PREVIEW] Sending message " .. message)
-       return false
-end)
-
--- This is an example function to ensure it's working properly, should be removed before merge
-core.register_on_hp_modification(function(hp)
-       print("[PREVIEW] HP modified " .. hp)
-end)
-
--- This is an example function to ensure it's working properly, should be removed before merge
-core.register_on_damage_taken(function(hp)
-       print("[PREVIEW] Damage taken " .. hp)
-end)
-
--- This is an example function to ensure it's working properly, should be removed before merge
-core.register_globalstep(function(dtime)
-       -- print("[PREVIEW] globalstep " .. dtime)
-end)
-
--- This is an example function to ensure it's working properly, should be removed before merge
-core.register_chatcommand("dump", {
-       func = function(param)
-               return true, dump(_G)
-       end,
-})
-
-core.after(2, function()
-       print("After 2")
-end)
diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua
new file mode 100644 (file)
index 0000000..4c01d66
--- /dev/null
@@ -0,0 +1,42 @@
+-- This is an example function to ensure it's working properly, should be removed before merge
+core.register_on_shutdown(function()
+       print("[PREVIEW] shutdown client")
+end)
+
+-- This is an example function to ensure it's working properly, should be removed before merge
+core.register_on_receiving_chat_messages(function(message)
+       print("[PREVIEW] Received message " .. message)
+       return false
+end)
+
+-- This is an example function to ensure it's working properly, should be removed before merge
+core.register_on_sending_chat_messages(function(message)
+       print("[PREVIEW] Sending message " .. message)
+       return false
+end)
+
+-- This is an example function to ensure it's working properly, should be removed before merge
+core.register_on_hp_modification(function(hp)
+       print("[PREVIEW] HP modified " .. hp)
+end)
+
+-- This is an example function to ensure it's working properly, should be removed before merge
+core.register_on_damage_taken(function(hp)
+       print("[PREVIEW] Damage taken " .. hp)
+end)
+
+-- This is an example function to ensure it's working properly, should be removed before merge
+core.register_globalstep(function(dtime)
+       -- print("[PREVIEW] globalstep " .. dtime)
+end)
+
+-- This is an example function to ensure it's working properly, should be removed before merge
+core.register_chatcommand("dump", {
+       func = function(param)
+               return true, dump(_G)
+       end,
+})
+
+core.after(2, function()
+       print("After 2")
+end)
index 3b807425224caf859232b422bd58092c55df83f9..4bb63fef1302be68319cb4141485af478a0638e2 100644 (file)
@@ -268,14 +268,50 @@ Client::Client(
 
 void Client::initMods()
 {
-       std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua";
+       m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
+
+       ClientModConfiguration modconf(getClientModsLuaPath());
+       std::vector<ModSpec> mods = modconf.getMods();
+       std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
+       // complain about mods with unsatisfied dependencies
+       if (!modconf.isConsistent()) {
+               modconf.printUnsatisfiedModsError();
+       }
+
+       // Print mods
+       infostream << "Client Loading mods: ";
+       for (std::vector<ModSpec>::const_iterator i = mods.begin();
+               i != mods.end(); ++i) {
+               infostream << (*i).name << " ";
+       }
+
+       infostream << std::endl;
+       // Load and run "mod" scripts
+       for (std::vector<ModSpec>::const_iterator it = mods.begin();
+               it != mods.end(); ++it) {
+               const ModSpec &mod = *it;
+               if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
+                       throw ModError("Error loading mod \"" + mod.name +
+                               "\": Mod name does not follow naming conventions: "
+                                       "Only chararacters [a-z0-9_] are allowed.");
+               }
+               std::string script_path = mod.path + DIR_DELIM + "init.lua";
+               infostream << "  [" << padStringRight(mod.name, 12) << "] [\""
+                       << script_path << "\"]" << std::endl;
+               m_script->loadMod(script_path, mod.name);
+       }
+}
 
-       m_script->loadMod(script_path, BUILTIN_MOD_NAME);
+const std::string &Client::getBuiltinLuaPath()
+{
+       static const std::string builtin_dir = porting::path_share + DIR_DELIM + "builtin";
+       return builtin_dir;
 }
 
-const std::string Client::getBuiltinLuaPath()
+const std::string &Client::getClientModsLuaPath()
 {
-       return porting::path_share + DIR_DELIM + "builtin";
+       static const std::string clientmods_dir = porting::path_share + DIR_DELIM + "clientmods";
+       return clientmods_dir;
 }
 
 const std::vector<ModSpec>& Client::getMods() const
index d170f9a07bdc198ecd0fe7669cbb19daed7b4d34..9b7130268be5c78102d682d118a4139b6fa95c1d 100644 (file)
@@ -433,7 +433,8 @@ public:
        ClientEnvironment& getEnv() { return m_env; }
        ITextureSource *tsrc() { return getTextureSource(); }
        ISoundManager *sound() { return getSoundManager(); }
-       static const std::string getBuiltinLuaPath();
+       static const std::string &getBuiltinLuaPath();
+       static const std::string &getClientModsLuaPath();
 
        virtual const std::vector<ModSpec> &getMods() const;
        virtual const ModSpec* getModSpec(const std::string &modname) const;
index bae9a42d31c64a8e62ddf0c19920eb775ddddf7c..5a7dc6dcaf7c29a33a8ecb6b5c6e965370aedad7 100644 (file)
@@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "subgame.h"
 #include "settings.h"
 #include "convert_json.h"
+#include "exceptions.h"
+#include "porting.h"
 
 static bool parseDependsLine(std::istream &is,
                std::string &dep, std::set<char> &symbols)
@@ -107,28 +109,6 @@ std::map<std::string, ModSpec> getModsInPath(std::string path, bool part_of_modp
        return result;
 }
 
-std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods)
-{
-       std::map<std::string, ModSpec> result;
-       for(std::map<std::string,ModSpec>::iterator it = mods.begin();
-               it != mods.end(); ++it)
-       {
-               ModSpec mod = (*it).second;
-               if(mod.is_modpack)
-               {
-                       std::map<std::string, ModSpec> content =
-                               flattenModTree(mod.modpack_content);
-                       result.insert(content.begin(),content.end());
-                       result.insert(std::make_pair(mod.name,mod));
-               }
-               else //not a modpack
-               {
-                       result.insert(std::make_pair(mod.name,mod));
-               }
-       }
-       return result;
-}
-
 std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
 {
        std::vector<ModSpec> result;
@@ -151,78 +131,32 @@ std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
        return result;
 }
 
-ModConfiguration::ModConfiguration(std::string worldpath)
+ModConfiguration::ModConfiguration(const std::string &worldpath):
+       m_unsatisfied_mods(),
+       m_sorted_mods(),
+       m_name_conflicts()
 {
-       SubgameSpec gamespec = findWorldSubgame(worldpath);
-
-       // Add all game mods and all world mods
-       addModsInPath(gamespec.gamemods_path);
-       addModsInPath(worldpath + DIR_DELIM + "worldmods");
-
-       // check world.mt file for mods explicitely declared to be
-       // loaded or not by a load_mod_<modname> = ... line.
-       std::string worldmt = worldpath+DIR_DELIM+"world.mt";
-       Settings worldmt_settings;
-       worldmt_settings.readConfigFile(worldmt.c_str());
-       std::vector<std::string> names = worldmt_settings.getNames();
-       std::set<std::string> include_mod_names;
-       for(std::vector<std::string>::iterator it = names.begin();
-               it != names.end(); ++it)
-       {
-               std::string name = *it;
-               // for backwards compatibility: exclude only mods which are
-               // explicitely excluded. if mod is not mentioned at all, it is
-               // enabled. So by default, all installed mods are enabled.
-               if (name.compare(0,9,"load_mod_") == 0 &&
-                       worldmt_settings.getBool(name))
-               {
-                       include_mod_names.insert(name.substr(9));
-               }
-       }
-
-       // Collect all mods that are also in include_mod_names
-       std::vector<ModSpec> addon_mods;
-       for(std::set<std::string>::const_iterator it_path = gamespec.addon_mods_paths.begin();
-                       it_path != gamespec.addon_mods_paths.end(); ++it_path)
-       {
-               std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(*it_path));
-               for(std::vector<ModSpec>::iterator it = addon_mods_in_path.begin();
-                       it != addon_mods_in_path.end(); ++it)
-               {
-                       ModSpec& mod = *it;
-                       if(include_mod_names.count(mod.name) != 0)
-                               addon_mods.push_back(mod);
-                       else
-                               worldmt_settings.setBool("load_mod_" + mod.name, false);
-               }
-       }
-       worldmt_settings.updateConfigFile(worldmt.c_str());
-
-       addMods(addon_mods);
+}
 
-       // report on name conflicts
-       if(!m_name_conflicts.empty()){
-               std::string s = "Unresolved name conflicts for mods ";
-               for(std::set<std::string>::const_iterator it = m_name_conflicts.begin();
-                               it != m_name_conflicts.end(); ++it)
-               {
-                       if(it != m_name_conflicts.begin()) s += ", ";
-                       s += std::string("\"") + (*it) + "\"";
-               }
-               s += ".";
-               throw ModError(s);
+void ModConfiguration::printUnsatisfiedModsError() const
+{
+       for (std::vector<ModSpec>::const_iterator it = m_unsatisfied_mods.begin();
+               it != m_unsatisfied_mods.end(); ++it) {
+               ModSpec mod = *it;
+               errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
+               for (UNORDERED_SET<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
+                       dep_it != mod.unsatisfied_depends.end(); ++dep_it)
+                       errorstream << " \"" << *dep_it << "\"";
+               errorstream << std::endl;
        }
-
-       // get the mods in order
-       resolveDependencies();
 }
 
-void ModConfiguration::addModsInPath(std::string path)
+void ModConfiguration::addModsInPath(const std::string &path)
 {
        addMods(flattenMods(getModsInPath(path)));
 }
 
-void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
+void ModConfiguration::addMods(const std::vector<ModSpec> &new_mods)
 {
        // Maintain a map of all existing m_unsatisfied_mods.
        // Keys are mod names and values are indices into m_unsatisfied_mods.
@@ -240,8 +174,8 @@ void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
 
                std::set<std::string> seen_this_iteration;
 
-               for(std::vector<ModSpec>::const_iterator it = new_mods.begin();
-                               it != new_mods.end(); ++it){
+               for (std::vector<ModSpec>::const_iterator it = new_mods.begin();
+                               it != new_mods.end(); ++it) {
                        const ModSpec &mod = *it;
                        if(mod.part_of_modpack != (bool)want_from_modpack)
                                continue;
@@ -280,6 +214,24 @@ void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
        }
 }
 
+void ModConfiguration::checkConflictsAndDeps()
+{
+       // report on name conflicts
+       if (!m_name_conflicts.empty()) {
+               std::string s = "Unresolved name conflicts for mods ";
+               for (UNORDERED_SET<std::string>::const_iterator it = m_name_conflicts.begin();
+                       it != m_name_conflicts.end(); ++it) {
+                       if (it != m_name_conflicts.begin()) s += ", ";
+                       s += std::string("\"") + (*it) + "\"";
+               }
+               s += ".";
+               throw ModError(s);
+       }
+
+       // get the mods in order
+       resolveDependencies();
+}
+
 void ModConfiguration::resolveDependencies()
 {
        // Step 1: Compile a list of the mod names we're working with
@@ -293,19 +245,19 @@ void ModConfiguration::resolveDependencies()
        // of each mod, split mods into satisfied and unsatisfied
        std::list<ModSpec> satisfied;
        std::list<ModSpec> unsatisfied;
-       for(std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin();
-                       it != m_unsatisfied_mods.end(); ++it){
+       for (std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin();
+                       it != m_unsatisfied_mods.end(); ++it) {
                ModSpec mod = *it;
                mod.unsatisfied_depends = mod.depends;
                // check which optional dependencies actually exist
-               for(std::set<std::string>::iterator it_optdep = mod.optdepends.begin();
-                               it_optdep != mod.optdepends.end(); ++it_optdep){
+               for (UNORDERED_SET<std::string>::iterator it_optdep = mod.optdepends.begin();
+                               it_optdep != mod.optdepends.end(); ++it_optdep) {
                        std::string optdep = *it_optdep;
-                       if(modnames.count(optdep) != 0)
+                       if (modnames.count(optdep) != 0)
                                mod.unsatisfied_depends.insert(optdep);
                }
                // if a mod has no depends it is initially satisfied
-               if(mod.unsatisfied_depends.empty())
+               if (mod.unsatisfied_depends.empty())
                        satisfied.push_back(mod);
                else
                        unsatisfied.push_back(mod);
@@ -335,6 +287,65 @@ void ModConfiguration::resolveDependencies()
        m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
 }
 
+ServerModConfiguration::ServerModConfiguration(const std::string &worldpath):
+       ModConfiguration(worldpath)
+{
+       SubgameSpec gamespec = findWorldSubgame(worldpath);
+
+       // Add all game mods and all world mods
+       addModsInPath(gamespec.gamemods_path);
+       addModsInPath(worldpath + DIR_DELIM + "worldmods");
+
+       // check world.mt file for mods explicitely declared to be
+       // loaded or not by a load_mod_<modname> = ... line.
+       std::string worldmt = worldpath+DIR_DELIM+"world.mt";
+       Settings worldmt_settings;
+       worldmt_settings.readConfigFile(worldmt.c_str());
+       std::vector<std::string> names = worldmt_settings.getNames();
+       std::set<std::string> include_mod_names;
+       for (std::vector<std::string>::const_iterator it = names.begin();
+               it != names.end(); ++it) {
+               std::string name = *it;
+               // for backwards compatibility: exclude only mods which are
+               // explicitely excluded. if mod is not mentioned at all, it is
+               // enabled. So by default, all installed mods are enabled.
+               if (name.compare(0,9,"load_mod_") == 0 &&
+                       worldmt_settings.getBool(name)) {
+                       include_mod_names.insert(name.substr(9));
+               }
+       }
+
+       // Collect all mods that are also in include_mod_names
+       std::vector<ModSpec> addon_mods;
+       for (std::set<std::string>::const_iterator it_path = gamespec.addon_mods_paths.begin();
+               it_path != gamespec.addon_mods_paths.end(); ++it_path) {
+               std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(*it_path));
+               for (std::vector<ModSpec>::const_iterator it = addon_mods_in_path.begin();
+                       it != addon_mods_in_path.end(); ++it) {
+                       const ModSpec& mod = *it;
+                       if (include_mod_names.count(mod.name) != 0)
+                               addon_mods.push_back(mod);
+                       else
+                               worldmt_settings.setBool("load_mod_" + mod.name, false);
+               }
+       }
+       worldmt_settings.updateConfigFile(worldmt.c_str());
+
+       addMods(addon_mods);
+
+       checkConflictsAndDeps();
+}
+
+#ifndef SERVER
+ClientModConfiguration::ClientModConfiguration(const std::string &path):
+       ModConfiguration(path)
+{
+       addModsInPath(path);
+       addModsInPath(porting::path_user + DIR_DELIM + "clientmods");
+       checkConflictsAndDeps();
+}
+#endif
+
 #if USE_CURL
 Json::Value getModstoreUrl(std::string url)
 {
index 61af5e5d165daa118056f83202a4c4c4a5b4edeb..c9bd51d992f6e6b78478517050a876ca480316bf 100644 (file)
@@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <string>
 #include <map>
 #include <json/json.h>
+#include "util/cpp11_container.h"
 #include "config.h"
 #include "metadata.h"
 
@@ -37,9 +38,9 @@ struct ModSpec
        std::string name;
        std::string path;
        //if normal mod:
-       std::set<std::string> depends;
-       std::set<std::string> optdepends;
-       std::set<std::string> unsatisfied_depends;
+       UNORDERED_SET<std::string> depends;
+       UNORDERED_SET<std::string> optdepends;
+       UNORDERED_SET<std::string> unsatisfied_depends;
 
        bool part_of_modpack;
        bool is_modpack;
@@ -62,12 +63,6 @@ void parseModContents(ModSpec &mod);
 
 std::map<std::string,ModSpec> getModsInPath(std::string path, bool part_of_modpack = false);
 
-// If failed, returned modspec has name==""
-ModSpec findCommonMod(const std::string &modname);
-
-// expands modpack contents, but does not replace them.
-std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods);
-
 // replaces modpack Modspecs with their content
 std::vector<ModSpec> flattenMods(std::map<std::string,ModSpec> mods);
 
@@ -77,17 +72,8 @@ std::vector<ModSpec> flattenMods(std::map<std::string,ModSpec> mods);
 class ModConfiguration
 {
 public:
-       ModConfiguration():
-               m_unsatisfied_mods(),
-               m_sorted_mods(),
-               m_name_conflicts()
-       {}
-
-
-       ModConfiguration(std::string worldpath);
-
        // checks if all dependencies are fullfilled.
-       bool isConsistent()
+       bool isConsistent() const
        {
                return m_unsatisfied_mods.empty();
        }
@@ -97,19 +83,24 @@ public:
                return m_sorted_mods;
        }
 
-       std::vector<ModSpec> getUnsatisfiedMods()
+       const std::vector<ModSpec> &getUnsatisfiedMods() const
        {
                return m_unsatisfied_mods;
        }
 
-private:
+       void printUnsatisfiedModsError() const;
+
+protected:
+       ModConfiguration(const std::string &worldpath);
        // adds all mods in the given path. used for games, modpacks
        // and world-specific mods (worldmods-folders)
-       void addModsInPath(std::string path);
+       void addModsInPath(const std::string &path);
 
        // adds all mods in the set.
-       void addMods(std::vector<ModSpec> new_mods);
+       void addMods(const std::vector<ModSpec> &new_mods);
 
+       void checkConflictsAndDeps();
+private:
        // move mods from m_unsatisfied_mods to m_sorted_mods
        // in an order that satisfies dependencies
        void resolveDependencies();
@@ -132,10 +123,28 @@ private:
        // 1. game mod in modpack; 2. game mod;
        // 3. world mod in modpack; 4. world mod;
        // 5. addon mod in modpack; 6. addon mod.
-       std::set<std::string> m_name_conflicts;
+       UNORDERED_SET<std::string> m_name_conflicts;
+
+       // Deleted default constructor
+       ModConfiguration() {}
 
 };
 
+class ServerModConfiguration: public ModConfiguration
+{
+public:
+       ServerModConfiguration(const std::string &worldpath);
+
+};
+
+#ifndef SERVER
+class ClientModConfiguration: public ModConfiguration
+{
+public:
+       ClientModConfiguration(const std::string &path);
+};
+#endif
+
 #if USE_CURL
 Json::Value getModstoreUrl(std::string url);
 #else
index 3adbf40cc5fed5e17dcc00adab4a56da81ff43cf..dd6c9a418f03ae86f08e8543afe348c18c14a098 100644 (file)
@@ -218,20 +218,12 @@ Server::Server(
        std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
        m_banmanager = new BanManager(ban_path);
 
-       ModConfiguration modconf(m_path_world);
+       ServerModConfiguration modconf(m_path_world);
        m_mods = modconf.getMods();
        std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
        // complain about mods with unsatisfied dependencies
-       if(!modconf.isConsistent()) {
-               for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
-                       it != unsatisfied_mods.end(); ++it) {
-                       ModSpec mod = *it;
-                       errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
-                       for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
-                               dep_it != mod.unsatisfied_depends.end(); ++dep_it)
-                               errorstream << " \"" << *dep_it << "\"";
-                       errorstream << std::endl;
-               }
+       if (!modconf.isConsistent()) {
+               modconf.printUnsatisfiedModsError();
        }
 
        Settings worldmt_settings;
@@ -271,20 +263,17 @@ Server::Server(
 
        m_script = new ServerScripting(this);
 
-       std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua";
-
-       m_script->loadMod(script_path, BUILTIN_MOD_NAME);
+       m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
 
        // Print mods
        infostream << "Server: Loading mods: ";
-       for(std::vector<ModSpec>::iterator i = m_mods.begin();
+       for (std::vector<ModSpec>::const_iterator i = m_mods.begin();
                        i != m_mods.end(); ++i) {
-               const ModSpec &mod = *i;
-               infostream << mod.name << " ";
+               infostream << (*i).name << " ";
        }
        infostream << std::endl;
        // Load and run "mod" scripts
-       for (std::vector<ModSpec>::iterator it = m_mods.begin();
+       for (std::vector<ModSpec>::const_iterator it = m_mods.begin();
                        it != m_mods.end(); ++it) {
                const ModSpec &mod = *it;
                if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {