From 7b962841d6a256b3a005a7fe7c2d296ca2baed28 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 11 Jan 2025 18:50:45 +0300 Subject: [PATCH 1/8] fix world.set_chunk_data & rid of extra memory allocations in world.get_chunk_data --- src/logic/scripting/lua/libs/libworld.cpp | 86 +++++++++++------------ 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 1acc1ad7..832f2e2e 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -7,6 +7,7 @@ #include "coders/compression.hpp" #include "coders/gzip.hpp" #include "coders/json.hpp" +#include "coders/rle.hpp" #include "engine/Engine.hpp" #include "files/engine_paths.hpp" #include "files/files.hpp" @@ -123,45 +124,44 @@ static int l_get_generator(lua::State* L) { } static int l_get_chunk_data(lua::State* L) { - int x = (int)lua::tointeger(L, 1); - int y = (int)lua::tointeger(L, 2); + int x = static_cast(lua::tointeger(L, 1)); + int y = static_cast(lua::tointeger(L, 2)); const auto& chunk = level->chunks->getChunk(x, y); if (chunk == nullptr) { lua::pushnil(L); return 0; } - bool compress = false; + bool compress = true; if (lua::gettop(L) >= 3) { compress = lua::toboolean(L, 3); } std::vector chunk_data; if (compress) { - size_t rle_compressed_size; - size_t gzip_compressed_size; + // 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); + } + const auto& data_ptr = chunk->encode(); ubyte* data = data_ptr.get(); - const auto& rle_compressed_data_ptr = compression::compress( - data, - CHUNK_DATA_LEN, - rle_compressed_size, - compression::Method::EXTRLE16 - ); - const auto& gzip_compressed_data = compression::compress( - rle_compressed_data_ptr.get(), - rle_compressed_size, - gzip_compressed_size, - compression::Method::GZIP + + size_t rle_compressed_size = + extrle::encode16(data, CHUNK_DATA_LEN, rlebuffer.data()); + + const auto gzip_compressed_data = gzip::compress( + rlebuffer.data(), rle_compressed_size ); auto tmp = dataio::h2le(rle_compressed_size); - chunk_data.reserve(gzip_compressed_size + sizeof(tmp)); + chunk_data.reserve(gzip_compressed_data.size() + sizeof(tmp)); chunk_data.insert( chunk_data.begin() + 0, (char*)&tmp, ((char*)&tmp) + sizeof(tmp) ); chunk_data.insert( chunk_data.begin() + sizeof(tmp), - gzip_compressed_data.get(), - gzip_compressed_data.get() + gzip_compressed_size + gzip_compressed_data.data(), + gzip_compressed_data.data() + gzip_compressed_data.size() ); } else { const auto& data = chunk->encode(); @@ -174,10 +174,10 @@ static int l_get_chunk_data(lua::State* L) { } static int l_set_chunk_data(lua::State* L) { - int x = (int)lua::tointeger(L, 1); - int y = (int)lua::tointeger(L, 2); + int x = static_cast(lua::tointeger(L, 1)); + int y = static_cast(lua::tointeger(L, 2)); auto buffer = lua::touserdata(L, 3); - bool is_compressed = false; + bool is_compressed = true; if (lua::gettop(L) >= 4) { is_compressed = lua::toboolean(L, 4); } @@ -207,38 +207,32 @@ static int l_set_chunk_data(lua::State* L) { } auto chunksController = controller->getChunksController(); - if (chunksController == nullptr) { - return 1; + if (chunksController->lighting == nullptr) { + return lua::pushboolean(L, true); } Lighting& lighting = *chunksController->lighting; chunk->updateHeights(); - lighting.buildSkyLight(x, y); - chunk->flags.modified = true; + chunk->flags.loadedLights = false; + chunk->flags.lighted = false; + chunk->setModifiedAndUnsaved(); + Lighting::prebuildSkyLight(*chunk, *indices); lighting.onChunkLoaded(x, y, true); - chunk = level->chunks->getChunk(x - 1, y); - if (chunk != nullptr) { - chunk->flags.modified = true; - lighting.onChunkLoaded(x - 1, y, true); - } - chunk = level->chunks->getChunk(x + 1, y); - if (chunk != nullptr) { - chunk->flags.modified = true; - lighting.onChunkLoaded(x + 1, y, true); - } - chunk = level->chunks->getChunk(x, y - 1); - if (chunk != nullptr) { - chunk->flags.modified = true; - lighting.onChunkLoaded(x, y - 1, true); - } - chunk = level->chunks->getChunk(x, y + 1); - if (chunk != nullptr) { - chunk->flags.modified = true; - lighting.onChunkLoaded(x, y + 1, true); + for (int lz = -1; lz <= 1; lz++) { + for (int lx = -1; lx <= 1; lx++) { + if (std::abs(lx) + std::abs(lz) != 1) { + continue; + } + chunk = level->chunks->getChunk(x + lx, y + lz); + if (chunk != nullptr) { + chunk->setModifiedAndUnsaved(); + lighting.onChunkLoaded(x - 1, y, true); + } + } } - return 1; + return lua::pushboolean(L, true); } static int l_count_chunks(lua::State* L) { From 29e018cff8183872ab6400b745b338bf967d27b2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 11 Jan 2025 20:11:29 +0300 Subject: [PATCH 2/8] fix chunk mesh refreshing --- src/graphics/render/ChunksRenderer.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index da40d822..6e6b4bd3 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -135,7 +135,7 @@ const Mesh* ChunksRenderer::getOrRender( if (found == meshes.end()) { return render(chunk, important); } - if (chunk->flags.modified) { + if (chunk->flags.modified && chunk->flags.lighted) { render(chunk, important); } return found->second.mesh.get(); @@ -149,9 +149,17 @@ const Mesh* ChunksRenderer::retrieveChunk( size_t index, const Camera& camera, Shader& shader, bool culling ) { auto chunk = chunks.getChunks()[index]; - if (chunk == nullptr || !chunk->flags.lighted) { + if (chunk == nullptr) { return nullptr; } + if (!chunk->flags.lighted) { + const auto& found = meshes.find({chunk->x, chunk->z}); + if (found == meshes.end()) { + return nullptr; + } else { + return found->second.mesh.get(); + } + } float distance = glm::distance( camera.position, glm::vec3( From fe210708e5502a3525a99317064b83d78b049a73 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 11 Jan 2025 23:03:19 +0300 Subject: [PATCH 3/8] fix --- src/logic/scripting/lua/libs/libworld.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 832f2e2e..073e7f05 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -206,16 +206,18 @@ static int l_set_chunk_data(lua::State* L) { chunk->decode(buffer->data().data()); } + chunk->setModifiedAndUnsaved(); + chunk->updateHeights(); + auto chunksController = controller->getChunksController(); if (chunksController->lighting == nullptr) { return lua::pushboolean(L, true); } Lighting& lighting = *chunksController->lighting; - chunk->updateHeights(); chunk->flags.loadedLights = false; chunk->flags.lighted = false; - chunk->setModifiedAndUnsaved(); + Lighting::prebuildSkyLight(*chunk, *indices); lighting.onChunkLoaded(x, y, true); @@ -226,7 +228,7 @@ static int l_set_chunk_data(lua::State* L) { } chunk = level->chunks->getChunk(x + lx, y + lz); if (chunk != nullptr) { - chunk->setModifiedAndUnsaved(); + chunk->flags.modified = true; lighting.onChunkLoaded(x - 1, y, true); } } From 077de25acc2cc51ecc17ff6a667256a180ac1402 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 11 Jan 2025 23:38:43 +0300 Subject: [PATCH 4/8] refactor --- src/logic/scripting/lua/libs/libworld.cpp | 98 +++++++++++------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 073e7f05..60183ab3 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -123,6 +123,41 @@ static int l_get_generator(lua::State* L) { return lua::pushstring(L, require_world_info().generator); } +static std::vector prepare_chunk_data(const Chunk& chunk, bool compress) { + auto data = chunk.encode(); + + std::vector chunkData; + if (compress) { + // 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); + } + size_t rleCompressedSize = + extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data()); + + const auto gzipCompressedData = gzip::compress( + rleBuffer.data(), rleCompressedSize + ); + auto tmp = dataio::h2le(rleCompressedSize); + chunkData.reserve(gzipCompressedData.size() + sizeof(tmp)); + chunkData.insert( + chunkData.begin() + 0, (char*)&tmp, ((char*)&tmp) + sizeof(tmp) + ); + chunkData.insert( + chunkData.begin() + sizeof(tmp), + gzipCompressedData.data(), + gzipCompressedData.data() + gzipCompressedData.size() + ); + } else { + chunkData.reserve(CHUNK_DATA_LEN); + chunkData.insert( + chunkData.begin(), data.get(), data.get() + CHUNK_DATA_LEN + ); + } + return chunkData; +} + static int l_get_chunk_data(lua::State* L) { int x = static_cast(lua::tointeger(L, 1)); int y = static_cast(lua::tointeger(L, 2)); @@ -131,73 +166,38 @@ static int l_get_chunk_data(lua::State* L) { lua::pushnil(L); return 0; } - bool compress = true; if (lua::gettop(L) >= 3) { compress = lua::toboolean(L, 3); } - std::vector chunk_data; - if (compress) { - // 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); - } - - const auto& data_ptr = chunk->encode(); - ubyte* data = data_ptr.get(); - - size_t rle_compressed_size = - extrle::encode16(data, CHUNK_DATA_LEN, rlebuffer.data()); - - const auto gzip_compressed_data = gzip::compress( - rlebuffer.data(), rle_compressed_size - ); - auto tmp = dataio::h2le(rle_compressed_size); - chunk_data.reserve(gzip_compressed_data.size() + sizeof(tmp)); - chunk_data.insert( - chunk_data.begin() + 0, (char*)&tmp, ((char*)&tmp) + sizeof(tmp) - ); - chunk_data.insert( - chunk_data.begin() + sizeof(tmp), - gzip_compressed_data.data(), - gzip_compressed_data.data() + gzip_compressed_data.size() - ); - } else { - const auto& data = chunk->encode(); - chunk_data.reserve(CHUNK_DATA_LEN); - chunk_data.insert( - chunk_data.begin(), data.get(), data.get() + CHUNK_DATA_LEN - ); - } - return lua::newuserdata(L, chunk_data); + auto chunkData = prepare_chunk_data(*chunk, compress); + return lua::newuserdata(L, std::move(chunkData)); } static int l_set_chunk_data(lua::State* L) { int x = static_cast(lua::tointeger(L, 1)); int y = static_cast(lua::tointeger(L, 2)); auto buffer = lua::touserdata(L, 3); - bool is_compressed = true; + bool isCompressed = true; if (lua::gettop(L) >= 4) { - is_compressed = lua::toboolean(L, 4); + isCompressed = lua::toboolean(L, 4); } auto chunk = level->chunks->getChunk(x, y); if (chunk == nullptr) { return 0; } - if (is_compressed) { - std::vector& raw_data = buffer->data(); - size_t gzip_decompressed_size = - dataio::le2h(*(size_t*)(raw_data.data())); - const auto& rle_data = compression::decompress( - raw_data.data() + sizeof(gzip_decompressed_size), - buffer->data().size() - sizeof(gzip_decompressed_size), - gzip_decompressed_size, + if (isCompressed) { + std::vector& rawData = buffer->data(); + size_t gzipDecompressedSize = dataio::le2h(*(size_t*)(rawData.data())); + auto rleData = compression::decompress( + rawData.data() + sizeof(gzipDecompressedSize), + buffer->data().size() - sizeof(gzipDecompressedSize), + gzipDecompressedSize, compression::Method::GZIP ); - const auto& data = compression::decompress( - rle_data.get(), - gzip_decompressed_size, + auto data = compression::decompress( + rleData.get(), + gzipDecompressedSize, CHUNK_DATA_LEN, compression::Method::EXTRLE16 ); From c5fa6935a6bea4b4d823d860fafadf31c45e0f9b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 12 Jan 2025 01:47:07 +0300 Subject: [PATCH 5/8] add metadata to chunk data used in world.get/set_chunk_data --- src/logic/scripting/lua/libs/libworld.cpp | 100 +++++++++------------- 1 file changed, 41 insertions(+), 59 deletions(-) diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 60183ab3..ddba261e 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -7,6 +7,7 @@ #include "coders/compression.hpp" #include "coders/gzip.hpp" #include "coders/json.hpp" +#include "coders/byte_utils.hpp" #include "coders/rle.hpp" #include "engine/Engine.hpp" #include "files/engine_paths.hpp" @@ -123,39 +124,29 @@ static int l_get_generator(lua::State* L) { return lua::pushstring(L, require_world_info().generator); } -static std::vector prepare_chunk_data(const Chunk& chunk, bool compress) { +static std::vector prepare_chunk_data(const Chunk& chunk) { auto data = chunk.encode(); - std::vector chunkData; - if (compress) { - // 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); - } - size_t rleCompressedSize = - extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data()); - - const auto gzipCompressedData = gzip::compress( - rleBuffer.data(), rleCompressedSize - ); - auto tmp = dataio::h2le(rleCompressedSize); - chunkData.reserve(gzipCompressedData.size() + sizeof(tmp)); - chunkData.insert( - chunkData.begin() + 0, (char*)&tmp, ((char*)&tmp) + sizeof(tmp) - ); - chunkData.insert( - chunkData.begin() + sizeof(tmp), - gzipCompressedData.data(), - gzipCompressedData.data() + gzipCompressedData.size() - ); - } else { - chunkData.reserve(CHUNK_DATA_LEN); - chunkData.insert( - chunkData.begin(), data.get(), data.get() + CHUNK_DATA_LEN - ); + /// 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); } - return chunkData; + size_t rleCompressedSize = + extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data()); + + const auto gzipCompressedData = gzip::compress( + rleBuffer.data(), rleCompressedSize + ); + auto metadataBytes = chunk.blocksMetadata.serialize(); + + ByteBuilder builder(2 + 8 + gzipCompressedData.size() + metadataBytes.size()); + builder.putInt16(0); // header + builder.putInt32(gzipCompressedData.size()); + builder.put(gzipCompressedData.data(), gzipCompressedData.size()); + builder.putInt32(metadataBytes.size()); + builder.put(metadataBytes.data(), metadataBytes.size()); + return builder.build(); } static int l_get_chunk_data(lua::State* L) { @@ -166,11 +157,7 @@ static int l_get_chunk_data(lua::State* L) { lua::pushnil(L); return 0; } - bool compress = true; - if (lua::gettop(L) >= 3) { - compress = lua::toboolean(L, 3); - } - auto chunkData = prepare_chunk_data(*chunk, compress); + auto chunkData = prepare_chunk_data(*chunk); return lua::newuserdata(L, std::move(chunkData)); } @@ -178,33 +165,29 @@ static int l_set_chunk_data(lua::State* L) { int x = static_cast(lua::tointeger(L, 1)); int y = static_cast(lua::tointeger(L, 2)); auto buffer = lua::touserdata(L, 3); - bool isCompressed = true; - if (lua::gettop(L) >= 4) { - isCompressed = lua::toboolean(L, 4); - } auto chunk = level->chunks->getChunk(x, y); if (chunk == nullptr) { return 0; } - if (isCompressed) { - std::vector& rawData = buffer->data(); - size_t gzipDecompressedSize = dataio::le2h(*(size_t*)(rawData.data())); - auto rleData = compression::decompress( - rawData.data() + sizeof(gzipDecompressedSize), - buffer->data().size() - sizeof(gzipDecompressedSize), - gzipDecompressedSize, - compression::Method::GZIP - ); - auto data = compression::decompress( - rleData.get(), - gzipDecompressedSize, - CHUNK_DATA_LEN, - compression::Method::EXTRLE16 - ); - chunk->decode(data.get()); - } else { - chunk->decode(buffer->data().data()); - } + + std::vector& rawData = buffer->data(); + + ByteReader reader(rawData.data(), rawData.size()); + + uint16_t header = reader.getInt16(); + 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()); + chunk->decode(voxelData.data()); + + size_t metadataSize = reader.getInt32(); + chunk->blocksMetadata.deserialize(reader.pointer(), metadataSize); + reader.skip(metadataSize); chunk->setModifiedAndUnsaved(); chunk->updateHeights(); @@ -233,7 +216,6 @@ static int l_set_chunk_data(lua::State* L) { } } } - return lua::pushboolean(L, true); } From 41f6194fc1354ef2b2c487b9c21946e5bb9c6bb3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 12 Jan 2025 03:05:58 +0300 Subject: [PATCH 6/8] introduce compressed_chunks namespace --- src/logic/scripting/lua/libs/libworld.cpp | 106 +++++++--------------- src/voxels/compressed_chunks.cpp | 61 +++++++++++++ src/voxels/compressed_chunks.hpp | 12 +++ 3 files changed, 104 insertions(+), 75 deletions(-) create mode 100644 src/voxels/compressed_chunks.cpp create mode 100644 src/voxels/compressed_chunks.hpp diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index ddba261e..5d936842 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -4,11 +4,7 @@ #include "api_lua.hpp" #include "assets/AssetsLoader.hpp" -#include "coders/compression.hpp" -#include "coders/gzip.hpp" #include "coders/json.hpp" -#include "coders/byte_utils.hpp" -#include "coders/rle.hpp" #include "engine/Engine.hpp" #include "files/engine_paths.hpp" #include "files/files.hpp" @@ -16,6 +12,7 @@ #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" #include "voxels/GlobalChunks.hpp" +#include "voxels/compressed_chunks.hpp" #include "world/Level.hpp" #include "world/World.hpp" #include "logic/LevelController.hpp" @@ -124,98 +121,57 @@ static int l_get_generator(lua::State* L) { return lua::pushstring(L, require_world_info().generator); } -static std::vector prepare_chunk_data(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); - } - size_t rleCompressedSize = - extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data()); - - const auto gzipCompressedData = gzip::compress( - rleBuffer.data(), rleCompressedSize - ); - auto metadataBytes = chunk.blocksMetadata.serialize(); - - ByteBuilder builder(2 + 8 + gzipCompressedData.size() + metadataBytes.size()); - builder.putInt16(0); // header - builder.putInt32(gzipCompressedData.size()); - builder.put(gzipCompressedData.data(), gzipCompressedData.size()); - builder.putInt32(metadataBytes.size()); - builder.put(metadataBytes.data(), metadataBytes.size()); - return builder.build(); -} - static int l_get_chunk_data(lua::State* L) { int x = static_cast(lua::tointeger(L, 1)); - int y = static_cast(lua::tointeger(L, 2)); - const auto& chunk = level->chunks->getChunk(x, y); + int z = static_cast(lua::tointeger(L, 2)); + const auto& chunk = level->chunks->getChunk(x, z); if (chunk == nullptr) { lua::pushnil(L); return 0; } - auto chunkData = prepare_chunk_data(*chunk); + auto chunkData = compressed_chunks::encode(*chunk); return lua::newuserdata(L, std::move(chunkData)); } -static int l_set_chunk_data(lua::State* L) { - int x = static_cast(lua::tointeger(L, 1)); - int y = static_cast(lua::tointeger(L, 2)); - auto buffer = lua::touserdata(L, 3); - auto chunk = level->chunks->getChunk(x, y); - if (chunk == nullptr) { - return 0; - } - - std::vector& rawData = buffer->data(); - - ByteReader reader(rawData.data(), rawData.size()); - - uint16_t header = reader.getInt16(); - 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()); - chunk->decode(voxelData.data()); - - size_t metadataSize = reader.getInt32(); - chunk->blocksMetadata.deserialize(reader.pointer(), metadataSize); - reader.skip(metadataSize); - - chunk->setModifiedAndUnsaved(); - chunk->updateHeights(); - +static void integrate_chunk_client(Chunk& chunk) { + int x = chunk.x; + int z = chunk.z; auto chunksController = controller->getChunksController(); - if (chunksController->lighting == nullptr) { - return lua::pushboolean(L, true); - } - Lighting& lighting = *chunksController->lighting; - chunk->flags.loadedLights = false; - chunk->flags.lighted = false; + chunk.flags.loadedLights = false; + chunk.flags.lighted = false; - Lighting::prebuildSkyLight(*chunk, *indices); - lighting.onChunkLoaded(x, y, true); + Lighting::prebuildSkyLight(chunk, *indices); + lighting.onChunkLoaded(x, z, true); for (int lz = -1; lz <= 1; lz++) { for (int lx = -1; lx <= 1; lx++) { if (std::abs(lx) + std::abs(lz) != 1) { continue; } - chunk = level->chunks->getChunk(x + lx, y + lz); - if (chunk != nullptr) { - chunk->flags.modified = true; - lighting.onChunkLoaded(x - 1, y, true); + if (auto other = level->chunks->getChunk(x + lx, z + lz)) { + other->flags.modified = true; + lighting.onChunkLoaded(x - 1, z, true); } } } +} + +static int l_set_chunk_data(lua::State* L) { + int x = static_cast(lua::tointeger(L, 1)); + int z = static_cast(lua::tointeger(L, 2)); + auto buffer = lua::touserdata(L, 3); + auto chunk = level->chunks->getChunk(x, z); + if (chunk == nullptr) { + return 0; + } + compressed_chunks::decode( + *chunk, buffer->data().data(), buffer->data().size() + ); + if (controller->getChunksController()->lighting == nullptr) { + return lua::pushboolean(L, true); + } + integrate_chunk_client(*chunk); return lua::pushboolean(L, true); } diff --git a/src/voxels/compressed_chunks.cpp b/src/voxels/compressed_chunks.cpp new file mode 100644 index 00000000..0e6772af --- /dev/null +++ b/src/voxels/compressed_chunks.cpp @@ -0,0 +1,61 @@ +#include "compressed_chunks.hpp" + +#include "coders/rle.hpp" +#include "coders/gzip.hpp" +#include "coders/byte_utils.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); + } + size_t rleCompressedSize = + extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data()); + + const auto gzipCompressedData = gzip::compress( + rleBuffer.data(), rleCompressedSize + ); + auto metadataBytes = chunk.blocksMetadata.serialize(); + + ByteBuilder builder(2 + 8 + gzipCompressedData.size() + metadataBytes.size()); + builder.put(HAS_VOXELS | HAS_METADATA); // flags + builder.put(0); // reserved + builder.putInt32(gzipCompressedData.size()); + builder.put(gzipCompressedData.data(), gzipCompressedData.size()); + builder.putInt32(metadataBytes.size()); + builder.put(metadataBytes.data(), metadataBytes.size()); + return builder.build(); +} + +void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) { + ByteReader reader(src, size); + + ubyte flags = reader.get(); + 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()); + chunk.decode(voxelData.data()); + chunk.updateHeights(); + } + if (flags & HAS_METADATA) { + size_t metadataSize = reader.getInt32(); + chunk.blocksMetadata.deserialize(reader.pointer(), metadataSize); + reader.skip(metadataSize); + } + chunk.setModifiedAndUnsaved(); +} diff --git a/src/voxels/compressed_chunks.hpp b/src/voxels/compressed_chunks.hpp new file mode 100644 index 00000000..dc0bc5b6 --- /dev/null +++ b/src/voxels/compressed_chunks.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "typedefs.hpp" + +#include + +class Chunk; + +namespace compressed_chunks { + std::vector encode(const Chunk& chunk); + void decode(Chunk& chunk, const ubyte* src, size_t size); +} From fc9c096873a6f69b1eb91b27abb1d57a77d8d7da Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 12 Jan 2025 03:34:50 +0300 Subject: [PATCH 7/8] cleanup --- src/voxels/Chunk.cpp | 13 ------------- src/voxels/Chunk.hpp | 4 ++-- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/voxels/Chunk.cpp b/src/voxels/Chunk.cpp index a32fc8ba..2c1f4fd8 100644 --- a/src/voxels/Chunk.cpp +++ b/src/voxels/Chunk.cpp @@ -13,19 +13,6 @@ Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos) { top = CHUNK_H; } -bool Chunk::isEmpty() const { - int id = -1; - for (uint i = 0; i < CHUNK_VOL; i++) { - if (voxels[i].id != id) { - if (id != -1) - return false; - else - id = voxels[i].id; - } - } - return true; -} - void Chunk::updateHeights() { for (uint i = 0; i < CHUNK_VOL; i++) { if (voxels[i].id != 0) { diff --git a/src/voxels/Chunk.hpp b/src/voxels/Chunk.hpp index 55cdac21..3ce6e641 100644 --- a/src/voxels/Chunk.hpp +++ b/src/voxels/Chunk.hpp @@ -11,6 +11,7 @@ #include "maths/aabb.hpp" #include "voxel.hpp" +/// @brief Total bytes number of chunk voxel data inline constexpr int CHUNK_DATA_LEN = CHUNK_VOL * 4; class ContentReport; @@ -45,8 +46,7 @@ public: Chunk(int x, int z); - bool isEmpty() const; - + /// @brief Refresh `bottom` and `top` values void updateHeights(); // unused From cafaa3a168ba162859f12f93a38a29c4168e6abb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 12 Jan 2025 03:43:35 +0300 Subject: [PATCH 8/8] update doc/*/scripting/builtins/libworld.md --- doc/en/scripting/builtins/libworld.md | 14 ++++++++++++++ doc/ru/scripting/builtins/libworld.md | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/doc/en/scripting/builtins/libworld.md b/doc/en/scripting/builtins/libworld.md index fc18773e..97c6fdee 100644 --- a/doc/en/scripting/builtins/libworld.md +++ b/doc/en/scripting/builtins/libworld.md @@ -47,4 +47,18 @@ world.is_night() -> bool -- Returns the total number of chunks loaded into memory world.count_chunks() -> int + +-- Returns the compressed chunk data to send. +-- Currently includes: +-- 1. Voxel data (id and state) +-- 2. Voxel metadata (fields) +world.get_chunk_data(x: int, z: int) -> Bytearray + +-- Modifies the chunk based on the compressed data. +-- Returns true if the chunk exists. +world.set_chunk_data( + x: int, z: int, + -- compressed chunk data + data: Bytearray +) -> bool ``` diff --git a/doc/ru/scripting/builtins/libworld.md b/doc/ru/scripting/builtins/libworld.md index 0467d784..a7e0e5a6 100644 --- a/doc/ru/scripting/builtins/libworld.md +++ b/doc/ru/scripting/builtins/libworld.md @@ -46,4 +46,18 @@ world.is_night() -> bool -- Возвращает общее количество загруженных в память чанков world.count_chunks() -> int + +-- Возвращает сжатые данные чанка для отправки. +-- На данный момент включает: +-- 1. Данные вокселей (id и состояние) +-- 2. Метаданные (поля) вокселей +world.get_chunk_data(x: int, z: int) -> Bytearray + +-- Изменяет чанк на основе сжатых данных. +-- Возвращает true если чанк существует. +world.set_chunk_data( + x: int, z: int, + -- сжатые данные чанка + data: Bytearray +) -> bool ```