From f1f81371affec4e1f5f8fe69c84e8cc50dc1e3f6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 19 Jan 2025 23:36:37 +0300 Subject: [PATCH 1/3] add world.save_chunk_data(...) --- src/logic/scripting/lua/libs/libworld.cpp | 22 ++++++++++++ src/voxels/compressed_chunks.cpp | 44 +++++++++++++++++++---- src/voxels/compressed_chunks.hpp | 2 ++ 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index bbba422d..5db150b0 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -6,6 +6,7 @@ #include "assets/AssetsLoader.hpp" #include "coders/json.hpp" #include "engine/Engine.hpp" +#include "files/WorldFiles.hpp" #include "files/engine_paths.hpp" #include "files/files.hpp" #include "lighting/Lighting.hpp" @@ -158,9 +159,14 @@ static void integrate_chunk_client(Chunk& chunk) { } static int l_set_chunk_data(lua::State* L) { + if (level == nullptr) { + throw std::runtime_error("no open world"); + } + int x = static_cast(lua::tointeger(L, 1)); int z = static_cast(lua::tointeger(L, 2)); auto buffer = lua::require_bytearray(L, 3); + auto chunk = level->chunks->getChunk(x, z); if (chunk == nullptr) { return lua::pushboolean(L, false); @@ -175,6 +181,21 @@ static int l_set_chunk_data(lua::State* L) { return lua::pushboolean(L, true); } +static int l_save_chunk_data(lua::State* L) { + if (level == nullptr) { + throw std::runtime_error("no open world"); + } + + int x = static_cast(lua::tointeger(L, 1)); + int z = static_cast(lua::tointeger(L, 2)); + auto buffer = lua::require_bytearray(L, 3); + + compressed_chunks::save( + x, z, std::move(buffer), level->getWorld()->wfile->getRegions() + ); + return 0; +} + static int l_count_chunks(lua::State* L) { if (level == nullptr) { return 0; @@ -197,6 +218,7 @@ const luaL_Reg worldlib[] = { {"exists", lua::wrap}, {"get_chunk_data", lua::wrap}, {"set_chunk_data", lua::wrap}, + {"save_chunk_data", lua::wrap}, {"count_chunks", lua::wrap}, {NULL, NULL} }; diff --git a/src/voxels/compressed_chunks.cpp b/src/voxels/compressed_chunks.cpp index 0e6772af..64000a79 100644 --- a/src/voxels/compressed_chunks.cpp +++ b/src/voxels/compressed_chunks.cpp @@ -3,6 +3,7 @@ #include "coders/rle.hpp" #include "coders/gzip.hpp" #include "coders/byte_utils.hpp" +#include "files/WorldFiles.hpp" #include "voxels/Chunk.hpp" inline constexpr int HAS_VOXELS = 0x1; @@ -34,6 +35,15 @@ std::vector compressed_chunks::encode(const Chunk& chunk) { return builder.build(); } +static void read_voxel_data(ByteReader& reader, util::Buffer& dst) { + size_t gzipCompressedSize = reader.getInt32(); + + auto rleData = gzip::decompress(reader.pointer(), gzipCompressedSize); + reader.skip(gzipCompressedSize); + + extrle::decode16(rleData.data(), rleData.size(), dst.data()); +} + void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) { ByteReader reader(src, size); @@ -41,14 +51,9 @@ void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) { reader.skip(1); // reserved byte if (flags & HAS_VOXELS) { - size_t gzipCompressedSize = reader.getInt32(); - - auto rleData = gzip::decompress(reader.pointer(), gzipCompressedSize); - reader.skip(gzipCompressedSize); - /// world.get_chunk_data is only available in the main Lua state static util::Buffer voxelData (CHUNK_DATA_LEN); - extrle::decode16(rleData.data(), rleData.size(), voxelData.data()); + read_voxel_data(reader, voxelData); chunk.decode(voxelData.data()); chunk.updateHeights(); } @@ -59,3 +64,30 @@ void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) { } chunk.setModifiedAndUnsaved(); } + +void compressed_chunks::save( + int x, int z, std::vector bytes, WorldRegions& regions +) { + ByteReader reader(bytes.data(), bytes.size()); + + ubyte flags = reader.get(); + reader.skip(1); // reserved byte + if (flags & HAS_VOXELS) { + util::Buffer voxelData (CHUNK_DATA_LEN); + read_voxel_data(reader, voxelData); + regions.put( + x, z, REGION_LAYER_VOXELS, voxelData.release(), CHUNK_DATA_LEN + ); + } + if (flags & HAS_METADATA) { + size_t metadataSize = reader.getInt32(); + regions.put( + x, + z, + REGION_LAYER_BLOCKS_DATA, + util::Buffer(reader.pointer(), metadataSize).release(), + metadataSize + ); + reader.skip(metadataSize); + } +} diff --git a/src/voxels/compressed_chunks.hpp b/src/voxels/compressed_chunks.hpp index dc0bc5b6..2181669c 100644 --- a/src/voxels/compressed_chunks.hpp +++ b/src/voxels/compressed_chunks.hpp @@ -5,8 +5,10 @@ #include class Chunk; +class WorldRegions; namespace compressed_chunks { std::vector encode(const Chunk& chunk); void decode(Chunk& chunk, const ubyte* src, size_t size); + void save(int x, int z, std::vector bytes, WorldRegions& regions); } From 02a93d8bc36ba0ca49c5e9d633c96aac4f92f7db Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 20 Jan 2025 01:25:43 +0300 Subject: [PATCH 2/3] feat: world.get_chunk_data(...) saved chunk data when chunk is not loaded --- src/logic/scripting/lua/libs/libworld.cpp | 16 ++++++++++--- src/voxels/compressed_chunks.cpp | 28 +++++++++++++---------- src/voxels/compressed_chunks.hpp | 8 ++++++- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 5db150b0..362d08c7 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -126,11 +126,21 @@ static int l_get_chunk_data(lua::State* L) { int x = static_cast(lua::tointeger(L, 1)); int z = static_cast(lua::tointeger(L, 2)); const auto& chunk = level->chunks->getChunk(x, z); + + std::vector chunkData; if (chunk == nullptr) { - lua::pushnil(L); - return 0; + auto& regions = level->getWorld()->wfile->getRegions(); + auto voxelData = regions.getVoxels(x, z); + if (voxelData == nullptr) { + return 0; + } + static util::Buffer rleBuffer(CHUNK_DATA_LEN * 2); + auto metadata = regions.getBlocksData(x, z); + chunkData = + compressed_chunks::encode(voxelData.get(), metadata, rleBuffer); + } else { + chunkData = compressed_chunks::encode(*chunk); } - auto chunkData = compressed_chunks::encode(*chunk); return lua::newuserdata(L, std::move(chunkData)); } diff --git a/src/voxels/compressed_chunks.cpp b/src/voxels/compressed_chunks.cpp index 64000a79..24cc02dd 100644 --- a/src/voxels/compressed_chunks.cpp +++ b/src/voxels/compressed_chunks.cpp @@ -2,28 +2,24 @@ #include "coders/rle.hpp" #include "coders/gzip.hpp" -#include "coders/byte_utils.hpp" + #include "files/WorldFiles.hpp" -#include "voxels/Chunk.hpp" inline constexpr int HAS_VOXELS = 0x1; inline constexpr int HAS_METADATA = 0x2; -std::vector compressed_chunks::encode(const Chunk& chunk) { - auto data = chunk.encode(); - - /// world.get_chunk_data is only available in the main Lua state - static util::Buffer rleBuffer; - if (rleBuffer.size() < CHUNK_DATA_LEN * 2) { - rleBuffer = util::Buffer(CHUNK_DATA_LEN * 2); - } +std::vector compressed_chunks::encode( + const ubyte* data, + const BlocksMetadata& metadata, + util::Buffer& rleBuffer +) { size_t rleCompressedSize = - extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data()); + extrle::encode16(data, CHUNK_DATA_LEN, rleBuffer.data()); const auto gzipCompressedData = gzip::compress( rleBuffer.data(), rleCompressedSize ); - auto metadataBytes = chunk.blocksMetadata.serialize(); + auto metadataBytes = metadata.serialize(); ByteBuilder builder(2 + 8 + gzipCompressedData.size() + metadataBytes.size()); builder.put(HAS_VOXELS | HAS_METADATA); // flags @@ -35,6 +31,14 @@ std::vector compressed_chunks::encode(const Chunk& chunk) { return builder.build(); } +std::vector compressed_chunks::encode(const Chunk& chunk) { + auto data = chunk.encode(); + + /// world.get_chunk_data is only available in the main Lua state + static util::Buffer rleBuffer(CHUNK_DATA_LEN * 2); + return encode(data.get(), chunk.blocksMetadata, rleBuffer); +} + static void read_voxel_data(ByteReader& reader, util::Buffer& dst) { size_t gzipCompressedSize = reader.getInt32(); diff --git a/src/voxels/compressed_chunks.hpp b/src/voxels/compressed_chunks.hpp index 2181669c..dd00bc66 100644 --- a/src/voxels/compressed_chunks.hpp +++ b/src/voxels/compressed_chunks.hpp @@ -1,13 +1,19 @@ #pragma once #include "typedefs.hpp" +#include "Chunk.hpp" +#include "coders/byte_utils.hpp" #include -class Chunk; class WorldRegions; namespace compressed_chunks { + std::vector encode( + const ubyte* voxelData, + const BlocksMetadata& metadata, + util::Buffer& rleBuffer + ); std::vector encode(const Chunk& chunk); void decode(Chunk& chunk, const ubyte* src, size_t size); void save(int x, int z, std::vector bytes, WorldRegions& regions); From d113cfa86c6400fbb67e3cd86155f47e5ace703c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 20 Jan 2025 01:33:01 +0300 Subject: [PATCH 3/3] update doc/*/scripting/builtins/libworld.md --- doc/en/scripting/builtins/libworld.md | 11 ++++++++++- doc/ru/scripting/builtins/libworld.md | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/doc/en/scripting/builtins/libworld.md b/doc/en/scripting/builtins/libworld.md index 97c6fdee..b2453b34 100644 --- a/doc/en/scripting/builtins/libworld.md +++ b/doc/en/scripting/builtins/libworld.md @@ -49,10 +49,11 @@ world.is_night() -> bool world.count_chunks() -> int -- Returns the compressed chunk data to send. +-- If the chunk is not loaded, returns the saved data. -- Currently includes: -- 1. Voxel data (id and state) -- 2. Voxel metadata (fields) -world.get_chunk_data(x: int, z: int) -> Bytearray +world.get_chunk_data(x: int, z: int) -> Bytearray or nil -- Modifies the chunk based on the compressed data. -- Returns true if the chunk exists. @@ -61,4 +62,12 @@ world.set_chunk_data( -- compressed chunk data data: Bytearray ) -> bool + +-- Saves chunk data to region. +-- Changes will be written to file only on world save. +world.save_chunk_data( + x: int, z: int, + -- compressed chunk data + data: Bytearray +) ``` diff --git a/doc/ru/scripting/builtins/libworld.md b/doc/ru/scripting/builtins/libworld.md index a7e0e5a6..5713a328 100644 --- a/doc/ru/scripting/builtins/libworld.md +++ b/doc/ru/scripting/builtins/libworld.md @@ -48,10 +48,11 @@ world.is_night() -> bool world.count_chunks() -> int -- Возвращает сжатые данные чанка для отправки. +-- Если чанк не загружен, возвращает сохранённые данные. -- На данный момент включает: -- 1. Данные вокселей (id и состояние) -- 2. Метаданные (поля) вокселей -world.get_chunk_data(x: int, z: int) -> Bytearray +world.get_chunk_data(x: int, z: int) -> Bytearray или nil -- Изменяет чанк на основе сжатых данных. -- Возвращает true если чанк существует. @@ -60,4 +61,12 @@ world.set_chunk_data( -- сжатые данные чанка data: Bytearray ) -> bool + +-- Сохраняет данные чанка в регион. +-- Изменения будет записаны в файл только после сохранения мира. +world.save_chunk_data( + x: int, z: int, + -- сжатые данные чанка + data: Bytearray +) ```