add struct Biome & move generator-related scripting to scripting_world_generation.cpp

This commit is contained in:
MihailRis 2024-08-18 22:25:36 +03:00
parent 810519fb4d
commit 685cd414c4
5 changed files with 181 additions and 164 deletions

View File

@ -3,6 +3,7 @@
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include "scripting_commons.hpp"
#include "content/Content.hpp" #include "content/Content.hpp"
#include "content/ContentPack.hpp" #include "content/ContentPack.hpp"
#include "debug/Logger.hpp" #include "debug/Logger.hpp"
@ -24,7 +25,6 @@
#include "util/timeutil.hpp" #include "util/timeutil.hpp"
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
#include "world/generator/GeneratorDef.hpp"
using namespace scripting; using namespace scripting;
@ -39,7 +39,7 @@ const ContentIndices* scripting::indices = nullptr;
BlocksController* scripting::blocks = nullptr; BlocksController* scripting::blocks = nullptr;
LevelController* scripting::controller = nullptr; LevelController* scripting::controller = nullptr;
static void load_script(const fs::path& name, bool throwable) { void scripting::load_script(const fs::path& name, bool throwable) {
auto paths = scripting::engine->getPaths(); auto paths = scripting::engine->getPaths();
fs::path file = paths->getResourcesFolder() / fs::path("scripts") / name; fs::path file = paths->getResourcesFolder() / fs::path("scripts") / name;
std::string src = files::read_string(file); std::string src = files::read_string(file);
@ -52,6 +52,14 @@ static void load_script(const fs::path& name, bool throwable) {
} }
} }
int scripting::load_script(
int env, const std::string& type, const fs::path& file
) {
std::string src = files::read_string(file);
logger.info() << "script (" << type << ") " << file.u8string();
return lua::execute(lua::get_main_thread(), env, src, file.u8string());
}
void scripting::initialize(Engine* engine) { void scripting::initialize(Engine* engine) {
scripting::engine = engine; scripting::engine = engine;
lua::initialize(); lua::initialize();
@ -630,15 +638,6 @@ int scripting::get_values_on_stack() {
return lua::gettop(lua::get_main_thread()); return lua::gettop(lua::get_main_thread());
} }
[[nodiscard]]
static int load_script(
int env, const std::string& type, const fs::path& file
) {
std::string src = files::read_string(file);
logger.info() << "script (" << type << ") " << file.u8string();
return lua::execute(lua::get_main_thread(), env, src, file.u8string());
}
void scripting::load_block_script( void scripting::load_block_script(
const scriptenv& senv, const scriptenv& senv,
const std::string& prefix, const std::string& prefix,
@ -685,155 +684,6 @@ void scripting::load_entity_component(
lua::store_in(L, lua::CHUNKS_TABLE, name); lua::store_in(L, lua::CHUNKS_TABLE, name);
} }
class LuaGeneratorScript : public GeneratorScript {
scriptenv env;
BlocksLayers groundLayers;
BlocksLayers seaLayers;
uint seaLevel;
public:
LuaGeneratorScript(
scriptenv env,
BlocksLayers groundLayers,
BlocksLayers seaLayers,
uint seaLevel)
: env(std::move(env)),
groundLayers(std::move(groundLayers)),
seaLayers(std::move(seaLayers)),
seaLevel(seaLevel)
{}
std::shared_ptr<Heightmap> generateHeightmap(
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed
) override {
auto L = lua::get_main_thread();
lua::pushenv(L, *env);
if (lua::getfield(L, "generate_heightmap")) {
lua::pushivec_stack(L, offset);
lua::pushivec_stack(L, size);
lua::pushinteger(L, seed);
if (lua::call_nothrow(L, 5)) {
auto map = lua::touserdata<lua::LuaHeightmap>(L, -1)->getHeightmap();
lua::pop(L, 2);
return map;
}
}
lua::pop(L);
return std::make_shared<Heightmap>(size.x, size.y);
}
void prepare(const Content* content) override {
for (auto& layer : groundLayers.layers) {
layer.rt.id = content->blocks.require(layer.block).rt.id;
}
for (auto& layer : seaLayers.layers) {
layer.rt.id = content->blocks.require(layer.block).rt.id;
}
}
const BlocksLayers& getGroundLayers() const override {
return groundLayers;
}
const BlocksLayers& getSeaLayers() const override {
return seaLayers;
}
uint getSeaLevel() const override {
return seaLevel;
}
};
static BlocksLayer load_layer(
lua::State* L, int idx, uint& lastLayersHeight, bool& hasResizeableLayer
) {
lua::requirefield(L, "block");
auto name = lua::require_string(L, -1);
lua::pop(L);
lua::requirefield(L, "height");
int height = lua::tointeger(L, -1);
lua::pop(L);
bool belowSeaLevel = true;
if (lua::getfield(L, "below_sea_level")) {
belowSeaLevel = lua::toboolean(L, -1);
lua::pop(L);
}
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(
lua::State* L, const std::string& fieldname
) {
uint lastLayersHeight = 0;
bool hasResizeableLayer = false;
std::vector<BlocksLayer> layers;
if (lua::getfield(L, fieldname)) {
int len = lua::objlen(L, -1);
for (int i = 1; i <= len; i++) {
lua::rawgeti(L, i);
try {
layers.push_back(
load_layer(L, -1, lastLayersHeight, hasResizeableLayer));
} catch (const std::runtime_error& err) {
lua::pop(L, 2);
throw std::runtime_error(
fieldname+" #"+std::to_string(i)+": "+err.what());
}
lua::pop(L);
}
lua::pop(L);
}
return BlocksLayers {std::move(layers), lastLayersHeight};
}
std::unique_ptr<GeneratorScript> scripting::load_generator(
const fs::path& file
) {
auto env = create_environment();
auto L = lua::get_main_thread();
lua::pop(L, load_script(*env, "generator", file));
lua::pushenv(L, *env);
uint seaLevel = 0;
if (lua::getfield(L, "sea_level")) {
seaLevel = lua::tointeger(L, -1);
lua::pop(L);
}
uint lastGroundLayersHeight = 0;
uint lastSeaLayersHeight = 0;
bool hasResizeableGroundLayer = false;
bool hasResizeableSeaLayer = false;
BlocksLayers groundLayers;
BlocksLayers seaLayers;
try {
groundLayers = load_layers(L, "layers");
seaLayers = load_layers(L, "sea_layers");
} catch (const std::runtime_error& err) {
lua::pop(L);
throw std::runtime_error(file.u8string()+": "+err.what());
}
lua::pop(L);
return std::make_unique<LuaGeneratorScript>(
std::move(env),
std::move(groundLayers),
std::move(seaLayers),
seaLevel);
}
void scripting::load_world_script( void scripting::load_world_script(
const scriptenv& senv, const scriptenv& senv,
const std::string& prefix, const std::string& prefix,

View File

@ -0,0 +1,11 @@
#pragma once
#include <string>
#include <filesystem>
namespace scripting {
void load_script(const std::filesystem::path& name, bool throwable);
[[nodiscard]]
int load_script(int env, const std::string& type, const std::filesystem::path& file);
}

View File

@ -0,0 +1,150 @@
#include "scripting.hpp"
#include "scripting_commons.hpp"
#include "typedefs.hpp"
#include "lua/lua_engine.hpp"
#include "lua/lua_custom_types.hpp"
#include "content/Content.hpp"
#include "voxels/Block.hpp"
#include "world/generator/GeneratorDef.hpp"
class LuaGeneratorScript : public GeneratorScript {
scriptenv env;
Biome biome;
uint seaLevel;
public:
LuaGeneratorScript(
scriptenv env,
Biome biome,
uint seaLevel)
: env(std::move(env)),
biome(std::move(biome)),
seaLevel(seaLevel)
{}
std::shared_ptr<Heightmap> generateHeightmap(
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed
) override {
auto L = lua::get_main_thread();
lua::pushenv(L, *env);
if (lua::getfield(L, "generate_heightmap")) {
lua::pushivec_stack(L, offset);
lua::pushivec_stack(L, size);
lua::pushinteger(L, seed);
if (lua::call_nothrow(L, 5)) {
auto map = lua::touserdata<lua::LuaHeightmap>(L, -1)->getHeightmap();
lua::pop(L, 2);
return map;
}
}
lua::pop(L);
return std::make_shared<Heightmap>(size.x, size.y);
}
void prepare(const Content* content) override {
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;
}
}
const Biome& getBiome() const override {
return biome;
}
uint getSeaLevel() const override {
return seaLevel;
}
};
static BlocksLayer load_layer(
lua::State* L, int idx, uint& lastLayersHeight, bool& hasResizeableLayer
) {
lua::requirefield(L, "block");
auto name = lua::require_string(L, -1);
lua::pop(L);
lua::requirefield(L, "height");
int height = lua::tointeger(L, -1);
lua::pop(L);
bool belowSeaLevel = true;
if (lua::getfield(L, "below_sea_level")) {
belowSeaLevel = lua::toboolean(L, -1);
lua::pop(L);
}
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(
lua::State* L, const std::string& fieldname
) {
uint lastLayersHeight = 0;
bool hasResizeableLayer = false;
std::vector<BlocksLayer> layers;
if (lua::getfield(L, fieldname)) {
int len = lua::objlen(L, -1);
for (int i = 1; i <= len; i++) {
lua::rawgeti(L, i);
try {
layers.push_back(
load_layer(L, -1, lastLayersHeight, hasResizeableLayer));
} catch (const std::runtime_error& err) {
lua::pop(L, 2);
throw std::runtime_error(
fieldname+" #"+std::to_string(i)+": "+err.what());
}
lua::pop(L);
}
lua::pop(L);
}
return BlocksLayers {std::move(layers), lastLayersHeight};
}
std::unique_ptr<GeneratorScript> scripting::load_generator(
const fs::path& file
) {
auto env = create_environment();
auto L = lua::get_main_thread();
lua::pop(L, load_script(*env, "generator", file));
lua::pushenv(L, *env);
uint seaLevel = 0;
if (lua::getfield(L, "sea_level")) {
seaLevel = lua::tointeger(L, -1);
lua::pop(L);
}
uint lastGroundLayersHeight = 0;
uint lastSeaLayersHeight = 0;
bool hasResizeableGroundLayer = false;
bool hasResizeableSeaLayer = false;
BlocksLayers groundLayers;
BlocksLayers seaLayers;
try {
groundLayers = load_layers(L, "layers");
seaLayers = load_layers(L, "sea_layers");
} catch (const std::runtime_error& err) {
lua::pop(L);
throw std::runtime_error(file.u8string()+": "+err.what());
}
lua::pop(L);
return std::make_unique<LuaGeneratorScript>(
std::move(env),
Biome {"default", std::move(groundLayers), std::move(seaLayers)},
seaLevel);
}

View File

@ -33,6 +33,12 @@ struct BlocksLayers {
uint lastLayersHeight; uint lastLayersHeight;
}; };
struct Biome {
std::string name;
BlocksLayers groundLayers;
BlocksLayers seaLayers;
};
/// @brief Generator behaviour and settings interface /// @brief Generator behaviour and settings interface
class GeneratorScript { class GeneratorScript {
public: public:
@ -46,8 +52,7 @@ public:
virtual std::shared_ptr<Heightmap> generateHeightmap( virtual std::shared_ptr<Heightmap> generateHeightmap(
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0;
virtual const BlocksLayers& getGroundLayers() const = 0; virtual const Biome& getBiome() const = 0;
virtual const BlocksLayers& getSeaLayers() const = 0;
virtual uint getSeaLevel() const = 0; virtual uint getSeaLevel() const = 0;

View File

@ -56,8 +56,9 @@ void WorldGenerator::generate(
{chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed
); );
auto values = heightmap->getValues(); auto values = heightmap->getValues();
const auto& groundLayers = def.script->getGroundLayers(); const auto& biome = def.script->getBiome();
const auto& seaLayers = def.script->getSeaLayers(); const auto& groundLayers = biome.groundLayers;
const auto& seaLayers = biome.seaLayers;
uint seaLevel = def.script->getSeaLevel(); uint seaLevel = def.script->getSeaLevel();