void Client::loadMods() { // Load builtin scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath()); // If modding is not enabled, don't load mods, just builtin if (!m_modding_enabled) { return; } ClientModConfiguration modconf(getClientModsLuaPath()); m_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 (const ModSpec &mod : m_mods) infostream << mod.name << " "; infostream << std::endl; // Load and run "mod" scripts for (const ModSpec &mod : m_mods) { if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) { throw ModError("Error loading mod \"" + mod.name + "\": Mod name does not follow naming conventions: " "Only characters [a-z0-9_] are allowed."); } scanModIntoMemory(mod.name, mod.path); } }
void Client::initMods() { m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); // If modding is not enabled, don't load mods, just builtin if (!m_modding_enabled) { return; } 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 characters [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); } }
ModConfiguration::ModConfiguration(std::string worldpath) { SubgameSpec gamespec = findWorldSubgame(worldpath); // Add common mods std::map<std::string, ModSpec> common_mods; std::vector<std::string> inexistent_common_mods; Settings gameconf; if(getGameConfig(gamespec.path, gameconf)){ if(gameconf.exists("common_mods")){ Strfnd f(gameconf.get("common_mods")); while(!f.atend()){ std::string modname = trim(f.next(",")); if(modname.empty()) continue; ModSpec spec = findCommonMod(modname); if(spec.name.empty()) inexistent_common_mods.push_back(modname); else common_mods.insert(std::make_pair(modname, spec)); } } } if(!inexistent_common_mods.empty()){ std::string s = "Required common mods "; for(u32 i=0; i<inexistent_common_mods.size(); i++){ if(i != 0) s += ", "; s += std::string("\"") + inexistent_common_mods[i] + "\""; } s += " could not be found."; throw ModError(s); } addMods(flattenMods(common_mods)); // 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> exclude_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)) { exclude_mod_names.insert(name.substr(9)); } } // Collect all mods in gamespec.addon_mods_paths, // excluding those in the set exclude_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(exclude_mod_names.count(mod.name) == 0) addon_mods.push_back(mod); } } 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); } // get the mods in order resolveDependencies(); }
ModConfiguration::ModConfiguration(std::string 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>::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); } // get the mods in order resolveDependencies(); }
// Get a dependency-sorted list of ModSpecs core::list<ModSpec> getMods(core::list<std::string> &modspaths) throw(ModError) { std::queue<ModSpec> mods_satisfied; core::list<ModSpec> mods_unsorted; core::list<ModSpec> mods_sorted; // name, path: For detecting name conflicts std::map<std::string, std::string> mod_names; for(core::list<std::string>::Iterator i = modspaths.begin(); i != modspaths.end(); i++){ std::string modspath = *i; std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath); for(u32 j=0; j<dirlist.size(); j++){ if(!dirlist[j].dir) continue; std::string modname = dirlist[j].name; std::string modpath = modspath + DIR_DELIM + modname; // Detect mod name conflicts { std::map<std::string, std::string>::const_iterator i; i = mod_names.find(modname); if(i != mod_names.end()){ std::string s; infostream<<"WARNING: Mod name conflict detected: " <<std::endl <<"Already loaded: "<<i->second<<std::endl <<"Will not load: "<<modpath<<std::endl; continue; } } std::set<std::string> depends; std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(), std::ios_base::binary); while(is.good()){ std::string dep; std::getline(is, dep); dep = trim(dep); if(dep != "") depends.insert(dep); } ModSpec spec(modname, modpath, depends); mods_unsorted.push_back(spec); if(depends.empty()) mods_satisfied.push(spec); mod_names[modname] = modpath; } } // Sort by depencencies while(!mods_satisfied.empty()){ ModSpec mod = mods_satisfied.front(); mods_satisfied.pop(); mods_sorted.push_back(mod); for(core::list<ModSpec>::Iterator i = mods_unsorted.begin(); i != mods_unsorted.end(); i++){ ModSpec &mod2 = *i; if(mod2.unsatisfied_depends.empty()) continue; mod2.unsatisfied_depends.erase(mod.name); if(!mod2.unsatisfied_depends.empty()) continue; mods_satisfied.push(mod2); } } std::ostringstream errs(std::ios::binary); // Check unsatisfied dependencies for(core::list<ModSpec>::Iterator i = mods_unsorted.begin(); i != mods_unsorted.end(); i++){ ModSpec &mod = *i; if(mod.unsatisfied_depends.empty()) continue; errs<<"mod \""<<mod.name <<"\" has unsatisfied dependencies:"; for(std::set<std::string>::iterator i = mod.unsatisfied_depends.begin(); i != mod.unsatisfied_depends.end(); i++){ errs<<" \""<<(*i)<<"\""; } errs<<"."<<std::endl; mods_sorted.push_back(mod); } if(errs.str().size() != 0){ throw ModError(errs.str()); } return mods_sorted; }