block inventories (backend)

This commit is contained in:
MihailRis 2024-02-12 18:28:18 +03:00
parent fe57c38edf
commit 87f50f79c9
12 changed files with 108 additions and 18 deletions

View File

@ -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;
}

View File

@ -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_

View File

@ -29,6 +29,8 @@
#include <sstream>
#include <cstring>
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<ubyte[]>(builder.size());
for (uint i = 0; i < builder.size(); i++) {
auto data = std::make_unique<ubyte[]>(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<ubyte> data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS));
std::unique_ptr<ubyte[]> 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<Inventory>(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) {

View File

@ -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);

View File

@ -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);

View File

@ -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();
}

View File

@ -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_

View File

@ -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}
};

View File

@ -49,6 +49,16 @@ void Chunk::addBlockInventory(std::shared_ptr<Inventory> 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<Inventory> Chunk::getBlockInventory(uint x, uint y, uint z) const {
if (x >= CHUNK_W || y >= CHUNK_H || z >= CHUNK_D)
return nullptr;

View File

@ -23,6 +23,8 @@ class Lightmap;
class ContentLUT;
class Inventory;
using chunk_inventories_map = std::unordered_map<uint, std::shared_ptr<Inventory>>;
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<uint, std::shared_ptr<Inventory>> 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> 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<Inventory> getBlockInventory(uint x, uint y, uint z) const;

View File

@ -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;

View File

@ -57,6 +57,8 @@ std::shared_ptr<Chunk> ChunksStorage::create(int x, int z) {
std::unique_ptr<ubyte[]> 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());
}