From 87f50f79c9b46fdcacadb4fb6a2113a31c530c96 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 12 Feb 2024 18:28:18 +0300 Subject: [PATCH] block inventories (backend) --- src/coders/byte_utils.cpp | 8 ++++++ src/coders/byte_utils.h | 3 +++ src/files/WorldFiles.cpp | 47 +++++++++++++++++++++++++--------- src/files/WorldFiles.h | 8 +++--- src/items/Inventory.cpp | 5 +++- src/logic/BlocksController.cpp | 22 ++++++++++++++++ src/logic/BlocksController.h | 2 +- src/logic/scripting/api_lua.h | 10 ++++++++ src/voxels/Chunk.cpp | 10 ++++++++ src/voxels/Chunk.h | 6 ++++- src/voxels/Chunks.cpp | 3 +++ src/voxels/ChunksStorage.cpp | 2 ++ 12 files changed, 108 insertions(+), 18 deletions(-) diff --git a/src/coders/byte_utils.cpp b/src/coders/byte_utils.cpp index 5b9876a7..358eab5e 100644 --- a/src/coders/byte_utils.cpp +++ b/src/coders/byte_utils.cpp @@ -200,3 +200,11 @@ std::string ByteReader::getString() { bool ByteReader::hasNext() const { return pos < size; } + +const ubyte* ByteReader::pointer() const { + return data + pos; +} + +void ByteReader::skip(size_t n) { + pos += n; +} \ No newline at end of file diff --git a/src/coders/byte_utils.h b/src/coders/byte_utils.h index 7bd2846d..4d651892 100644 --- a/src/coders/byte_utils.h +++ b/src/coders/byte_utils.h @@ -71,6 +71,9 @@ public: const char* getCString(); std::string getString(); bool hasNext() const; + + const ubyte* pointer() const; + void skip(size_t n); }; #endif // CODERS_BYTE_UTILS_H_ diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index 6b011909..b0263c92 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -29,6 +29,8 @@ #include #include +const size_t BUFFER_SIZE_UNKNOWN = -1; + regfile::regfile(fs::path filename) : file(filename) { if (file.length() < REGION_HEADER_SIZE) throw std::runtime_error("incomplete region file header"); @@ -217,7 +219,7 @@ void WorldFiles::put(Chunk* chunk){ for (auto& entry : inventories) { builder.putInt32(entry.first); auto map = entry.second->serialize(); - auto bytes = json::to_binary(map.get(), true); + auto bytes = json::to_binary(map.get(), false); builder.putInt32(bytes.size()); builder.put(bytes.data(), bytes.size()); } @@ -226,8 +228,8 @@ void WorldFiles::put(Chunk* chunk){ auto datavec = builder.data(); uint datasize = builder.size(); - auto data = std::make_unique(builder.size()); - for (uint i = 0; i < builder.size(); i++) { + auto data = std::make_unique(datasize); + for (uint i = 0; i < datasize; i++) { data[i] = datavec[i]; } region->put(localX, localZ, data.release(), datasize); @@ -289,20 +291,39 @@ fs::path WorldFiles::getPacksFile() const { } ubyte* WorldFiles::getChunk(int x, int z){ - return getData(regions, getRegionsFolder(), x, z, REGION_LAYER_VOXELS); + return getData(regions, getRegionsFolder(), x, z, REGION_LAYER_VOXELS, true); } /* Get cached lights for chunk at x,z * @return lights data or nullptr */ light_t* WorldFiles::getLights(int x, int z) { - std::unique_ptr data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS)); + std::unique_ptr data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS, true)); if (data == nullptr) return nullptr; return Lightmap::decode(data.get()); } +chunk_inventories_map WorldFiles::fetchInventories(int x, int z) { + chunk_inventories_map inventories; + const ubyte* data = getData(storages, getInventoriesFolder(), x, z, REGION_LAYER_INVENTORIES, false); + if (data == nullptr) + return inventories; + ByteReader reader(data, BUFFER_SIZE_UNKNOWN); + int count = reader.getInt32(); + for (int i = 0; i < count; i++) { + uint index = reader.getInt32(); + uint size = reader.getInt32(); + auto map = json::from_binary(reader.pointer(), size); + reader.skip(size); + auto inv = std::make_shared(0, 0); + inv->deserialize(map.get()); + inventories[index] = inv; + } + return inventories; +} + ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder, - int x, int z, int layer) { + int x, int z, int layer, bool compression) { int regionX = floordiv(x, REGION_SIZE); int regionZ = floordiv(z, REGION_SIZE); @@ -320,7 +341,10 @@ ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder, } if (data != nullptr) { size_t size = region->getChunkDataSize(localX, localZ); - return decompress(data, size, CHUNK_DATA_LEN); + if (compression) { + return decompress(data, size, CHUNK_DATA_LEN); + } + return data; } return nullptr; } @@ -372,17 +396,16 @@ ubyte* WorldFiles::readChunkData(int x, file.seekg(table_offset + chunkIndex * 4); file.read((char*)(&offset), 4); offset = dataio::read_int32_big((const ubyte*)(&offset), 0); + if (offset == 0){ return nullptr; } + file.seekg(offset); file.read((char*)(&offset), 4); length = dataio::read_int32_big((const ubyte*)(&offset), 0); - ubyte* data = new ubyte[length]; + ubyte* data = new ubyte[length]{}; file.read((char*)data, length); - if (data == nullptr) { - std::cerr << "ERROR: failed to read data of chunk x("<< x <<"), z("<< z <<")" << std::endl; - } return data; } @@ -482,7 +505,7 @@ void WorldFiles::write(const World* world, const Content* content) { writeIndices(content->getIndices()); writeRegions(regions, regionsFolder, REGION_LAYER_VOXELS); writeRegions(lights, lightsFolder, REGION_LAYER_LIGHTS); - writeRegions(storages, inventoriesFolder, REGION_LAYER_STORAGES); + writeRegions(storages, inventoriesFolder, REGION_LAYER_INVENTORIES); } void WorldFiles::writePacks(const World* world) { diff --git a/src/files/WorldFiles.h b/src/files/WorldFiles.h index a6f9d534..34203b97 100644 --- a/src/files/WorldFiles.h +++ b/src/files/WorldFiles.h @@ -15,11 +15,13 @@ #include "../typedefs.h" #include "../settings.h" +#include "../voxels/Chunk.h" + const uint REGION_HEADER_SIZE = 10; const uint REGION_LAYER_VOXELS = 0; const uint REGION_LAYER_LIGHTS = 1; -const uint REGION_LAYER_STORAGES = 2; +const uint REGION_LAYER_INVENTORIES = 2; const uint REGION_SIZE_BIT = 5; const uint REGION_SIZE = (1 << (REGION_SIZE_BIT)); @@ -32,7 +34,6 @@ const uint MAX_OPEN_REGION_FILES = 16; #define WORLD_FORMAT_MAGIC ".VOXWLD" class Player; -class Chunk; class Content; class ContentIndices; class World; @@ -109,7 +110,7 @@ class WorldFiles { ubyte* getData(regionsmap& regions, const fs::path& folder, - int x, int z, int layer); + int x, int z, int layer, bool compression); regfile* getRegFile(glm::ivec3 coord, const fs::path& folder); @@ -139,6 +140,7 @@ public: ubyte* getChunk(int x, int z); light_t* getLights(int x, int z); + chunk_inventories_map fetchInventories(int x, int z); bool readWorldInfo(World* world); bool readPlayer(Player* player); diff --git a/src/items/Inventory.cpp b/src/items/Inventory.cpp index e844d18b..256e9083 100644 --- a/src/items/Inventory.cpp +++ b/src/items/Inventory.cpp @@ -47,7 +47,10 @@ void Inventory::move( void Inventory::deserialize(dynamic::Map* src) { id = src->getNum("id", 1); auto slotsarr = src->list("slots"); - size_t slotscount = std::min(slotsarr->size(), slots.size()); + size_t slotscount = slotsarr->size(); + while (slots.size() < slotscount) { + slots.push_back(ItemStack()); + } for (size_t i = 0; i < slotscount; i++) { auto item = slotsarr->map(i); itemid_t id = item->getInt("id", ITEM_EMPTY); diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index ddf78cf8..03e4e9c1 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -11,6 +11,7 @@ #include "../util/timeutil.h" #include "../maths/fastmaths.h" #include "../items/Inventory.h" +#include "../items/Inventories.h" #include "scripting/scripting.h" @@ -147,3 +148,24 @@ void BlocksController::randomTick(int tickid, int parts) { } } } + +int64_t BlocksController::createBlockInventory(int x, int y, int z) { + auto chunk = chunks->getChunkByVoxel(x, y, z); + if (chunk == nullptr) { + return 0; + } + int lx = x - chunk->x * CHUNK_W; + int lz = z - chunk->z * CHUNK_D; + auto inv = chunk->getBlockInventory(lx, y, lz); + if (inv == nullptr) { + auto indices = level->content->getIndices(); + auto def = indices->getBlockDef(chunk->voxels[vox_index(lx, y, lz)].id); + int invsize = def->inventorySize; + if (invsize == 0) { + return 0; + } + inv = level->inventories->create(invsize); + chunk->addBlockInventory(inv, lx, y, lz); + } + return inv->getId(); +} diff --git a/src/logic/BlocksController.h b/src/logic/BlocksController.h index b4664f9d..c4171f80 100644 --- a/src/logic/BlocksController.h +++ b/src/logic/BlocksController.h @@ -47,7 +47,7 @@ public: void update(float delta); void randomTick(int tickid, int parts); void onBlocksTick(int tickid, int parts); - uint createBlockInventory(int x, int y, int z); + int64_t createBlockInventory(int x, int y, int z); }; #endif // LOGIC_BLOCKS_CONTROLLER_H_ diff --git a/src/logic/scripting/api_lua.h b/src/logic/scripting/api_lua.h index 20dece5c..2bb3a2aa 100644 --- a/src/logic/scripting/api_lua.h +++ b/src/logic/scripting/api_lua.h @@ -299,11 +299,21 @@ static int l_inventory_add(lua_State* L) { return 1; } +static int l_inventory_get_block(lua_State* L) { + lua::luaint x = lua_tointeger(L, 1); + lua::luaint y = lua_tointeger(L, 2); + lua::luaint z = lua_tointeger(L, 3); + int64_t id = scripting::blocks->createBlockInventory(x, y, z); + lua_pushinteger(L, id); + return 1; +} + static const luaL_Reg inventorylib [] = { {"get", l_inventory_get}, {"set", l_inventory_set}, {"size", l_inventory_size}, {"add", l_inventory_add}, + {"get_block", l_inventory_get_block}, {NULL, NULL} }; diff --git a/src/voxels/Chunk.cpp b/src/voxels/Chunk.cpp index 8bca5b1e..2cdcf1a6 100644 --- a/src/voxels/Chunk.cpp +++ b/src/voxels/Chunk.cpp @@ -49,6 +49,16 @@ void Chunk::addBlockInventory(std::shared_ptr inventory, setUnsaved(true); } +void Chunk::removeBlockInventory(uint x, uint y, uint z) { + if (inventories.erase(vox_index(x, y, z))) { + setUnsaved(true); + } +} + +void Chunk::setBlockInventories(chunk_inventories_map map) { + inventories = map; +} + std::shared_ptr Chunk::getBlockInventory(uint x, uint y, uint z) const { if (x >= CHUNK_W || y >= CHUNK_H || z >= CHUNK_D) return nullptr; diff --git a/src/voxels/Chunk.h b/src/voxels/Chunk.h index 48d87f67..55978b2b 100644 --- a/src/voxels/Chunk.h +++ b/src/voxels/Chunk.h @@ -23,6 +23,8 @@ class Lightmap; class ContentLUT; class Inventory; +using chunk_inventories_map = std::unordered_map>; + class Chunk { public: int x, z; @@ -32,7 +34,7 @@ public: int flags = 0; /* Block inventories map where key is index of block in voxels array */ - std::unordered_map> inventories; + chunk_inventories_map inventories; Chunk(int x, int z); @@ -55,6 +57,8 @@ public: @return inventory id or 0 if block does not exists */ void addBlockInventory(std::shared_ptr inventory, uint x, uint y, uint z); + void removeBlockInventory(uint x, uint y, uint z); + void setBlockInventories(chunk_inventories_map map); /* @return inventory bound to the given block or nullptr */ std::shared_ptr getBlockInventory(uint x, uint y, uint z) const; diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 2da9cf8b..f29b59a0 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -162,6 +162,9 @@ void Chunks::set(int x, int y, int z, int id, uint8_t states){ int lz = z - cz * CHUNK_D; voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; + auto def = contentIds->getBlockDef(vox.id); + if (def->inventorySize == 0) + chunk->removeBlockInventory(lx, y, lz); vox.id = id; vox.states = states; diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index caf51d74..fe45ae20 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -57,6 +57,8 @@ std::shared_ptr ChunksStorage::create(int x, int z) { std::unique_ptr data(wfile->getChunk(chunk->x, chunk->z)); if (data) { chunk->decode(data.get()); + auto invs = wfile->fetchInventories(chunk->x, chunk->z); + chunk->setBlockInventories(std::move(invs)); chunk->setLoaded(true); verifyLoadedChunk(level->content->getIndices(), chunk.get()); }