From fc99343fb581d66bc48a9e89a68ad423edea938c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 30 Sep 2024 18:55:01 +0300 Subject: [PATCH] add block.get_field(...), block.set_field(...) --- src/content/ContentLoader.cpp | 7 ++ src/data/StructLayout.cpp | 26 +++---- src/data/StructLayout.hpp | 51 ++++++++++--- src/logic/scripting/lua/libblock.cpp | 105 +++++++++++++++++++++++++++ src/voxels/Chunks.cpp | 8 ++ 5 files changed, 169 insertions(+), 28 deletions(-) diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 5c10b6e3..33b0cf4a 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -21,6 +21,7 @@ #include "util/stringutil.hpp" #include "voxels/Block.hpp" #include "data/dv_util.hpp" +#include "data/StructLayout.hpp" namespace fs = std::filesystem; @@ -250,6 +251,12 @@ void ContentLoader::loadBlock( root.at("ui-layout").get(def.uiLayout); root.at("inventory-size").get(def.inventorySize); root.at("tick-interval").get(def.tickInterval); + + if (root.has("fields")) { + def.dataStruct = std::make_unique(); + def.dataStruct->deserialize(root["fields"]); + } + if (def.tickInterval == 0) { def.tickInterval = 1; } diff --git a/src/data/StructLayout.cpp b/src/data/StructLayout.cpp index 554dd0dc..9007213c 100644 --- a/src/data/StructLayout.cpp +++ b/src/data/StructLayout.cpp @@ -183,7 +183,7 @@ std::vector StructLayout::checkCompatibility( return report; } -const Field& StructLayout::requreField(const std::string& name) const { +const Field& StructLayout::requireField(const std::string& name) const { auto found = indices.find(name); if (found == indices.end()) { throw std::runtime_error("field '"+name+"' does not exist"); @@ -200,9 +200,8 @@ static void set_int(ubyte* dst, integer_t value) { } void StructLayout::setInteger( - ubyte* dst, integer_t value, const std::string& name, int index + ubyte* dst, integer_t value, const Field& field, int index ) const { - const auto& field = requreField(name); if (index < 0 || index >= field.elements) { throw std::out_of_range( "index out of bounds [0, "+std::to_string(field.elements)+"]"); @@ -216,7 +215,7 @@ void StructLayout::setInteger( case FieldType::CHAR: set_int(ptr, value); break; case FieldType::F32: case FieldType::F64: - setNumber(dst, static_cast(value), name, index); + setNumber(dst, static_cast(value), field, index); break; default: throw std::runtime_error("type error"); @@ -224,9 +223,8 @@ void StructLayout::setInteger( } void StructLayout::setNumber( - ubyte* dst, number_t value, const std::string& name, int index + ubyte* dst, number_t value, const Field& field, int index ) const { - const auto& field = requreField(name); if (index < 0 || index >= field.elements) { throw std::out_of_range( "index out of bounds [0, "+std::to_string(field.elements)+"]"); @@ -255,7 +253,7 @@ void StructLayout::setNumber( size_t StructLayout::setAscii( ubyte* dst, std::string_view value, const std::string& name ) const { - const auto& field = requreField(name); + const auto& field = requireField(name); if (field.type != FieldType::CHAR) { throw std::runtime_error("'char' field type required"); } @@ -266,9 +264,8 @@ size_t StructLayout::setAscii( } size_t StructLayout::setUnicode( - ubyte* dst, std::string_view value, const std::string& name + ubyte* dst, std::string_view value, const Field& field ) const { - const auto& field = requreField(name); if (field.type != FieldType::CHAR) { throw std::runtime_error("'char' field type required"); } @@ -285,9 +282,8 @@ static T get_int(const ubyte* src) { } integer_t StructLayout::getInteger( - const ubyte* src, const std::string& name, int index + const ubyte* src, const Field& field, int index ) const { - const auto& field = requreField(name); if (index < 0 || index >= field.elements) { throw std::out_of_range( "index out of bounds [0, "+std::to_string(field.elements)+"]"); @@ -305,9 +301,8 @@ integer_t StructLayout::getInteger( } number_t StructLayout::getNumber( - const ubyte* src, const std::string& name, int index + const ubyte* src, const Field& field, int index ) const { - const auto& field = requreField(name); if (index < 0 || index >= field.elements) { throw std::out_of_range( "index out of bounds [0, "+std::to_string(field.elements)+"]"); @@ -331,16 +326,15 @@ number_t StructLayout::getNumber( case FieldType::I32: case FieldType::I64: case FieldType::CHAR: - return getInteger(src, name, index); + return getInteger(src, field, index); default: throw std::runtime_error("type error"); } } std::string_view StructLayout::getChars( - const ubyte* src, const std::string& name + const ubyte* src, const Field& field ) const { - const auto& field = requreField(name); if (field.type != FieldType::CHAR) { throw std::runtime_error("'char' field type required"); } diff --git a/src/data/StructLayout.hpp b/src/data/StructLayout.hpp index f94d9365..4e0ff886 100644 --- a/src/data/StructLayout.hpp +++ b/src/data/StructLayout.hpp @@ -129,35 +129,54 @@ namespace data { /// @throws std::runtime_exception - field not found /// @param name field name /// @return read-only field reference - const Field& requreField(const std::string& name) const; + const Field& requireField(const std::string& name) const; + + [[nodiscard]] + integer_t getInteger(const ubyte* src, const std::string& name, int index=0) const { + return getInteger(src, requireField(name), index); + } /// @brief Get integer from specified field. /// Types: (i8, i16, i32, i64, char) /// @throws std::runtime_exception - field not found /// @throws std::out_of_range - index is out of range [0, elements-1] /// @param src source buffer - /// @param name field name + /// @param field target field /// @param index array index /// @return field value [[nodiscard]] - integer_t getInteger(const ubyte* src, const std::string& name, int index=0) const; + integer_t getInteger(const ubyte* src, const Field& field, int index=0) const; + + [[nodiscard]] + number_t getNumber(const ubyte* src, const std::string& name, int index=0) const { + return getNumber(src, requireField(name), index); + } /// @brief Get floating-point number from specified field. /// Types: (f32, f64, i8, i16, i32, i64, char) /// @throws std::runtime_exception - field not found /// @throws std::out_of_range - index is out of range [0, elements-1] /// @param src source buffer - /// @param name field name + /// @param field target field /// @param index array index /// @return field value [[nodiscard]] - number_t getNumber(const ubyte* src, const std::string& name, int index=0) const; + number_t getNumber(const ubyte* src, const Field& field, int index=0) const; + + [[nodiscard]] + std::string_view getChars(const ubyte* src, const std::string& name) const { + return getChars(src, requireField(name)); + } /// @brief Get read-only chars array as string_view. /// @param src source buffer - /// @param name field name + /// @param field target field [[nodiscard]] - std::string_view getChars(const ubyte* src, const std::string& name) const; + std::string_view getChars(const ubyte* src, const Field& field) const; + + void setInteger(ubyte* dst, integer_t value, const std::string& name, int index=0) const { + setInteger(dst, value, requireField(name), index); + } /// @brief Set field integer value. /// Types: (i8, i16, i32, i64, f32, f64, char) @@ -165,9 +184,13 @@ namespace data { /// @throws std::out_of_range - index is out of range [0, elements-1] /// @param dst destination buffer /// @param value value - /// @param name field name + /// @param field target field /// @param index array index - void setInteger(ubyte* dst, integer_t value, const std::string& name, int index=0) const; + void setInteger(ubyte* dst, integer_t value, const Field& field, int index=0) const; + + void setNumber(ubyte* dst, number_t value, const std::string& name, int index=0) const { + setNumber(dst, value, requireField(name), index); + } /// @brief Set field numeric value. /// Types: (f32, f64) @@ -175,9 +198,9 @@ namespace data { /// @throws std::out_of_range - index is out of range [0, elements-1] /// @param dst destination buffer /// @param value value - /// @param name field name + /// @param field target field /// @param index array index - void setNumber(ubyte* dst, number_t value, const std::string& name, int index=0) const; + void setNumber(ubyte* dst, number_t value, const Field& field, int index=0) const; /// @brief Replace chars array to given ASCII string /// @throws std::runtime_exception - field not found @@ -187,6 +210,10 @@ namespace data { /// @param name field name /// @return number of written string chars size_t setAscii(ubyte* dst, std::string_view value, const std::string& name) const; + + size_t setUnicode(ubyte* dst, std::string_view value, const std::string& name) const { + return setUnicode(dst, value, requireField(name)); + } /// @brief Unicode-safe version of setAscii /// @throws std::runtime_exception - field not found @@ -194,7 +221,7 @@ namespace data { /// @param value utf-8 string /// @param name field name /// @return number of written string chars - size_t setUnicode(ubyte* dst, std::string_view value, const std::string& name) const; + size_t setUnicode(ubyte* dst, std::string_view value, const Field& field) const; /// @return total structure size (bytes) [[nodiscard]] size_t size() const { diff --git a/src/logic/scripting/lua/libblock.cpp b/src/logic/scripting/lua/libblock.cpp index bd906f5d..f282422c 100644 --- a/src/logic/scripting/lua/libblock.cpp +++ b/src/logic/scripting/lua/libblock.cpp @@ -7,6 +7,8 @@ #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" #include "world/Level.hpp" +#include "maths/voxmaths.hpp" +#include "data/StructLayout.hpp" #include "api_lua.hpp" using namespace scripting; @@ -436,6 +438,107 @@ static int l_decompose_state(lua::State* L) { return 1; } +static int l_get_field(lua::State* L) { + auto x = lua::tointeger(L, 1); + auto y = lua::tointeger(L, 2); + auto z = lua::tointeger(L, 3); + auto name = lua::require_string(L, 4); + size_t index = 0; + if (lua::gettop(L) >= 5) { + index = lua::tointeger(L, 5); + } + auto vox = level->chunks->get(x, y, z); + auto cx = floordiv(x, CHUNK_W); + auto cz = floordiv(z, CHUNK_D); + auto chunk = level->chunks->getChunk(cx, cz); + auto lx = x - cx * CHUNK_W; + auto lz = z - cz * CHUNK_W; + size_t voxelIndex = vox_index(lx, y, lz); + + const auto& def = content->getIndices()->blocks.require(vox->id); + if (def.dataStruct == nullptr) { + return 0; + } + const auto& dataStruct = *def.dataStruct; + const auto field = dataStruct.getField(name); + if (field == nullptr) { + return 0; + } + const ubyte* src = chunk->blocksMetadata.find(voxelIndex); + if (src == nullptr) { + throw std::runtime_error("block data is not allocated"); + } + switch (field->type) { + case data::FieldType::I8: + case data::FieldType::I16: + case data::FieldType::I32: + case data::FieldType::I64: + return lua::pushinteger(L, dataStruct.getInteger(src, *field, index)); + case data::FieldType::F32: + case data::FieldType::F64: + return lua::pushnumber(L, dataStruct.getNumber(src, *field, index)); + case data::FieldType::CHAR: + return lua::pushstring(L, + std::string(dataStruct.getChars(src, *field)).c_str()); + case data::FieldType::COUNT: + return 0; + } + return 0; +} + +static int l_set_field(lua::State* L) { + auto x = lua::tointeger(L, 1); + auto y = lua::tointeger(L, 2); + auto z = lua::tointeger(L, 3); + auto name = lua::require_string(L, 4); + auto value = lua::tovalue(L, 5); + size_t index = 0; + if (lua::gettop(L) >= 6) { + index = lua::tointeger(L, 6); + } + auto vox = level->chunks->get(x, y, z); + auto cx = floordiv(x, CHUNK_W); + auto cz = floordiv(z, CHUNK_D); + auto chunk = level->chunks->getChunk(cx, cz); + auto lx = x - cx * CHUNK_W; + auto lz = z - cz * CHUNK_W; + size_t voxelIndex = vox_index(lx, y, lz); + + const auto& def = content->getIndices()->blocks.require(vox->id); + if (def.dataStruct == nullptr) { + return 0; + } + const auto& dataStruct = *def.dataStruct; + const auto field = dataStruct.getField(name); + if (field == nullptr) { + return 0; + } + ubyte* dst = chunk->blocksMetadata.find(voxelIndex); + if (dst == nullptr) { + throw std::runtime_error("block data is not allocated"); + } + switch (field->type) { + case data::FieldType::CHAR: + if (value.isString()) { + return lua::pushinteger(L, + dataStruct.setUnicode(dst, value.asString(), *field)); + } + case data::FieldType::I8: + case data::FieldType::I16: + case data::FieldType::I32: + case data::FieldType::I64: + dataStruct.setInteger(dst, value.asInteger(), *field, index); + break; + case data::FieldType::F32: + case data::FieldType::F64: + dataStruct.setNumber(dst, value.asNumber(), *field, index); + break; + case data::FieldType::COUNT: + break; + } + return 0; +} + const luaL_Reg blocklib[] = { {"index", lua::wrap}, {"name", lua::wrap}, @@ -469,5 +572,7 @@ const luaL_Reg blocklib[] = { {"raycast", lua::wrap}, {"compose_state", lua::wrap}, {"decompose_state", lua::wrap}, + {"get_field", lua::wrap}, + {"set_field", lua::wrap}, {NULL, NULL} }; diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 0e194dce..9b584007 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -6,6 +6,7 @@ #include #include +#include "data/StructLayout.hpp" #include "coders/byte_utils.hpp" #include "coders/json.hpp" #include "content/Content.hpp" @@ -370,6 +371,7 @@ void Chunks::set( } int lx = x - cx * CHUNK_W; int lz = z - cz * CHUNK_D; + size_t index = vox_index(lx, y, lz); // block finalization voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; @@ -380,6 +382,9 @@ void Chunks::set( if (prevdef.rt.extended && !vox.state.segment) { eraseSegments(prevdef, vox.state, gx, y, gz); } + if (prevdef.dataStruct) { + chunk->blocksMetadata.free(chunk->blocksMetadata.find(index)); + } // block initialization const auto& newdef = indices->blocks.require(id); @@ -389,6 +394,9 @@ void Chunks::set( if (!state.segment && newdef.rt.extended) { repairSegments(newdef, state, gx, y, gz); } + if (newdef.dataStruct) { + chunk->blocksMetadata.allocate(index, newdef.dataStruct->size()); + } if (y < chunk->bottom) chunk->bottom = y;