introduce compressed_chunks namespace

This commit is contained in:
MihailRis 2025-01-12 03:05:58 +03:00
parent c5fa6935a6
commit 41f6194fc1
3 changed files with 104 additions and 75 deletions

View File

@ -4,11 +4,7 @@
#include "api_lua.hpp" #include "api_lua.hpp"
#include "assets/AssetsLoader.hpp" #include "assets/AssetsLoader.hpp"
#include "coders/compression.hpp"
#include "coders/gzip.hpp"
#include "coders/json.hpp" #include "coders/json.hpp"
#include "coders/byte_utils.hpp"
#include "coders/rle.hpp"
#include "engine/Engine.hpp" #include "engine/Engine.hpp"
#include "files/engine_paths.hpp" #include "files/engine_paths.hpp"
#include "files/files.hpp" #include "files/files.hpp"
@ -16,6 +12,7 @@
#include "voxels/Chunk.hpp" #include "voxels/Chunk.hpp"
#include "voxels/Chunks.hpp" #include "voxels/Chunks.hpp"
#include "voxels/GlobalChunks.hpp" #include "voxels/GlobalChunks.hpp"
#include "voxels/compressed_chunks.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
#include "world/World.hpp" #include "world/World.hpp"
#include "logic/LevelController.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); return lua::pushstring(L, require_world_info().generator);
} }
static std::vector<ubyte> 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<ubyte> rleBuffer;
if (rleBuffer.size() < CHUNK_DATA_LEN * 2) {
rleBuffer = util::Buffer<ubyte>(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) { static int l_get_chunk_data(lua::State* L) {
int x = static_cast<int>(lua::tointeger(L, 1)); int x = static_cast<int>(lua::tointeger(L, 1));
int y = static_cast<int>(lua::tointeger(L, 2)); int z = static_cast<int>(lua::tointeger(L, 2));
const auto& chunk = level->chunks->getChunk(x, y); const auto& chunk = level->chunks->getChunk(x, z);
if (chunk == nullptr) { if (chunk == nullptr) {
lua::pushnil(L); lua::pushnil(L);
return 0; return 0;
} }
auto chunkData = prepare_chunk_data(*chunk); auto chunkData = compressed_chunks::encode(*chunk);
return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData)); return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData));
} }
static int l_set_chunk_data(lua::State* L) { static void integrate_chunk_client(Chunk& chunk) {
int x = static_cast<int>(lua::tointeger(L, 1)); int x = chunk.x;
int y = static_cast<int>(lua::tointeger(L, 2)); int z = chunk.z;
auto buffer = lua::touserdata<lua::LuaBytearray>(L, 3);
auto chunk = level->chunks->getChunk(x, y);
if (chunk == nullptr) {
return 0;
}
std::vector<ubyte>& 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<ubyte> 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();
auto chunksController = controller->getChunksController(); auto chunksController = controller->getChunksController();
if (chunksController->lighting == nullptr) {
return lua::pushboolean(L, true);
}
Lighting& lighting = *chunksController->lighting; Lighting& lighting = *chunksController->lighting;
chunk->flags.loadedLights = false; chunk.flags.loadedLights = false;
chunk->flags.lighted = false; chunk.flags.lighted = false;
Lighting::prebuildSkyLight(*chunk, *indices); Lighting::prebuildSkyLight(chunk, *indices);
lighting.onChunkLoaded(x, y, true); lighting.onChunkLoaded(x, z, true);
for (int lz = -1; lz <= 1; lz++) { for (int lz = -1; lz <= 1; lz++) {
for (int lx = -1; lx <= 1; lx++) { for (int lx = -1; lx <= 1; lx++) {
if (std::abs(lx) + std::abs(lz) != 1) { if (std::abs(lx) + std::abs(lz) != 1) {
continue; continue;
} }
chunk = level->chunks->getChunk(x + lx, y + lz); if (auto other = level->chunks->getChunk(x + lx, z + lz)) {
if (chunk != nullptr) { other->flags.modified = true;
chunk->flags.modified = true; lighting.onChunkLoaded(x - 1, z, true);
lighting.onChunkLoaded(x - 1, y, true);
} }
} }
} }
}
static int l_set_chunk_data(lua::State* L) {
int x = static_cast<int>(lua::tointeger(L, 1));
int z = static_cast<int>(lua::tointeger(L, 2));
auto buffer = lua::touserdata<lua::LuaBytearray>(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); return lua::pushboolean(L, true);
} }

View File

@ -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<ubyte> 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<ubyte> rleBuffer;
if (rleBuffer.size() < CHUNK_DATA_LEN * 2) {
rleBuffer = util::Buffer<ubyte>(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<ubyte> 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();
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "typedefs.hpp"
#include <vector>
class Chunk;
namespace compressed_chunks {
std::vector<ubyte> encode(const Chunk& chunk);
void decode(Chunk& chunk, const ubyte* src, size_t size);
}