#include "scripting.hpp" #include #include #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 "voxels/Chunk.hpp" #include "data/dv.hpp" #include "world/generator/GeneratorDef.hpp" #include "util/timeutil.hpp" #include "files/files.hpp" #include "engine.hpp" #include "debug/Logger.hpp" using namespace lua; static debug::Logger logger("generator-scripting"); class LuaGeneratorScript : public GeneratorScript { State* L; const GeneratorDef& def; scriptenv env; public: LuaGeneratorScript(State* L, const GeneratorDef& def, scriptenv env) : L(L), def(def), env(std::move(env)) { } virtual ~LuaGeneratorScript() { env.reset(); if (L != get_main_state()) { close(L); } } std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint bpd ) override { pushenv(L, *env); if (getfield(L, "generate_heightmap")) { pushivec_stack(L, offset); pushivec_stack(L, size); pushinteger(L, seed); pushinteger(L, bpd); if (call_nothrow(L, 6)) { auto map = touserdata(L, -1)->getHeightmap(); pop(L, 2); return map; } } pop(L); return std::make_shared(size.x, size.y); } std::vector> generateParameterMaps( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint bpd ) override { std::vector> maps; uint biomeParameters = def.biomeParameters; pushenv(L, *env); if (getfield(L, "generate_biome_parameters")) { pushivec_stack(L, offset); pushivec_stack(L, size); pushinteger(L, seed); pushinteger(L, bpd); if (call_nothrow(L, 6, biomeParameters)) { for (int i = biomeParameters-1; i >= 0; i--) { maps.push_back( touserdata(L, -1-i)->getHeightmap()); } pop(L, 1+biomeParameters); return maps; } } pop(L); for (uint i = 0; i < biomeParameters; i++) { maps.push_back(std::make_shared(size.x, size.y)); } return maps; } void perform_line(lua::State* L, PrototypePlacements& placements) { rawgeti(L, 2); blockid_t block = touinteger(L, -1); pop(L); rawgeti(L, 3); glm::ivec3 a = tovec3(L, -1); pop(L); rawgeti(L, 4); glm::ivec3 b = tovec3(L, -1); pop(L); rawgeti(L, 5); int radius = touinteger(L, -1); pop(L); placements.lines.emplace_back(block, a, b, radius); } void perform_placement(lua::State* L, PrototypePlacements& placements) { rawgeti(L, 1); int structIndex = 0; if (isstring(L, -1)) { const char* name = require_string(L, -1); if (!std::strcmp(name, ":line")) { pop(L); perform_line(L, placements); return; } const auto& found = def.structuresIndices.find(name); if (found != def.structuresIndices.end()) { structIndex = found->second; } } else { structIndex = tointeger(L, -1); } pop(L); rawgeti(L, 2); glm::ivec3 pos = tovec3(L, -1); pop(L); rawgeti(L, 3); int rotation = tointeger(L, -1) & 0b11; pop(L); placements.structs.emplace_back(structIndex, pos, rotation); } PrototypePlacements placeStructuresWide( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, uint chunkHeight ) override { PrototypePlacements placements {}; stackguard _(L); pushenv(L, *env); if (getfield(L, "place_structures_wide")) { pushivec_stack(L, offset); pushivec_stack(L, size); pushinteger(L, seed); pushinteger(L, chunkHeight); if (call_nothrow(L, 6, 1)) { int len = objlen(L, -1); for (int i = 1; i <= len; i++) { rawgeti(L, i); perform_placement(L, placements); pop(L); } pop(L); } } return placements; } PrototypePlacements placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap, uint chunkHeight ) override { PrototypePlacements placements {}; stackguard _(L); pushenv(L, *env); if (getfield(L, "place_structures")) { pushivec_stack(L, offset); pushivec_stack(L, size); pushinteger(L, seed); newuserdata(L, heightmap); pushinteger(L, chunkHeight); if (call_nothrow(L, 7, 1)) { int len = objlen(L, -1); for (int i = 1; i <= len; i++) { rawgeti(L, i); perform_placement(L, placements); pop(L); } pop(L); } } return placements; } }; std::unique_ptr scripting::load_generator( const GeneratorDef& def, const fs::path& file, const std::string& dirPath ) { auto L = create_state(*engine->getPaths(), StateType::GENERATOR); auto env = create_environment(L); stackguard _(L); pushenv(L, *env); pushstring(L, dirPath); setfield(L, "__DIR__"); pushstring(L, dirPath + "/script.lua"); setfield(L, "__FILE__"); pop(L); if (fs::exists(file)) { std::string src = files::read_string(file); logger.info() << "script (generator) " << file.u8string(); pop(L, execute(L, *env, src, file.u8string())); } else { // Use default (empty) script pop(L, execute(L, *env, "", "")); } return std::make_unique(L, def, std::move(env)); }