From 95cf451cc808cf1206c9f749dd5a2769e3ca1186 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 14 Aug 2024 17:12:12 +0300 Subject: [PATCH] add GeneratorScript --- res/generators/{default.lua => flat.lua} | 20 +++------ src/content/ContentLoader.cpp | 32 ++++++++++++-- src/content/ContentLoader.hpp | 4 ++ src/files/util.hpp | 16 +++++++ src/logic/scripting/lua/lua_custom_types.hpp | 36 +++++++++------- src/logic/scripting/lua/lua_engine.cpp | 2 +- .../scripting/lua/lua_type_heightmap.cpp | 31 ++++++------- src/logic/scripting/lua/lua_util.hpp | 8 ++++ src/logic/scripting/scripting.cpp | 43 ++++++++++++++++++- src/logic/scripting/scripting.hpp | 6 +++ src/maths/Heightmap.hpp | 34 +++++++++++++++ src/world/generator/GeneratorDef.hpp | 15 ++++++- 12 files changed, 196 insertions(+), 51 deletions(-) rename res/generators/{default.lua => flat.lua} (60%) create mode 100644 src/files/util.hpp create mode 100644 src/maths/Heightmap.hpp diff --git a/res/generators/default.lua b/res/generators/flat.lua similarity index 60% rename from res/generators/default.lua rename to res/generators/flat.lua index defe9131..2d8c47b4 100644 --- a/res/generators/default.lua +++ b/res/generators/flat.lua @@ -1,24 +1,21 @@ -local W = 256 -local H = 256 - -function generate_heightmap(x, y) - local umap = Heightmap(W, H) - local vmap = Heightmap(W, H) +function generate_heightmap(x, y, w, h) + local umap = Heightmap(w, h) + local vmap = Heightmap(w, h) umap:noise({x+521, y+73}, 0.05, 1, 20.8) umap:noise({x+51, y+75}, 0.05, 1, 21.8) - umap:noise({x+521, y+70}, 0.1, 3, 35.8) + vmap:noise({x+521, y+70}, 0.1, 3, 35.8) vmap:noise({x+95, y+246}, 0.15, 3, 35.8) - local bmap = Heightmap(W, H) + local bmap = Heightmap(w, h) bmap:noise({x+3, y+6}, 0.1, 1, 3) - local map = Heightmap(W, H) + local map = Heightmap(w, h) map:noise({x, y}, 0.06, 5, 0.2, umap, vmap) map:noise({x, y}, 0.12, 6, 0.5, umap, vmap) map:mul(bmap) map:mul(0.7) - local rivermap = Heightmap(W, H) + local rivermap = Heightmap(w, h) rivermap:noise({x+21, y+12}, 0.1, 3) rivermap:abs() rivermap:min(0.5) @@ -31,6 +28,3 @@ function generate_heightmap(x, y) return map end - -local map = generate_heightmap(0, 0) -map:dump("heightmap.png") diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 96a5eadf..fef55cdb 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -376,6 +376,18 @@ void ContentLoader::loadEntity( if (fs::exists(configFile)) loadEntity(def, full, configFile); } + +void ContentLoader::loadGenerator( + GeneratorDef& def, const std::string& full, const std::string& name +) { + auto folder = pack->folder; + auto generatorFile = folder / fs::path("generators/" + name + ".lua"); + if (!fs::exists(generatorFile)) { + return; + } + def.script = scripting::load_generator(generatorFile); +} + void ContentLoader::loadBlock( Block& def, const std::string& full, const std::string& name ) { @@ -450,6 +462,21 @@ void ContentLoader::load() { ); } + fs::path generatorsDir = folder / fs::u8path("generators"); + if (fs::is_directory(generatorsDir)) { + for (const auto& entry : fs::directory_iterator(generatorsDir)) { + const auto& file = entry.path(); + + std::string name = file.stem().u8string(); + auto [packid, full, filename] = + create_unit_id(pack->id, file.stem().u8string()); + + auto& def = builder.generators.create(full); + + loadGenerator(def, full, name); + } + } + if (!fs::is_regular_file(pack->getContentFile())) return; auto root = files::read_json(pack->getContentFile()); @@ -486,7 +513,6 @@ void ContentLoader::load() { if (auto entitiesarr = root->list("entities")) { for (size_t i = 0; i < entitiesarr->size(); i++) { std::string name = entitiesarr->str(i); - auto [packid, full, filename] = create_unit_id(pack->id, name); auto& def = builder.entities.create(full); @@ -499,7 +525,7 @@ void ContentLoader::load() { fs::path materialsDir = folder / fs::u8path("block_materials"); if (fs::is_directory(materialsDir)) { for (const auto& entry : fs::directory_iterator(materialsDir)) { - const fs::path& file = entry.path(); + const auto& file = entry.path(); auto [packid, full, filename] = create_unit_id(pack->id, file.stem().u8string()); loadBlockMaterial( @@ -512,7 +538,7 @@ void ContentLoader::load() { fs::path skeletonsDir = folder / fs::u8path("skeletons"); if (fs::is_directory(skeletonsDir)) { for (const auto& entry : fs::directory_iterator(skeletonsDir)) { - const fs::path& file = entry.path(); + const auto& file = entry.path(); std::string name = pack->id + ":" + file.stem().u8string(); std::string text = files::read_string(file); builder.add( diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index 0673a96d..0a65f640 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -13,6 +13,7 @@ struct BlockMaterial; struct ItemDef; struct EntityDef; struct ContentPack; +struct GeneratorDef; class ContentBuilder; class ContentPackRuntime; @@ -39,6 +40,9 @@ class ContentLoader { void loadEntity( EntityDef& def, const std::string& full, const std::string& name ); + void loadGenerator( + GeneratorDef& def, const std::string& full, const std::string& name + ); static void loadCustomBlockModel(Block& def, dynamic::Map* primitives); static void loadBlockMaterial(BlockMaterial& def, const fs::path& file); diff --git a/src/files/util.hpp b/src/files/util.hpp new file mode 100644 index 00000000..5967299e --- /dev/null +++ b/src/files/util.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace files { + inline bool is_valid_name(std::string_view name) { + static std::string illegalChars = "\\/%?!<>:; "; + for (char c : illegalChars) { + if (name.find(c) != std::string::npos) { + return false; + } + } + return !name.empty(); + } +} diff --git a/src/logic/scripting/lua/lua_custom_types.hpp b/src/logic/scripting/lua/lua_custom_types.hpp index 885052ce..951d52cf 100644 --- a/src/logic/scripting/lua/lua_custom_types.hpp +++ b/src/logic/scripting/lua/lua_custom_types.hpp @@ -4,6 +4,7 @@ #include #include "lua_commons.hpp" +#include "maths/Heightmap.hpp" namespace lua { class Userdata { @@ -27,37 +28,42 @@ namespace lua { } static int createMetatable(lua::State*); - inline static std::string TYPENAME = "bytearray"; + inline static std::string TYPENAME = "Bytearray"; }; - class Heightmap : public Userdata { - std::vector buffer; - uint width, height; + class LuaHeightmap : public Userdata { + std::shared_ptr map; public: - Heightmap(uint width, uint height); - virtual ~Heightmap(); + LuaHeightmap(uint width, uint height) + : map(std::make_shared(width, height)) {} + + virtual ~LuaHeightmap() = default; uint getWidth() const { - return width; + return map->getWidth(); } uint getHeight() const { - return height; + return map->getHeight(); + } + + float* getValues() { + return map->getValues(); + } + + const float* getValues() const { + return map->getValues(); } const std::string& getTypeName() const override { return TYPENAME; } - float* getValues() { - return buffer.data(); - } - - const float* getValues() const { - return buffer.data(); + std::shared_ptr getHeightmap() const { + return map; } static int createMetatable(lua::State*); - inline static std::string TYPENAME = "heightmap"; + inline static std::string TYPENAME = "Heightmap"; }; } diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 8008517a..879ec4c6 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -97,7 +97,7 @@ void lua::initialize() { initialize_libs_extends(L); newusertype(L, "Bytearray"); - newusertype(L, "Heightmap"); + newusertype(L, "Heightmap"); } void lua::finalize() { diff --git a/src/logic/scripting/lua/lua_type_heightmap.cpp b/src/logic/scripting/lua/lua_type_heightmap.cpp index ecbdc1bf..357011f3 100644 --- a/src/logic/scripting/lua/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/lua_type_heightmap.cpp @@ -8,6 +8,7 @@ #include "util/functional_util.hpp" #include "maths/FastNoiseLite.h" #include "coders/png.hpp" +#include "files/util.hpp" #include "graphics/core/ImageData.hpp" #include "lua_util.hpp" @@ -15,16 +16,12 @@ using namespace lua; static fnl_state noise = fnlCreateState(); -Heightmap::Heightmap(uint width, uint height) : width(width), height(height) { - buffer.resize(width*height); -} - -Heightmap::~Heightmap() { -} - static int l_dump(lua::State* L) { - if (auto heightmap = touserdata(L, 1)) { + if (auto heightmap = touserdata(L, 1)) { auto filename = tostring(L, 2); + if (!files::is_valid_name(filename)) { + throw std::runtime_error("invalid file name"); + } uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); ImageData image(ImageFormat::rgb888, w, h); @@ -46,7 +43,7 @@ static int l_dump(lua::State* L) { } static int l_noise(lua::State* L) { - if (auto heightmap = touserdata(L, 1)) { + if (auto heightmap = touserdata(L, 1)) { uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); auto heights = heightmap->getValues(); @@ -62,13 +59,13 @@ static int l_noise(lua::State* L) { if (gettop(L) > 4) { multiplier = tonumber(L, 5); } - const Heightmap* shiftMapX = nullptr; - const Heightmap* shiftMapY = nullptr; + const LuaHeightmap* shiftMapX = nullptr; + const LuaHeightmap* shiftMapY = nullptr; if (gettop(L) > 5) { - shiftMapX = touserdata(L, 6); + shiftMapX = touserdata(L, 6); } if (gettop(L) > 6) { - shiftMapY = touserdata(L, 7); + shiftMapY = touserdata(L, 7); } for (uint y = 0; y < h; y++) { for (uint x = 0; x < w; x++) { @@ -159,13 +156,13 @@ static int l_meta_meta_call(lua::State* L) { if (width <= 0 || height <= 0) { throw std::runtime_error("width and height must be greather than 0"); } - return newuserdata( + return newuserdata( L, static_cast(width), static_cast(height) ); } static int l_meta_index(lua::State* L) { - auto map = touserdata(L, 1); + auto map = touserdata(L, 1); if (map == nullptr) { return 0; } @@ -186,7 +183,7 @@ static int l_meta_index(lua::State* L) { } static int l_meta_tostring(lua::State* L) { - auto map = touserdata(L, 1); + auto map = touserdata(L, 1); if (map == nullptr) { return 0; } @@ -201,7 +198,7 @@ static int l_meta_tostring(lua::State* L) { ); } -int Heightmap::createMetatable(lua::State* L) { +int LuaHeightmap::createMetatable(lua::State* L) { createtable(L, 0, 2); pushcfunction(L, lua::wrap); setfield(L, "__tostring"); diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 0822e5bc..ce77c429 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -153,6 +153,14 @@ namespace lua { return 3; } + template + inline int pushivec_stack(lua::State* L, glm::vec vec) { + for (int i = 0; i < n; i++) { + pushinteger(L, vec[i]); + } + return n; + } + inline int pushivec3_stack(lua::State* L, glm::ivec3 vec) { pushinteger(L, vec.x); pushinteger(L, vec.y); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index aeedc1d9..03267e20 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -14,6 +14,9 @@ #include "items/ItemDef.hpp" #include "logic/BlocksController.hpp" #include "logic/LevelController.hpp" +#include "lua/lua_engine.hpp" +#include "lua/lua_custom_types.hpp" +#include "maths/Heightmap.hpp" #include "objects/Entities.hpp" #include "objects/EntityDef.hpp" #include "objects/Player.hpp" @@ -21,7 +24,7 @@ #include "util/timeutil.hpp" #include "voxels/Block.hpp" #include "world/Level.hpp" -#include "lua/lua_engine.hpp" +#include "world/generator/GeneratorDef.hpp" using namespace scripting; @@ -62,6 +65,15 @@ void scripting::initialize(Engine* engine) { return std::make_shared(0); } +[[nodiscard]] scriptenv scripting::create_environment() { + auto L = lua::get_main_thread(); + int id = lua::create_environment(L, 0); + return std::shared_ptr(new int(id), [=](int* id) { //-V508 + lua::removeEnvironment(L, *id); + delete id; + }); +} + [[nodiscard]] scriptenv scripting::create_pack_environment( const ContentPack& pack ) { @@ -672,6 +684,35 @@ void scripting::load_entity_component( lua::store_in(L, lua::CHUNKS_TABLE, name); } +class LuaGeneratorScript : public GeneratorScript { + scriptenv env; +public: + LuaGeneratorScript(scriptenv env) : env(std::move(env)) {} + + std::shared_ptr generateHeightmap( + const glm::ivec2& offset, const glm::ivec2& size + ) 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); + if (lua::call_nothrow(L, 4)) { + return lua::touserdata(L, -1)->getHeightmap(); + } + } + return std::make_shared(size.x, size.y); + } +}; + +std::unique_ptr scripting::load_generator( + const fs::path& file +) { + auto env = create_environment(); + load_script(*env, "generator", file); + return std::make_unique(std::move(env)); +} + void scripting::load_world_script( const scriptenv& senv, const std::string& prefix, diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 94723f05..85fd8323 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -32,6 +32,7 @@ class BlocksController; class LevelController; class Entity; struct EntityDef; +class GeneratorScript; namespace scripting { extern Engine* engine; @@ -50,6 +51,7 @@ namespace scripting { scriptenv get_root_environment(); scriptenv create_pack_environment(const ContentPack& pack); + scriptenv create_environment(); scriptenv create_doc_environment( const scriptenv& parent, const std::string& name ); @@ -144,6 +146,10 @@ namespace scripting { void load_entity_component(const std::string& name, const fs::path& file); + std::unique_ptr load_generator( + const fs::path& file + ); + /// @brief Load package-specific world script /// @param env environment /// @param packid content-pack id diff --git a/src/maths/Heightmap.hpp b/src/maths/Heightmap.hpp new file mode 100644 index 00000000..cd125d36 --- /dev/null +++ b/src/maths/Heightmap.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include "typedefs.hpp" + +class Heightmap { + std::vector buffer; + uint width, height; +public: + Heightmap(uint width, uint height) + : width(width), height(height) { + buffer.resize(width*height); + } + + ~Heightmap() = default; + + uint getWidth() const { + return width; + } + + uint getHeight() const { + return height; + } + + float* getValues() { + return buffer.data(); + } + + const float* getValues() const { + return buffer.data(); + } +}; diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index fa26787f..608d0749 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -1,10 +1,23 @@ #pragma once #include +#include #include "typedefs.hpp" +#include "maths/Heightmap.hpp" + +class GeneratorScript { +public: + virtual ~GeneratorScript() = default; + + virtual std::shared_ptr generateHeightmap( + const glm::ivec2& offset, const glm::ivec2& size) = 0; +}; struct GeneratorDef { std::string name; - scriptenv env; + std::unique_ptr script; + + GeneratorDef(std::string name) : name(std::move(name)) {} + GeneratorDef(const GeneratorDef&) = delete; };