From 49defaeb3b2fd51f82f0d00a4f3dcf6964cc0e8f Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Mon, 2 Dec 2024 02:33:38 +0300 Subject: [PATCH 1/4] Add api to serialize and deserialize chunk to bytes --- src/logic/scripting/lua/libs/libworld.cpp | 99 ++++++++++++++++++++--- 1 file changed, 88 insertions(+), 11 deletions(-) diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 5422c873..b51bc7f8 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -1,16 +1,20 @@ #include -#include #include +#include +#include "api_lua.hpp" #include "assets/Assets.hpp" #include "assets/AssetsLoader.hpp" +#include "coders/gzip.hpp" #include "coders/json.hpp" #include "engine.hpp" -#include "files/files.hpp" #include "files/engine_paths.hpp" +#include "files/files.hpp" +#include "lighting/Lighting.hpp" +#include "voxels/Chunk.hpp" +#include "voxels/Chunks.hpp" #include "world/Level.hpp" #include "world/World.hpp" -#include "api_lua.hpp" using namespace scripting; namespace fs = std::filesystem; @@ -36,12 +40,12 @@ static int l_get_list(lua::State* L) { const auto& folder = worlds[i]; - auto root = json::parse(files::read_string(folder/fs::u8path("world.json"))); + auto root = + json::parse(files::read_string(folder / fs::u8path("world.json"))); const auto& versionMap = root["version"]; int versionMajor = versionMap["major"].asInteger(); int versionMinor = versionMap["minor"].asInteger(); - auto name = folder.filename().u8string(); lua::pushstring(L, name); lua::setfield(L, "name"); @@ -49,11 +53,11 @@ static int l_get_list(lua::State* L) { auto assets = engine->getAssets(); std::string icon = "world#" + name + ".icon"; if (!AssetsLoader::loadExternalTexture( - assets, - icon, - {worlds[i] / fs::path("icon.png"), - worlds[i] / fs::path("preview.png")} - )) { + assets, + icon, + {worlds[i] / fs::path("icon.png"), + worlds[i] / fs::path("preview.png")} + )) { icon = "gui/no_world_icon"; } lua::pushstring(L, icon); @@ -115,6 +119,76 @@ static int l_get_generator(lua::State* L) { return lua::pushstring(L, require_world_info().generator); } +static int l_get_chunk_data(lua::State* L) { + int x = (int)lua::tointeger(L, 1); + int y = (int)lua::tointeger(L, 2); + const auto& chunk = level->chunks->getChunk(x, y); + if (chunk->isEmpty()) { + return 0; + } + + bool compress = false; + if (lua::gettop(L) >= 3) { + compress = lua::toboolean(L, 3); + } + + if (compress) { + return lua::newuserdata( + L, gzip::compress(chunk->encode().get(), CHUNK_DATA_LEN) + ); + } + const auto chunk_data = chunk->encode(); + const std::vector data( + chunk_data.get(), chunk_data.get() + CHUNK_DATA_LEN + ); + return lua::newuserdata(L, data); +} + +static int l_set_chunk_data(lua::State* L) { + int x = (int)lua::tointeger(L, 1); + int y = (int)lua::tointeger(L, 2); + auto buffer = lua::touserdata(L, 3); + bool is_compressed = false; + if (lua::gettop(L) >= 4) { + is_compressed = lua::toboolean(L, 4); + } + auto chunk = level->chunks->getChunk(x, y); + if (is_compressed) { + auto data = + gzip::decompress(buffer->data().data(), buffer->data().size()); + chunk->decode(data.data()); + } else { + chunk->decode(buffer->data().data()); + } + chunk->updateHeights(); + level->lighting->buildSkyLight(x, y); + chunk->flags.modified = true; + level->lighting->onChunkLoaded(x, y, true); + + chunk = level->chunks->getChunk(x - 1, y); + if (chunk != nullptr) { + chunk->flags.modified = true; + level->lighting->onChunkLoaded(x - 1, y, true); + } + chunk = level->chunks->getChunk(x + 1, y); + if (chunk != nullptr) { + chunk->flags.modified = true; + level->lighting->onChunkLoaded(x + 1, y, true); + } + chunk = level->chunks->getChunk(x, y - 1); + if (chunk != nullptr) { + chunk->flags.modified = true; + level->lighting->onChunkLoaded(x, y - 1, true); + } + chunk = level->chunks->getChunk(x, y + 1); + if (chunk != nullptr) { + chunk->flags.modified = true; + level->lighting->onChunkLoaded(x, y + 1, true); + } + + return 1; +} + const luaL_Reg worldlib[] = { {"is_open", lua::wrap}, {"get_list", lua::wrap}, @@ -128,4 +202,7 @@ const luaL_Reg worldlib[] = { {"is_day", lua::wrap}, {"is_night", lua::wrap}, {"exists", lua::wrap}, - {NULL, NULL}}; + {"get_chunk_data", lua::wrap}, + {"set_chunk_data", lua::wrap}, + {NULL, NULL} +}; From ad81e644616a8c773633a6a31971e506a7b84340 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Mon, 2 Dec 2024 21:26:44 +0300 Subject: [PATCH 2/4] Add EXTRLE16+GZIP combo for chunk compression --- CMakeLists.txt | 8 +++ src/logic/scripting/lua/libs/libworld.cpp | 65 ++++++++++++++++++----- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f465fcb3..61177810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,3 +82,11 @@ if (VOXELENGINE_BUILD_TESTS) enable_testing() add_subdirectory(test) endif() + +add_custom_target(copy_resources ALL + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/res + ${CMAKE_CURRENT_BINARY_DIR}/res + COMMENT "Copying resource directory to the build directory" +) +add_dependencies(${PROJECT_NAME} copy_resources) \ No newline at end of file diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index b51bc7f8..0d476d16 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -3,8 +3,8 @@ #include #include "api_lua.hpp" -#include "assets/Assets.hpp" #include "assets/AssetsLoader.hpp" +#include "coders/compression.hpp" #include "coders/gzip.hpp" #include "coders/json.hpp" #include "engine.hpp" @@ -123,7 +123,8 @@ static int l_get_chunk_data(lua::State* L) { int x = (int)lua::tointeger(L, 1); int y = (int)lua::tointeger(L, 2); const auto& chunk = level->chunks->getChunk(x, y); - if (chunk->isEmpty()) { + if (chunk == nullptr) { + lua::pushnil(L); return 0; } @@ -131,17 +132,42 @@ static int l_get_chunk_data(lua::State* L) { if (lua::gettop(L) >= 3) { compress = lua::toboolean(L, 3); } - + std::vector chunk_data; if (compress) { - return lua::newuserdata( - L, gzip::compress(chunk->encode().get(), CHUNK_DATA_LEN) + size_t rle_compressed_size; + size_t gzip_compressed_size; + 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 + ); + auto tmp = dataio::h2le(rle_compressed_size); + chunk_data.reserve(gzip_compressed_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 + ); + } 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 ); } - const auto chunk_data = chunk->encode(); - const std::vector data( - chunk_data.get(), chunk_data.get() + CHUNK_DATA_LEN - ); - return lua::newuserdata(L, data); + return lua::newuserdata(L, chunk_data); } static int l_set_chunk_data(lua::State* L) { @@ -154,9 +180,22 @@ static int l_set_chunk_data(lua::State* L) { } auto chunk = level->chunks->getChunk(x, y); if (is_compressed) { - auto data = - gzip::decompress(buffer->data().data(), buffer->data().size()); - chunk->decode(data.data()); + 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, + compression::Method::GZIP + ); + const auto& data = compression::decompress( + rle_data.get(), + gzip_decompressed_size, + CHUNK_DATA_LEN, + compression::Method::EXTRLE16 + ); + chunk->decode(data.get()); } else { chunk->decode(buffer->data().data()); } From 57e9cade7b9391f1a112271bfb910444e14b6594 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Mon, 2 Dec 2024 21:27:37 +0300 Subject: [PATCH 3/4] Potential nullptr access fix --- src/logic/scripting/lua/libs/libworld.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 0d476d16..99d2fec3 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -179,6 +179,9 @@ static int l_set_chunk_data(lua::State* L) { is_compressed = 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 = From 5b6cee1bf586bfd9ea3a76f09f9a48df0f9bf2c2 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Fri, 6 Dec 2024 21:44:12 +0300 Subject: [PATCH 4/4] Remove custom target --- CMakeLists.txt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61177810..a56457bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,12 +81,4 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR if (VOXELENGINE_BUILD_TESTS) enable_testing() add_subdirectory(test) -endif() - -add_custom_target(copy_resources ALL - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/res - ${CMAKE_CURRENT_BINARY_DIR}/res - COMMENT "Copying resource directory to the build directory" -) -add_dependencies(${PROJECT_NAME} copy_resources) \ No newline at end of file +endif() \ No newline at end of file