aboutsummaryrefslogtreecommitdiff
path: root/src/mapgen_v5.cpp
Commit message (Collapse)AuthorAge
* Mapgen files: Update and correct copyright creditsparamat2017-05-26
|
* Caverns: Remove unnecessary liquid excavationparamat2017-05-16
| | | | | | | | | | | | | | | Also disable CavesRandomWalk at a safer distance from caverns. Excavating liquids in cavern code is unnecessary as in practice we are already successfully disabling the generation of liquid caves that could intersect with caverns and cause excessive amounts of spreading liquids in caverns. However to be safer this commit now disables liquid caves at a larger distance from caverns, to compensate for liquid caves being able to generate up to a mapblock beyond a mapchunk border. Not excavating liquids in cavern code also allows a feature i am working on in experimental new core mapgens, but also allows for more flexibility in future.
* Mapgen: Remove '#include treegen.h' from non-mgv6 mapgensparamat2017-04-05
| | | | Only mgv6 uses the tree functions from treegen.cpp.
* Cavegen/Mgv5/Mgv7: Add optional giant cavernsparamat2017-04-03
| | | | | | | | | | | | | | Add to MapgenBasic for use by multiple mapgens. Add to mgv5 and mgv7, enabled by default. Similar to mgvalleys caverns but half the scale. Parameters for upper y limit, distance caverns taper to full size, and noise threshold (full cavern size). As with mgvalleys caverns are generated first and classic caves are disabled in any mapchunk containing a cavern, to avoid excessive spreading volumes of liquids. This also avoids floating blobs of liquid where a large classic cave has overgenerated out into a neighbouring previously-generated mapchunk.
* Add MapSettingsManager and new mapgen setting script API functionskwolekr2016-07-03
| | | | | | | | | | | | | | | This commit refactors the majority of the Mapgen settings system. - MapgenParams is now owned by MapSettingsManager, itself a part of ServerMap, instead of the EmergeManager. - New Script API functions added: core.get_mapgen_setting core.get_mapgen_setting_noiseparams, core.set_mapgen_setting, and core.set_mapgen_setting_noiseparams. - minetest.get/set_mapgen_params are deprecated by the above new functions. - It is now possible to view and modify any arbitrary mapgen setting from a mod, rather than the base MapgenParams structure. - MapgenSpecificParams has been removed.
* Mapgen: Optimize biomemap creation by combining with generateBiomeskwolekr2016-06-05
|
* Mapgen: Combine dungeon generation codekwolekr2016-05-27
|
* Mapgen: Deduplicate common constructor codekwolekr2016-05-27
|
* Mapgen: Remove calculateNoise from most mapgenskwolekr2016-05-27
| | | | | | This commit moves noise calculation to the functions where the noise is actually required, increasing the separation of concerns and level of interdependency for each mapgen method. Valleys Mapgen is left unmodified.
* Cavegen: Move V5-style caves to CavesNoiseIntersectionkwolekr2016-05-27
|
* Fix MgStoneType and BiomeType enum nameskwolekr2016-05-27
|
* Mapgen: Combine generateBiomes, dustTopNodes, and generateCaveskwolekr2016-05-27
| | | | | This commit condenses the above methods into a single implementation used by V7, V5, Flat, Fractal, and Valleys mapgens and introduces MapgenBasic.
* Move biome calculation to BiomeGenkwolekr2016-05-27
| | | | | | | BiomeGen defines an interface that, given a set of BiomeParams, computes biomes for a given area using the algorithm implemented by that specific BiomeGen. This abstracts away the old system where each mapgen supplied the noises required for biome generation.
* Mapgen: Make 3D noise tunnels' width settableparamat2016-04-28
| | | | | | Correct parameter names mg_valleys to mgvalleys Remove biome NoiseParams from MapgenValleysParams Improve format of parameter code
* Mgv5: Optimise tunnels, add biome material in entrancesparamat2016-04-12
| | | | | | Place biome top node on tunnel entrance floor Instead of doing nothing at node_max.Y + 1 use 1-down overgeneration for tunnel generation and noisemaps
* Mapgen: Fix light in tunnels at mapchunk bordersparamat2016-03-14
| | | | | | Don't excavate the overgenerated stone at node_max.Y + 1, this creates a 'roof' over the tunnel, preventing light in tunnels at mapchunk borders when generating mapchunks upwards.
* FindSpawnPos: Let mapgens decide what spawn altitude is suitableparamat2016-02-09
| | | | | | | | | | | | To avoid spawn search failing in new specialised mapgens Increase spawn search range to 4000 nodes Add getSpawnLevelAtPoint() functions to EmergeManager, class Mapgen and all mapgens Remove getGroundLevelAtPoint() functions from all mapgens except mgv6 (possibly to be re-added later in the correct form to return actual ground level) Make mgvalleys flag names consistent with other mapgens Remove now unused 'vertical spawn range' setting
* Mgv5/v7/flat/fractal: Move tunnel noise calculation into generateCavesparamat2016-02-02
| | | | | | | | | | Tunnel 3D noises are only calculated when solid terrain is present in mapchunk, avoiding large amounts of unnecessary calculations Change 'int' to 's16' in calculateNoise Change 'i' to 'vi' for voxelmanip indexes for consistency Keep 'u32 index3d' local to a smaller part of tunnel code Mgv7: Don't call CaveV7 if no solid terrain in mapchunk Give 'open' bool a more descriptive name
* Mapgen: Various fixes and improvementsparamat2016-01-11
| | | | | | | | | | | | | Lua_api.txt: Document 'minetest.registered_biomes' Minimal: Remove 'mapgen_air' alias Cavegen: Add fallback node for 'mapgen_ice' Dungeongen: Add fallback node for 'mapgen_river_water_source' Mgv5: Remove unnecessary '#include util/directiontables.h' Add missing 'this->'s in makeChunk() Mgv6: Edit empty line formatting Remove leading spaces in makeChunk() Add missing spaces after 'for' and 'if' Mgv7: Edit empty line formatting
* Mgv5/v7/flat/fractal: More large pseudorandom cavesparamat2015-12-02
| | | | Mgv7/flat/fractal: Reduce tunnel noise spreads to 96
* Mapgen: Add global 'decorations' flagparamat2015-11-21
| | | | | | | | | | Flag is set by default in MapgenParams The global 'trees' flag remains but is now undocumented and unset by default in MapgenParams Add mgv6_spflag 'trees' set by default in defaultsettings.cpp to affect new worlds only This is automatically backwards compatible for existing worlds
* Mapgen: Use mapgen-specific names for constants in headersparamat2015-10-09
| | | | | Update copyright years in all mapgens Add myself to copyright notices in mgv5 and mgv7
* Mgv5: getGroundLevelAtPoint searches a larger rangeparamat2015-10-05
|
* Define and use limit constants for Irrlicht fixed-width typeskwolekr2015-10-04
|
* Mgv5/6/7: Re-add #include profiler.h as commented-out optionparamat2015-09-19
|
* Add map limit config optionrubenwardy2015-08-02
|
* Remove profiler.h include where it's not needed. Remove some unreachable and ↵Loic Blot2015-07-21
| | | | very old code
* Mgv5/mgv7 caves: Remove sand found in underground tunnelsparamat2015-06-27
| | | | | Add missing check for max_stone_y to mgv5 cavegen Tunnels now carve through sand below water_level
* Mapgen objects: Enable heatmap and humidmap for all biome api mapgensparamat2015-06-20
|
* Biome API: Add noise defined biome blendparamat2015-06-18
|
* Biome API: Enable biome generation to lower world limitparamat2015-06-15
| | | | Decorations: Remove lower limit of water level for placement
* Mgv5/mgv7: Trigger biome recalculation at underwater surfacesparamat2015-06-03
|
* Mgv5/v7: Fix generateBiomes biome recalculation logic Biomegen down to y = ↵paramat2015-05-23
| | | | -192 for mgv5 deep oceans. Improve code
* Mapgen v5/v7: Detect sandstone, enable sandstone brick dungeonsparamat2015-05-17
|
* Mapgen v5/6/7: Cleanup node resolver and aliasesparamat2015-05-12
|
* Mgv5: Fix above/below ground spawn when water level is alteredparamat2015-04-16
|
* Mgv5/v7: Add check for water for deciding biome node stabilityparamat2015-04-16
|
* Cavegen: Remove now unnecessary checks for water, lava, iceparamat2015-04-07
| | | | | | | Remove large cave checks for air Mgv5/mgv7:Add is_ground_content checks to 3d noise tunnels More large caves Shorten lines
* Move globals from main.cpp to more sane locationsCraig Robbins2015-04-01
| | | | | | | | | | | | Move debug streams to log.cpp|h Move GUI-related globals to clientlauncher Move g_settings and g_settings_path to settings.cpp|h Move g_menuclouds to clouds.cpp|h Move g_profiler to profiler.cpp|h
* GenElementManager: Pass opaque handles to Lua and rename to ObjDefManagerkwolekr2015-03-31
| | | | Add core.clear_registered_schematics() and refactor schematics somewhat
* Mgv7: 1 up , 1 down overgeneration for chunk border continuityparamat2015-03-22
| | | | | | Fixes biome layer at y = 47 when base/alt terrain exceeds it Also fixes missing dust glitch at y = 47 Mgv5/mgv7:Cleanup code
* Mgv5/mgv7: Sprinkle dust from full_node_max.Y if chunk above is generatedparamat2015-03-19
| | | | Mgv5: Add large_cave_depth constant
* Mgv5/mgv7: Add desert temples if desert stone detected in mapchunkparamat2015-03-12
|
* Heightmaps: Fix uninitialised values in mgv5/mgv6. findGroundLevel: Return ↵paramat2015-03-08
| | | | -MAP_GENERATION_LIMIT if surface not found
* Respect game mapgen flags and save world noise paramsngosang2015-03-07
|
* For usages of assert() that are meant to persist in Release builds (when ↵Craig Robbins2015-03-07
| | | | NDEBUG is defined), replace those usages with persistent alternatives
* Fix mapgen using unitialised height map valuesCraig Robbins2015-03-06
|
* Cavegen, mgv5: Cleanup codeparamat2015-03-04
| | | | | Conf.example: Update mgv5 mapgen params Mgv7: Lava caves only below -256
* Biome API: Re-calculate biome at every surface in a mapchunk columnparamat2015-02-26
|
* Mgv5: Remove blobgen. Remove crumble and wetness noisesparamat2015-02-21
| | | | | | Add large pseudorandom caves below -256 De-link terrain level from water_level Cavegen: Mgv5 large cave code
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "settings.h" #include "irrlichttypes_bloated.h" #include "exceptions.h" #include "threading/mutex_auto_lock.h" #include "util/strfnd.h" #include <iostream> #include <fstream> #include <sstream> #include "debug.h" #include "log.h" #include "util/serialize.h" #include "filesys.h" #include "noise.h" #include <cctype> #include <algorithm> Settings *g_settings = nullptr; // Populated in main() 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 *Settings::createLayer(SettingsLayer sl, const std::string &end_tag) { if ((int)sl < 0 || sl >= SL_TOTAL_COUNT) throw BaseException("Invalid settings layer"); Settings *&pos = s_layers[(size_t)sl]; if (pos) throw BaseException("Setting layer " + std::to_string(sl) + " already exists"); pos = new Settings(end_tag); pos->m_settingslayer = sl; if (sl == SL_GLOBAL) g_settings = pos; return pos; } Settings *Settings::getLayer(SettingsLayer sl) { sanity_check((int)sl >= 0 && sl < SL_TOTAL_COUNT); return s_layers[(size_t)sl]; } 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; clearNoLock(); } Settings & Settings::operator = (const Settings &other) { if (&other == this) 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()); MutexAutoLock lock(m_mutex); MutexAutoLock lock2(other.m_mutex); clearNoLock(); m_settings = other.m_settings; m_callbacks = other.m_callbacks; return *this; } bool Settings::checkNameValid(const std::string &name) { bool valid = name.find_first_of("=\"{}#") == std::string::npos; if (valid) valid = std::find_if(name.begin(), name.end(), ::isspace) == name.end(); if (!valid) { errorstream << "Invalid setting name \"" << name << "\"" << std::endl; return false; } return true; } bool Settings::checkValueValid(const std::string &value) { if (value.substr(0, 3) == "\"\"\"" || value.find("\n\"\"\"") != std::string::npos) { errorstream << "Invalid character sequence '\"\"\"' found in" " setting value!" << std::endl; return false; } return true; } std::string Settings::getMultiline(std::istream &is, size_t *num_lines) { size_t lines = 1; std::string value; std::string line; while (is.good()) { lines++; std::getline(is, line); if (line == "\"\"\"") break; value += line; value.push_back('\n'); } size_t len = value.size(); if (len) value.erase(len - 1); if (num_lines) *num_lines = lines; return value; } bool Settings::readConfigFile(const char *filename) { std::ifstream is(filename); if (!is.good()) return false; return parseConfigLines(is); } bool Settings::parseConfigLines(std::istream &is) { MutexAutoLock lock(m_mutex); std::string line, name, value; while (is.good()) { std::getline(is, line); SettingsParseEvent event = parseConfigObject(line, name, value); switch (event) { case SPE_NONE: case SPE_INVALID: case SPE_COMMENT: break; case SPE_KVPAIR: m_settings[name] = SettingsEntry(value); break; case SPE_END: return true; case SPE_GROUP: { Settings *group = new Settings("}"); if (!group->parseConfigLines(is)) { delete group; return false; } m_settings[name] = SettingsEntry(group); break; } case SPE_MULTILINE: m_settings[name] = SettingsEntry(getMultiline(is)); break; } } // false (failure) if end tag not found return m_end_tag.empty(); } void Settings::writeLines(std::ostream &os, u32 tab_depth) const { MutexAutoLock lock(m_mutex); for (const auto &setting_it : m_settings) printEntry(os, setting_it.first, setting_it.second, tab_depth); // For groups this must be "}" ! if (!m_end_tag.empty()) { for (u32 i = 0; i < tab_depth; i++) os << "\t"; os << m_end_tag << "\n"; } } void Settings::printEntry(std::ostream &os, const std::string &name, const SettingsEntry &entry, u32 tab_depth) { for (u32 i = 0; i != tab_depth; i++) os << "\t"; if (entry.is_group) { os << name << " = {\n"; entry.group->writeLines(os, tab_depth + 1); // Closing bracket handled by writeLines } else { os << name << " = "; if (entry.value.find('\n') != std::string::npos) os << "\"\"\"\n" << entry.value << "\n\"\"\"\n"; else os << entry.value << "\n"; } } bool Settings::updateConfigObject(std::istream &is, std::ostream &os, u32 tab_depth) { SettingEntries::const_iterator it; std::set<std::string> present_entries; std::string line, name, value; bool was_modified = false; bool end_found = false; // Add any settings that exist in the config file with the current value // in the object if existing while (is.good() && !end_found) { std::getline(is, line); SettingsParseEvent event = parseConfigObject(line, name, value); switch (event) { case SPE_END: // Skip end tag. Append later. end_found = true; break; case SPE_MULTILINE: value = getMultiline(is); /* FALLTHROUGH */ case SPE_KVPAIR: it = m_settings.find(name); if (it != m_settings.end() && (it->second.is_group || it->second.value != value)) { printEntry(os, name, it->second, tab_depth); was_modified = true; } else if (it == m_settings.end()) { // Remove by skipping was_modified = true; break; } else { os << line << "\n"; if (event == SPE_MULTILINE) os << value << "\n\"\"\"\n"; } present_entries.insert(name); break; case SPE_GROUP: it = m_settings.find(name); if (it != m_settings.end() && it->second.is_group) { os << line << "\n"; sanity_check(it->second.group != NULL); was_modified |= it->second.group->updateConfigObject(is, os, tab_depth + 1); } else if (it == m_settings.end()) { // Remove by skipping was_modified = true; Settings removed_group("}"); // Move 'is' to group end std::stringstream ss; removed_group.updateConfigObject(is, ss, tab_depth + 1); break; } else { printEntry(os, name, it->second, tab_depth); was_modified = true; } present_entries.insert(name); break; default: os << line << (is.eof() ? "" : "\n"); break; } } if (!line.empty() && is.eof()) os << "\n"; // Add any settings in the object that don't exist in the config file yet for (it = m_settings.begin(); it != m_settings.end(); ++it) { if (present_entries.find(it->first) != present_entries.end()) continue; printEntry(os, it->first, it->second, tab_depth); was_modified = true; } // Append ending tag if (!m_end_tag.empty()) { os << m_end_tag << "\n"; was_modified |= !end_found; } return was_modified; } bool Settings::updateConfigFile(const char *filename) { MutexAutoLock lock(m_mutex); std::ifstream is(filename); std::ostringstream os(std::ios_base::binary); bool was_modified = updateConfigObject(is, os); is.close(); if (!was_modified) return true; if (!fs::safeWriteToFile(filename, os.str())) { errorstream << "Error writing configuration file: \"" << filename << "\"" << std::endl; return false; } return true; } bool Settings::parseCommandLine(int argc, char *argv[], std::map<std::string, ValueSpec> &allowed_options) { int nonopt_index = 0; for (int i = 1; i < argc; i++) { std::string arg_name = argv[i]; if (arg_name.substr(0, 2) != "--") { // If option doesn't start with -, read it in as nonoptX if (arg_name[0] != '-'){ std::string name = "nonopt"; name += itos(nonopt_index); set(name, arg_name); nonopt_index++; continue; } errorstream << "Invalid command-line parameter \"" << arg_name << "\": --<option> expected." << std::endl; return false; } std::string name = arg_name.substr(2); std::map<std::string, ValueSpec>::iterator n; n = allowed_options.find(name); if (n == allowed_options.end()) { errorstream << "Unknown command-line parameter \"" << arg_name << "\"" << std::endl; return false; } ValueType type = n->second.type; std::string value; if (type == VALUETYPE_FLAG) { value = "true"; } else { if ((i + 1) >= argc) { errorstream << "Invalid command-line parameter \"" << name << "\": missing value" << std::endl; return false; } value = argv[++i]; } set(name, value); } return true; } /*********** * Getters * ***********/ 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; } const SettingsEntry &Settings::getEntry(const std::string &name) const { { MutexAutoLock lock(m_mutex); SettingEntries::const_iterator n; if ((n = m_settings.find(name)) != m_settings.end()) return n->second; } if (auto parent = getParent()) return parent->getEntry(name); throw SettingNotFoundException("Setting [" + name + "] not found."); } Settings *Settings::getGroup(const std::string &name) const { const SettingsEntry &entry = getEntry(name); if (!entry.is_group) throw SettingNotFoundException("Setting [" + name + "] is not a group."); return entry.group; } const std::string &Settings::get(const std::string &name) const { const SettingsEntry &entry = getEntry(name); if (entry.is_group) throw SettingNotFoundException("Setting [" + name + "] is a group."); return entry.value; } bool Settings::getBool(const std::string &name) const { return is_yes(get(name)); } u16 Settings::getU16(const std::string &name) const { return stoi(get(name), 0, 65535); } s16 Settings::getS16(const std::string &name) const { return stoi(get(name), -32768, 32767); } u32 Settings::getU32(const std::string &name) const { return (u32) stoi(get(name)); } s32 Settings::getS32(const std::string &name) const { return stoi(get(name)); } float Settings::getFloat(const std::string &name) const { return stof(get(name)); } u64 Settings::getU64(const std::string &name) const { u64 value = 0; std::string s = get(name); std::istringstream ss(s); ss >> value; return value; } v2f Settings::getV2F(const std::string &name) const { v2f value; Strfnd f(get(name)); f.next("("); value.X = stof(f.next(",")); value.Y = stof(f.next(")")); return value; } v3f Settings::getV3F(const std::string &name) const { v3f value; Strfnd f(get(name)); f.next("("); value.X = stof(f.next(",")); value.Y = stof(f.next(",")); value.Z = stof(f.next(")")); return value; } u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc, u32 *flagmask) const { u32 flags = 0; // Read default value (if there is any) if (auto parent = getParent()) flags = parent->getFlagStr(name, flagdesc, flagmask); // Apply custom flags "on top" if (m_settings.find(name) != m_settings.end()) { std::string value = get(name); u32 flags_user; u32 mask_user = U32_MAX; flags_user = std::isdigit(value[0]) ? stoi(value) // Override default : readFlagString(value, flagdesc, &mask_user); flags &= ~mask_user; flags |= flags_user; if (flagmask) *flagmask |= mask_user; } return flags; } bool Settings::getNoiseParams(const std::string &name, NoiseParams &np) const { if (getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np)) return true; if (auto parent = getParent()) return parent->getNoiseParams(name, np); return false; } bool Settings::getNoiseParamsFromValue(const std::string &name, NoiseParams &np) const { std::string value; if (!getNoEx(name, value)) return false; // Format: f32,f32,(f32,f32,f32),s32,s32,f32[,f32] Strfnd f(value); np.offset = stof(f.next(",")); np.scale = stof(f.next(",")); f.next("("); np.spread.X = stof(f.next(",")); np.spread.Y = stof(f.next(",")); np.spread.Z = stof(f.next(")")); f.next(","); np.seed = stoi(f.next(",")); np.octaves = stoi(f.next(",")); np.persist = stof(f.next(",")); std::string optional_params = f.next(""); if (!optional_params.empty()) np.lacunarity = stof(optional_params); return true; } bool Settings::getNoiseParamsFromGroup(const std::string &name, NoiseParams &np) const { Settings *group = NULL; if (!getGroupNoEx(name, group)) return false; group->getFloatNoEx("offset", np.offset); group->getFloatNoEx("scale", np.scale); group->getV3FNoEx("spread", np.spread); group->getS32NoEx("seed", np.seed); group->getU16NoEx("octaves", np.octaves); group->getFloatNoEx("persistence", np.persist); group->getFloatNoEx("lacunarity", np.lacunarity); np.flags = 0; if (!group->getFlagStrNoEx("flags", np.flags, flagdesc_noiseparams)) np.flags = NOISE_FLAG_DEFAULTS; return true; } bool Settings::exists(const std::string &name) const { MutexAutoLock lock(m_mutex); if (m_settings.find(name) != m_settings.end()) return true; if (auto parent = getParent()) return parent->exists(name); return false; } std::vector<std::string> Settings::getNames() const { MutexAutoLock lock(m_mutex); std::vector<std::string> names; names.reserve(m_settings.size()); for (const auto &settings_it : m_settings) { names.push_back(settings_it.first); } return names; } /*************************************** * Getters that don't throw exceptions * ***************************************/ bool Settings::getGroupNoEx(const std::string &name, Settings *&val) const { try { val = getGroup(name); return true; } catch (SettingNotFoundException &e) { return false; } } bool Settings::getNoEx(const std::string &name, std::string &val) const { try { val = get(name); return true; } catch (SettingNotFoundException &e) { return false; } } bool Settings::getFlag(const std::string &name) const { try { return getBool(name); } catch(SettingNotFoundException &e) { return false; } } bool Settings::getFloatNoEx(const std::string &name, float &val) const { try { val = getFloat(name); return true; } catch (SettingNotFoundException &e) { return false; } } bool Settings::getU16NoEx(const std::string &name, u16 &val) const { try { val = getU16(name); return true; } catch (SettingNotFoundException &e) { return false; } } bool Settings::getS16NoEx(const std::string &name, s16 &val) const { try { val = getS16(name); return true; } catch (SettingNotFoundException &e) { return false; } } bool Settings::getS32NoEx(const std::string &name, s32 &val) const { try { val = getS32(name); return true; } catch (SettingNotFoundException &e) { return false; } } bool Settings::getU64NoEx(const std::string &name, u64 &val) const { try { val = getU64(name); return true; } catch (SettingNotFoundException &e) { return false; } } bool Settings::getV2FNoEx(const std::string &name, v2f &val) const { try { val = getV2F(name); return true; } catch (SettingNotFoundException &e) { return false; } } bool Settings::getV3FNoEx(const std::string &name, v3f &val) const { try { val = getV3F(name); return true; } catch (SettingNotFoundException &e) { return false; } } bool Settings::getFlagStrNoEx(const std::string &name, u32 &val, const FlagDesc *flagdesc) const { if (!flagdesc) { if (!(flagdesc = getFlagDescFallback(name))) return false; // Not found } try { val = getFlagStr(name, flagdesc, nullptr); return true; } catch (SettingNotFoundException &e) { return false; } } /*********** * Setters * ***********/ bool Settings::setEntry(const std::string &name, const void *data, bool set_group) { if (!checkNameValid(name)) return false; if (!set_group && !checkValueValid(*(const std::string *)data)) return false; Settings *old_group = NULL; { MutexAutoLock lock(m_mutex); SettingsEntry &entry = m_settings[name]; old_group = entry.group; entry.value = set_group ? "" : *(const std::string *)data; entry.group = set_group ? *(Settings **)data : NULL; entry.is_group = set_group; if (set_group) entry.group->m_end_tag = "}"; } delete old_group; return true; } bool Settings::set(const std::string &name, const std::string &value) { if (!setEntry(name, &value, false)) return false; doCallbacks(name); return true; } // TODO: Remove this function bool Settings::setDefault(const std::string &name, const std::string &value) { return getLayer(SL_DEFAULTS)->set(name, value); } bool Settings::setGroup(const std::string &name, const Settings &group) { // Settings must own the group pointer // avoid double-free by copying the source Settings *copy = new Settings(); *copy = group; return setEntry(name, &copy, true); } bool Settings::setBool(const std::string &name, bool value) { return set(name, value ? "true" : "false"); } bool Settings::setS16(const std::string &name, s16 value) { return set(name, itos(value)); } bool Settings::setU16(const std::string &name, u16 value) { return set(name, itos(value)); } bool Settings::setS32(const std::string &name, s32 value) { return set(name, itos(value)); } bool Settings::setU64(const std::string &name, u64 value) { std::ostringstream os; os << value; return set(name, os.str()); } bool Settings::setFloat(const std::string &name, float value) { return set(name, ftos(value)); } bool Settings::setV2F(const std::string &name, v2f value) { std::ostringstream os; os << "(" << value.X << "," << value.Y << ")"; return set(name, os.str()); } bool Settings::setV3F(const std::string &name, v3f value) { std::ostringstream os; os << "(" << value.X << "," << value.Y << "," << value.Z << ")"; return set(name, os.str()); } bool Settings::setFlagStr(const std::string &name, u32 flags, const FlagDesc *flagdesc, u32 flagmask) { if (!flagdesc) { if (!(flagdesc = getFlagDescFallback(name))) return false; // Not found } return set(name, writeFlagString(flags, flagdesc, flagmask)); } bool Settings::setNoiseParams(const std::string &name, const NoiseParams &np) { Settings *group = new Settings; group->setFloat("offset", np.offset); group->setFloat("scale", np.scale); group->setV3F("spread", np.spread); group->setS32("seed", np.seed); group->setU16("octaves", np.octaves); group->setFloat("persistence", np.persist); group->setFloat("lacunarity", np.lacunarity); group->setFlagStr("flags", np.flags, flagdesc_noiseparams, np.flags); return setEntry(name, &group, true); } bool Settings::remove(const std::string &name) { // Lock as short as possible, unlock before doCallbacks() m_mutex.lock(); SettingEntries::iterator it = m_settings.find(name); if (it != m_settings.end()) { delete it->second.group; m_settings.erase(it); m_mutex.unlock(); doCallbacks(name); return true; } m_mutex.unlock(); return false; } SettingsParseEvent Settings::parseConfigObject(const std::string &line, std::string &name, std::string &value) { std::string trimmed_line = trim(line); if (trimmed_line.empty()) return SPE_NONE; if (trimmed_line[0] == '#') return SPE_COMMENT; if (trimmed_line == m_end_tag) return SPE_END; size_t pos = trimmed_line.find('='); if (pos == std::string::npos) return SPE_INVALID; name = trim(trimmed_line.substr(0, pos)); value = trim(trimmed_line.substr(pos + 1)); if (value == "{") return SPE_GROUP; if (value == "\"\"\"") return SPE_MULTILINE; return SPE_KVPAIR; } void Settings::clearNoLock() { for (SettingEntries::const_iterator it = m_settings.begin(); it != m_settings.end(); ++it) delete it->second.group; m_settings.clear(); } void Settings::setDefault(const std::string &name, const FlagDesc *flagdesc, u32 flags) { s_flags[name] = flagdesc; setDefault(name, writeFlagString(flags, flagdesc, U32_MAX)); } const FlagDesc *Settings::getFlagDescFallback(const std::string &name) const { auto it = s_flags.find(name); return it == s_flags.end() ? nullptr : it->second; } void Settings::registerChangedCallback(const std::string &name, SettingsChangedCallback cbf, void *userdata) { MutexAutoLock lock(m_callback_mutex); m_callbacks[name].emplace_back(cbf, userdata); } void Settings::deregisterChangedCallback(const std::string &name, SettingsChangedCallback cbf, void *userdata) { MutexAutoLock lock(m_callback_mutex); SettingsCallbackMap::iterator it_cbks = m_callbacks.find(name); if (it_cbks != m_callbacks.end()) { SettingsCallbackList &cbks = it_cbks->second; SettingsCallbackList::iterator position = std::find(cbks.begin(), cbks.end(), std::make_pair(cbf, userdata)); if (position != cbks.end()) cbks.erase(position); } } void Settings::removeSecureSettings() { for (const auto &name : getNames()) { if (name.compare(0, 7, "secure.") != 0) continue; errorstream << "Secure setting " << name << " isn't allowed, so was ignored." << std::endl; remove(name); } } void Settings::doCallbacks(const std::string &name) const { MutexAutoLock lock(m_callback_mutex); SettingsCallbackMap::const_iterator it_cbks = m_callbacks.find(name); if (it_cbks != m_callbacks.end()) { SettingsCallbackList::const_iterator it; for (it = it_cbks->second.begin(); it != it_cbks->second.end(); ++it) (it->first)(name, it->second); } }