add generator definition file (.toml)

This commit is contained in:
MihailRis 2024-09-26 22:26:01 +03:00
parent d839da7dab
commit 3d478aef08
12 changed files with 76 additions and 54 deletions

View File

@ -0,0 +1,4 @@
# 1 - temperature
# 2 - humidity
biome_parameters = 2
sea_level = 64

View File

@ -1,9 +1,3 @@
sea_level = 64
-- 1 - temperature
-- 2 - humidity
biome_parameters = 2
biomes = json.parse(file.read("base:generators/demo/biomes.json"))
function place_structures(x, z, w, d, seed, hmap)

View File

@ -0,0 +1 @@
biome_parameters = 0

View File

@ -1,5 +1,3 @@
biome_parameters = 0
biomes = {flat = {
parameters = {},
layers = {
@ -9,7 +7,3 @@ biomes = {flat = {
{height=-1, block="core:obstacle"}
}
}}
function generate_heightmap(x, y, w, h, seed)
return Heightmap(w, h)
end

View File

@ -49,7 +49,7 @@ static void detect_defs(
if (name[0] == '_') {
continue;
}
if (fs::is_regular_file(file) && file.extension() == ".json") {
if (fs::is_regular_file(file) && files::is_data_file(file)) {
detected.push_back(prefix.empty() ? name : prefix + ":" + name);
} else if (fs::is_directory(file)) {
detect_defs(file, name, detected);

View File

@ -66,14 +66,20 @@ void ContentLoader::loadGenerator(
) {
auto packDir = pack->folder;
auto generatorsDir = packDir / GENERATORS_DIR;
auto folder = generatorsDir / fs::u8path(name);
auto generatorFile = generatorsDir / fs::u8path(name + ".lua");
auto generatorFile = generatorsDir / fs::u8path(name + ".toml");
if (!fs::exists(generatorFile)) {
return;
}
auto map = files::read_toml(generatorsDir / fs::u8path(name + ".toml"));
map.at("biome_parameters").get(def.biomeParameters);
map.at("sea_level").get(def.seaLevel);
auto folder = generatorsDir / fs::u8path(name);
auto scriptFile = folder / fs::u8path("script.lua");
auto structuresFile = folder / STRUCTURES_FILE;
if (fs::exists(structuresFile)) {
load_structures(def, structuresFile);
}
def.script = scripting::load_generator(generatorFile);
def.script = scripting::load_generator(def, scriptFile);
}

View File

@ -159,3 +159,36 @@ std::vector<std::string> files::read_list(const fs::path& filename) {
}
return lines;
}
#include <map>
#include "coders/json.hpp"
#include "coders/toml.hpp"
using DecodeFunc = dv::value(*)(std::string_view, std::string_view);
static std::map<fs::path, DecodeFunc> data_decoders {
{fs::u8path(".json"), json::parse},
{fs::u8path(".toml"), toml::parse},
};
bool files::is_data_file(const fs::path& file) {
return is_data_interchange_format(file.extension());
}
bool files::is_data_interchange_format(const fs::path& ext) {
return data_decoders.find(ext) != data_decoders.end();
}
dv::value files::read_object(const fs::path& file) {
const auto& found = data_decoders.find(file.extension());
if (found == data_decoders.end()) {
throw std::runtime_error("unknown file format");
}
auto text = read_string(file);
try {
return found->second(file.u8string(), text);
} catch (const parsing_error& err) {
throw std::runtime_error(err.errorLog());
}
}

View File

@ -66,4 +66,8 @@ namespace files {
dv::value read_binary_json(const fs::path& file);
dv::value read_toml(const fs::path& file);
std::vector<std::string> read_list(const fs::path& file);
bool is_data_file(const fs::path& file);
bool is_data_interchange_format(const fs::path& ext);
dv::value read_object(const fs::path& file);
}

View File

@ -33,6 +33,7 @@ class LevelController;
class Entity;
struct EntityDef;
class GeneratorScript;
struct GeneratorDef;
namespace scripting {
extern Engine* engine;
@ -147,6 +148,7 @@ namespace scripting {
void load_entity_component(const std::string& name, const fs::path& file);
std::unique_ptr<GeneratorScript> load_generator(
const GeneratorDef& def,
const fs::path& file
);

View File

@ -16,20 +16,17 @@
#include "util/timeutil.hpp"
class LuaGeneratorScript : public GeneratorScript {
const GeneratorDef& def;
scriptenv env;
std::vector<Biome> biomes;
uint biomeParameters;
uint seaLevel;
public:
LuaGeneratorScript(
const GeneratorDef& def,
scriptenv env,
std::vector<Biome> biomes,
uint biomeParameters,
uint seaLevel)
: env(std::move(env)),
biomes(std::move(biomes)),
biomeParameters(biomeParameters),
seaLevel(seaLevel)
std::vector<Biome> biomes)
: def(def),
env(std::move(env)),
biomes(std::move(biomes))
{}
std::shared_ptr<Heightmap> generateHeightmap(
@ -56,6 +53,7 @@ public:
) override {
std::vector<std::shared_ptr<Heightmap>> maps;
uint biomeParameters = def.biomeParameters;
auto L = lua::get_main_thread();
lua::pushenv(L, *env);
if (lua::getfield(L, "generate_biome_parameters")) {
@ -79,7 +77,6 @@ public:
}
std::vector<StructurePlacement> placeStructures(
const GeneratorDef& def,
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed,
const std::shared_ptr<Heightmap>& heightmap
) override {
@ -153,14 +150,6 @@ public:
const std::vector<Biome>& getBiomes() const override {
return biomes;
}
uint getBiomeParameters() const override {
return biomeParameters;
}
uint getSeaLevel() const override {
return seaLevel;
}
};
static BlocksLayer load_layer(
@ -273,7 +262,7 @@ static inline Biome load_biome(
}
std::unique_ptr<GeneratorScript> scripting::load_generator(
const fs::path& file
const GeneratorDef& def, const fs::path& file
) {
auto env = create_environment();
auto L = lua::get_main_thread();
@ -285,24 +274,20 @@ std::unique_ptr<GeneratorScript> scripting::load_generator(
auto root = lua::tovalue(L, -1);
lua::pop(L);
uint biomeParameters = root["biome_parameters"].asInteger();
uint seaLevel = 0;
root.at("sea_level").get(seaLevel);
std::vector<Biome> biomes;
const auto& biomesMap = root["biomes"];
for (const auto& [biomeName, biomeMap] : biomesMap.asObject()) {
try {
biomes.push_back(
load_biome(biomeMap, biomeName, biomeParameters, -2));
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<LuaGeneratorScript>(
def,
std::move(env),
std::move(biomes),
biomeParameters,
seaLevel);
std::move(biomes)
);
}

View File

@ -135,19 +135,12 @@ public:
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0;
virtual std::vector<StructurePlacement> placeStructures(
const GeneratorDef& def,
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed,
const std::shared_ptr<Heightmap>& heightmap) = 0;
/// @brief Get generator biomes
virtual const std::vector<Biome>& getBiomes() const = 0;
/// @return Number of biome parameters, that biome choosing depending on
virtual uint getBiomeParameters() const = 0;
/// @return Sea level (top of seaLayers)
virtual uint getSeaLevel() const = 0;
/// @brief Build the runtime cache
/// @param content built content
virtual void prepare(const GeneratorDef& def, const Content* content) = 0;
@ -169,6 +162,12 @@ struct GeneratorDef {
std::string name;
std::unique_ptr<GeneratorScript> script;
/// @brief Sea level (top of seaLayers)
uint seaLevel = 0;
/// @brief Number of biome parameters, that biome choosing depending on
uint biomeParameters = 0;
std::unordered_map<std::string, size_t> structuresIndices;
std::vector<std::unique_ptr<GeneratingVoxelStructure>> structures;

View File

@ -175,7 +175,7 @@ void WorldGenerator::generateStructures(
const auto& heightmap = prototype.heightmap;
util::concat(prototype.structures, def.script->placeStructures(
def, {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed,
{chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed,
heightmap
));
for (const auto& placement : prototype.structures) {
@ -202,7 +202,7 @@ void WorldGenerator::generateStructures(
}
uint8_t rotation = structsRand.randU32() % 4;
int height = heights[z * CHUNK_W + x] * CHUNK_H;
if (height < def.script->getSeaLevel()) {
if (height < def.seaLevel) {
continue;
}
auto& structure = *def.structures[structureId]->fragments[rotation];
@ -268,7 +268,7 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) {
const auto& prototype = requirePrototype(chunkX, chunkZ);
const auto values = prototype.heightmap->getValues();
uint seaLevel = def.script->getSeaLevel();
uint seaLevel = def.seaLevel;
std::memset(voxels, 0, sizeof(voxel) * CHUNK_VOL);