From 3784b57eeddb6a597ff415fe1ecf815a20274493 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 29 Sep 2024 22:03:37 +0300 Subject: [PATCH] move biomes definition to a json file --- .../base/generators/demo.files/script.lua | 6 - res/generators/default.files/biomes.json | 11 ++ res/generators/default.files/script.lua | 9 - src/content/ContentBuilder.cpp | 2 +- src/content/loading/GeneratorLoader.cpp | 129 ++++++++++++++ src/logic/scripting/lua/libpack.cpp | 8 +- .../scripting/scripting_world_generation.cpp | 165 ++---------------- src/world/generator/GeneratorDef.cpp | 25 +++ src/world/generator/GeneratorDef.hpp | 10 +- src/world/generator/WorldGenerator.cpp | 2 +- 10 files changed, 183 insertions(+), 184 deletions(-) create mode 100644 res/generators/default.files/biomes.json delete mode 100644 res/generators/default.files/script.lua diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index a254c664..8a108964 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -1,5 +1,3 @@ -biomes = json.parse(file.read("base:generators/demo.files/biomes.json")) - function place_structures(x, z, w, d, seed, hmap) local placements = {} for i=1,10 do @@ -41,10 +39,6 @@ end function generate_heightmap(x, y, w, h, seed) -- blocks per dot - -- 8 - linear interpolation is visible, but not so much - -- 4 - high quality, but slower - -- 2 - you really don't need it - -- 1 - please have mercy on your CPU local bpd = 4 local map = _generate_heightmap( math.floor(x/bpd), math.floor(y/bpd), diff --git a/res/generators/default.files/biomes.json b/res/generators/default.files/biomes.json new file mode 100644 index 00000000..baf0bf9b --- /dev/null +++ b/res/generators/default.files/biomes.json @@ -0,0 +1,11 @@ +{ + "flat": { + "parameters": [], + "layers": [ + {"height": -1, "block": "core:obstacle"} + ], + "sea_layers": [ + {"height": -1, "block": "core:obstacle"} + ] + } +} diff --git a/res/generators/default.files/script.lua b/res/generators/default.files/script.lua deleted file mode 100644 index e949a610..00000000 --- a/res/generators/default.files/script.lua +++ /dev/null @@ -1,9 +0,0 @@ -biomes = {flat = { - parameters = {}, - layers = { - {height=-1, block="core:obstacle"}, - }, - sea_layers = { - {height=-1, block="core:obstacle"} - } -}} diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index 2ce7a963..60dbf36e 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -89,7 +89,7 @@ std::unique_ptr ContentBuilder::build() { } for (auto& [name, def] : content->generators.getDefs()) { - def->script->prepare(*def, content.get()); + def->prepare(content.get()); } return content; diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 28ff2f30..90cbc4c1 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -8,8 +8,116 @@ #include "world/generator/VoxelFragment.hpp" #include "debug/Logger.hpp" +static BlocksLayer load_layer( + const dv::value& map, uint& lastLayersHeight, bool& hasResizeableLayer +) { + const auto& name = map["block"].asString(); + int height = map["height"].asInteger(); + bool belowSeaLevel = true; + map.at("below_sea_level").get(belowSeaLevel); + + if (hasResizeableLayer) { + lastLayersHeight += height; + } + if (height == -1) { + if (hasResizeableLayer) { + throw std::runtime_error("only one resizeable layer allowed"); + } + hasResizeableLayer = true; + } + return BlocksLayer {name, height, belowSeaLevel, {}}; +} + +static inline BlocksLayers load_layers( + const dv::value& layersArr, const std::string& fieldname +) { + uint lastLayersHeight = 0; + bool hasResizeableLayer = false; + std::vector layers; + + for (int i = 0; i < layersArr.size(); i++) { + const auto& layerMap = layersArr[i]; + try { + layers.push_back( + load_layer(layerMap, lastLayersHeight, hasResizeableLayer)); + } catch (const std::runtime_error& err) { + throw std::runtime_error( + fieldname+" #"+std::to_string(i)+": "+err.what()); + } + } + return BlocksLayers {std::move(layers), lastLayersHeight}; +} + +static inline BiomeElementList load_biome_element_list( + const dv::value map, + const std::string& chanceName, + const std::string& arrName, + const std::string& nameName +) { + float chance = 0.0f; + map.at(chanceName).get(chance); + std::vector entries; + if (map.has(arrName)) { + const auto& arr = map[arrName]; + for (const auto& entry : arr) { + const auto& name = entry[nameName].asString(); + float weight = entry["weight"].asNumber(); + if (weight <= 0.0f) { + throw std::runtime_error("weight must be positive"); + } + entries.push_back(WeightedEntry {name, weight, {}}); + } + } + std::sort(entries.begin(), entries.end(), std::greater()); + return BiomeElementList(std::move(entries), chance); +} + +static inline BiomeElementList load_plants(const dv::value& biomeMap) { + return load_biome_element_list(biomeMap, "plant_chance", "plants", "block"); +} + +static inline BiomeElementList load_structures(const dv::value map) { + return load_biome_element_list(map, "structure_chance", "structures", "name"); +} + static debug::Logger logger("generator-loader"); +static inline Biome load_biome( + const dv::value& biomeMap, + const std::string& name, + uint parametersCount +) { + std::vector parameters; + + const auto& paramsArr = biomeMap["parameters"]; + if (paramsArr.size() < parametersCount) { + throw std::runtime_error( + std::to_string(parametersCount)+" parameters expected"); + } + for (size_t i = 0; i < parametersCount; i++) { + const auto& paramMap = paramsArr[i]; + float value = paramMap["value"].asNumber(); + float weight = paramMap["weight"].asNumber(); + parameters.push_back(BiomeParameter {value, weight}); + } + + auto plants = load_plants(biomeMap); + auto groundLayers = load_layers(biomeMap["layers"], "layers"); + auto seaLayers = load_layers(biomeMap["sea_layers"], "sea_layers"); + + BiomeElementList structures; + if (biomeMap.has("structures")) { + structures = load_structures(biomeMap); + } + return Biome { + name, + std::move(parameters), + std::move(plants), + std::move(structures), + std::move(groundLayers), + std::move(seaLayers)}; +} + static VoxelStructureMeta load_structure_meta( const std::string& name, const dv::value& config ) { @@ -59,8 +167,21 @@ static void load_structures(GeneratorDef& def, const fs::path& structuresFile) { } static inline const auto STRUCTURES_FILE = fs::u8path("structures.json"); +static inline const auto BIOMES_FILE = fs::u8path("biomes.json"); static inline const auto GENERATORS_DIR = fs::u8path("generators"); +static void load_biomes(GeneratorDef& def, const fs::path& file) { + auto root = files::read_json(file); + for (const auto& [biomeName, biomeMap] : root.asObject()) { + try { + def.biomes.push_back( + load_biome(biomeMap, biomeName, def.biomeParameters)); + } catch (const std::runtime_error& err) { + throw std::runtime_error("biome "+biomeName+": "+err.what()); + } + } +} + void ContentLoader::loadGenerator( GeneratorDef& def, const std::string& full, const std::string& name ) { @@ -81,5 +202,13 @@ void ContentLoader::loadGenerator( if (fs::exists(structuresFile)) { load_structures(def, structuresFile); } + auto biomesFiles = folder / BIOMES_FILE; + if (!fs::exists(biomesFiles)) { + throw std::runtime_error( + BIOMES_FILE.u8string() + + ": file not found (at least one biome required)" + ); + } + load_biomes(def, biomesFiles); def.script = scripting::load_generator(def, scriptFile); } diff --git a/src/logic/scripting/lua/libpack.cpp b/src/logic/scripting/lua/libpack.cpp index bc9bb204..fdd9ae8a 100644 --- a/src/logic/scripting/lua/libpack.cpp +++ b/src/logic/scripting/lua/libpack.cpp @@ -16,11 +16,9 @@ using namespace scripting; static int l_pack_get_folder(lua::State* L) { std::string packName = lua::tostring(L, 1); - if (packName == "core") { - auto folder = engine->getPaths()->getResourcesFolder().u8string() + "/"; - return lua::pushstring(L, folder); - } - for (auto& pack : engine->getContentPacks()) { + auto packs = engine->getAllContentPacks(); + + for (auto& pack : packs) { if (pack.id == packName) { return lua::pushstring(L, pack.folder.u8string() + "/"); } diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index fe0bd8de..28082540 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -12,21 +12,17 @@ #include "voxels/Chunk.hpp" #include "data/dv.hpp" #include "world/generator/GeneratorDef.hpp" -#include "util/stringutil.hpp" #include "util/timeutil.hpp" class LuaGeneratorScript : public GeneratorScript { const GeneratorDef& def; scriptenv env; - std::vector biomes; public: LuaGeneratorScript( const GeneratorDef& def, - scriptenv env, - std::vector biomes) + scriptenv env) : def(def), - env(std::move(env)), - biomes(std::move(biomes)) + env(std::move(env)) {} std::shared_ptr generateHeightmap( @@ -124,143 +120,8 @@ public: } return placements; } - - void prepare(const GeneratorDef& def, const Content* content) override { - for (auto& biome : biomes) { - for (auto& layer : biome.groundLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; - } - for (auto& layer : biome.seaLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; - } - for (auto& plant : biome.plants.entries) { - plant.rt.id = content->blocks.require(plant.name).rt.id; - } - for (auto& structure : biome.structures.entries) { - const auto& found = def.structuresIndices.find(structure.name); - if (found == def.structuresIndices.end()) { - throw std::runtime_error( - "no structure "+util::quote(structure.name)+" found"); - } - structure.rt.id = found->second; - } - } - } - - const std::vector& getBiomes() const override { - return biomes; - } }; -static BlocksLayer load_layer( - const dv::value& map, uint& lastLayersHeight, bool& hasResizeableLayer -) { - const auto& name = map["block"].asString(); - int height = map["height"].asInteger(); - bool belowSeaLevel = true; - map.at("below_sea_level").get(belowSeaLevel); - - if (hasResizeableLayer) { - lastLayersHeight += height; - } - if (height == -1) { - if (hasResizeableLayer) { - throw std::runtime_error("only one resizeable layer allowed"); - } - hasResizeableLayer = true; - } - return BlocksLayer {name, height, belowSeaLevel, {}}; -} - -static inline BlocksLayers load_layers( - const dv::value& layersArr, const std::string& fieldname -) { - uint lastLayersHeight = 0; - bool hasResizeableLayer = false; - std::vector layers; - - for (int i = 0; i < layersArr.size(); i++) { - const auto& layerMap = layersArr[i]; - try { - layers.push_back( - load_layer(layerMap, lastLayersHeight, hasResizeableLayer)); - } catch (const std::runtime_error& err) { - throw std::runtime_error( - fieldname+" #"+std::to_string(i)+": "+err.what()); - } - } - return BlocksLayers {std::move(layers), lastLayersHeight}; -} - -static inline BiomeElementList load_biome_element_list( - const dv::value map, - const std::string& chanceName, - const std::string& arrName, - const std::string& nameName -) { - float chance = 0.0f; - map.at(chanceName).get(chance); - std::vector entries; - if (map.has(arrName)) { - const auto& arr = map[arrName]; - for (const auto& entry : arr) { - const auto& name = entry[nameName].asString(); - float weight = entry["weight"].asNumber(); - if (weight <= 0.0f) { - throw std::runtime_error("weight must be positive"); - } - entries.push_back(WeightedEntry {name, weight, {}}); - } - } - std::sort(entries.begin(), entries.end(), std::greater()); - return BiomeElementList(std::move(entries), chance); -} - -static inline BiomeElementList load_plants(const dv::value& biomeMap) { - return load_biome_element_list(biomeMap, "plant_chance", "plants", "block"); -} - -static inline BiomeElementList load_structures(const dv::value map) { - return load_biome_element_list(map, "structure_chance", "structures", "name"); -} - -static inline Biome load_biome( - const dv::value& biomeMap, - const std::string& name, - uint parametersCount, - int idx -) { - std::vector parameters; - - const auto& paramsArr = biomeMap["parameters"]; - if (paramsArr.size() < parametersCount) { - throw std::runtime_error( - std::to_string(parametersCount)+" parameters expected"); - } - for (size_t i = 0; i < parametersCount; i++) { - const auto& paramMap = paramsArr[i]; - float value = paramMap["value"].asNumber(); - float weight = paramMap["weight"].asNumber(); - parameters.push_back(BiomeParameter {value, weight}); - } - - auto plants = load_plants(biomeMap); - auto groundLayers = load_layers(biomeMap["layers"], "layers"); - auto seaLayers = load_layers(biomeMap["sea_layers"], "sea_layers"); - - BiomeElementList structures; - if (biomeMap.has("structures")) { - structures = load_structures(biomeMap); - } - return Biome { - name, - std::move(parameters), - std::move(plants), - std::move(structures), - std::move(groundLayers), - std::move(seaLayers)}; -} - std::unique_ptr scripting::load_generator( const GeneratorDef& def, const fs::path& file ) { @@ -268,26 +129,20 @@ std::unique_ptr scripting::load_generator( auto L = lua::get_main_thread(); lua::stackguard _(L); - lua::pop(L, load_script(*env, "generator", file)); + if (fs::exists(file)) { + lua::pop(L, load_script(*env, "generator", file)); + } else { + // Use default (empty) script + lua::pop(L, lua::execute(lua::get_main_thread(), *env, "", "")); + } + lua::pushenv(L, *env); auto root = lua::tovalue(L, -1); lua::pop(L); - std::vector biomes; - - const auto& biomesMap = root["biomes"]; - for (const auto& [biomeName, biomeMap] : biomesMap.asObject()) { - try { - biomes.push_back( - load_biome(biomeMap, biomeName, def.biomeParameters, -2)); - } catch (const std::runtime_error& err) { - throw std::runtime_error("biome "+biomeName+": "+err.what()); - } - } return std::make_unique( def, - std::move(env), - std::move(biomes) + std::move(env) ); } diff --git a/src/world/generator/GeneratorDef.cpp b/src/world/generator/GeneratorDef.cpp index 123f5f98..c372cde4 100644 --- a/src/world/generator/GeneratorDef.cpp +++ b/src/world/generator/GeneratorDef.cpp @@ -1,6 +1,9 @@ #include "GeneratorDef.hpp" #include "VoxelFragment.hpp" +#include "content/Content.hpp" +#include "util/stringutil.hpp" +#include "voxels/Block.hpp" GeneratingVoxelStructure::GeneratingVoxelStructure( VoxelStructureMeta meta, @@ -9,3 +12,25 @@ GeneratingVoxelStructure::GeneratingVoxelStructure( GeneratorDef::GeneratorDef(std::string name) : name(std::move(name)) {} + +void GeneratorDef::prepare(const Content* content) { + for (auto& biome : biomes) { + for (auto& layer : biome.groundLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + for (auto& layer : biome.seaLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + for (auto& plant : biome.plants.entries) { + plant.rt.id = content->blocks.require(plant.name).rt.id; + } + for (auto& structure : biome.structures.entries) { + const auto& found = structuresIndices.find(structure.name); + if (found == structuresIndices.end()) { + throw std::runtime_error( + "no structure "+util::quote(structure.name)+" found"); + } + structure.rt.id = found->second; + } + } +} diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 8311e15f..7209b4c4 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -137,13 +137,6 @@ public: virtual std::vector placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap) = 0; - - /// @brief Get generator biomes - virtual const std::vector& getBiomes() const = 0; - - /// @brief Build the runtime cache - /// @param content built content - virtual void prepare(const GeneratorDef& def, const Content* content) = 0; }; struct GeneratingVoxelStructure { @@ -170,7 +163,10 @@ struct GeneratorDef { std::unordered_map structuresIndices; std::vector> structures; + std::vector biomes; GeneratorDef(std::string name); GeneratorDef(const GeneratorDef&) = delete; + + void prepare(const Content* content); }; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index e1c32c65..6c54cb65 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -231,7 +231,7 @@ void WorldGenerator::generateBiomes( } auto biomeParams = def.script->generateParameterMaps( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed); - const auto& biomes = def.script->getBiomes(); + const auto& biomes = def.biomes; auto chunkBiomes = std::make_unique(CHUNK_W*CHUNK_D); for (uint z = 0; z < CHUNK_D; z++) {