#include <stdlib.h>
#include <stdbool.h>
+#include <unistd.h>
#include <math.h>
+#include <endian.h>
+#include <zlib.h>
#include "map.h"
#include "util.h"
-#define CMPBOUNDS(x) x == 0 ? 0 : x > 0 ? 1 : -1
-
-static s8 sector_compare(void *hash, void *sector)
-{
- s64 d = *((u64 *) hash) - (*(MapSector **) sector)->hash;
- return CMPBOUNDS(d);
-}
-
-static s8 block_compare(void *level, void *block)
-{
- s32 d = *((s32 *) level) - (*(MapBlock **) block)->pos.y;
- return CMPBOUNDS(d);
-}
-
-static MapBlock *allocate_block(v3s32 pos)
-{
- MapBlock *block = malloc(sizeof(MapBlock));
- block->pos = pos;
- block->extra = NULL;
- return block;
-}
-
-static MapBlock **get_block_ptr(MapSector *sector, size_t idx)
+Map *map_create()
{
- return (MapBlock **) sector->blocks.ptr + idx;
+ Map *map = malloc(sizeof(Map));
+ pthread_rwlock_init(&map->rwlck, NULL);
+ pthread_rwlock_init(&map->cached_rwlck, NULL);
+ map->sectors = bintree_create(sizeof(v2s32));
+ map->cached = NULL;
+ return map;
}
-static MapSector **get_sector_ptr(Map *map, size_t idx)
+static void free_block(void *block)
{
- return (MapSector **) map->sectors.ptr + idx;
+ map_free_block(block);
}
-Map *map_create()
+static void free_sector(void *sector_void)
{
- Map *map = malloc(sizeof(Map));
- map->sectors = array_create(sizeof(MapSector *));
- map->sectors.cmp = §or_compare;
- return map;
+ MapSector *sector = sector_void;
+ bintree_clear(§or->blocks, &free_block);
+ pthread_rwlock_destroy(§or->rwlck);
+ free(sector);
}
void map_delete(Map *map)
{
- for (size_t s = 0; s < map->sectors.siz; s++) {
- MapSector *sector = *get_sector_ptr(map, s);
- for (size_t b = 0; b < sector->blocks.siz; b++)
- map_free_block(*get_block_ptr(sector, b));
- if (sector->blocks.ptr)
- free(sector->blocks.ptr);
- free(sector);
- }
- if (map->sectors.ptr)
- free(map->sectors.ptr);
+ pthread_rwlock_destroy(&map->rwlck);
+ pthread_rwlock_destroy(&map->cached_rwlck);
+ bintree_clear(&map->sectors, &free_sector);
free(map);
}
MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
{
- u64 hash = ((u64) pos.x << 32) + (u64) pos.y;
- ArraySearchResult res = array_search(&map->sectors, &hash);
+ if (create)
+ pthread_rwlock_wrlock(&map->rwlck);
+ else
+ pthread_rwlock_rdlock(&map->rwlck);
- if (res.success)
- return *get_sector_ptr(map, res.index);
- if (! create)
- return NULL;
+ BintreeNode **nodeptr = bintree_search(&map->sectors, &pos);
- MapSector *sector = malloc(sizeof(MapSector));
- sector->pos = pos;
- sector->hash = hash;
- sector->blocks = array_create(sizeof(MapBlock *));
- sector->blocks.cmp = &block_compare;
+ MapSector *sector = NULL;
- array_insert(&map->sectors, §or, res.index);
+ if (*nodeptr) {
+ sector = (*nodeptr)->value;
+ } else if (create) {
+ sector = malloc(sizeof(MapSector));
+ pthread_rwlock_init(§or->rwlck, NULL);
+ sector->pos = pos;
+ sector->blocks = bintree_create(sizeof(s32));
+
+ bintree_add_node(&map->sectors, nodeptr, &pos, sector);
+ }
+
+ pthread_rwlock_unlock(&map->rwlck);
return sector;
}
MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
{
+ MapBlock *cached = NULL;
+
+ pthread_rwlock_rdlock(&map->cached_rwlck);
+ cached = map->cached;
+ pthread_rwlock_unlock(&map->cached_rwlck);
+
+ if (cached && cached->pos.x == pos.x && cached->pos.y == pos.y && cached->pos.z == pos.z)
+ return cached;
+
MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
if (! sector)
return NULL;
- ArraySearchResult res = array_search(§or->blocks, &pos.y);
+ if (create)
+ pthread_rwlock_wrlock(§or->rwlck);
+ else
+ pthread_rwlock_rdlock(§or->rwlck);
+
+ BintreeNode **nodeptr = bintree_search(§or->blocks, &pos.y);
MapBlock *block = NULL;
- if (res.success) {
- block = *get_block_ptr(sector, res.index);
- } else if (create) {
- block = allocate_block(pos);
+ if (*nodeptr) {
+ block = (*nodeptr)->value;
- if (map->on_block_create)
- map->on_block_create(block);
+ if (block->state < MBS_READY) {
+ if (! create)
+ block = NULL;
+ } else {
+ pthread_rwlock_wrlock(&map->cached_rwlck);
+ map->cached = block;
+ pthread_rwlock_unlock(&map->cached_rwlck);
+ }
+ } else if (create) {
+ block = map_allocate_block(pos);
- array_insert(§or->blocks, &block, res.index);
- } else {
- return NULL;
+ bintree_add_node(§or->blocks, nodeptr, &pos.y, block);
}
- return block->ready ? block : NULL;
+ pthread_rwlock_unlock(§or->rwlck);
+
+ return block;
}
-void map_add_block(Map *map, MapBlock *block)
+MapBlock *map_allocate_block(v3s32 pos)
{
- MapSector *sector = map_get_sector(map, (v2s32) {block->pos.x, block->pos.z}, true);
- ArraySearchResult res = array_search(§or->blocks, &block->pos.y);
- if (res.success) {
- MapBlock **ptr = get_block_ptr(sector, res.index);
- map_free_block(*ptr);
- *ptr = block;
- } else {
- array_insert(§or->blocks, &block, res.index);
- }
- if (map->on_block_add)
- map->on_block_add(block);
+ MapBlock *block = malloc(sizeof(MapBlock));
+ block->pos = pos;
+ block->state = MBS_CREATED;
+ block->extra = NULL;
+ block->free_extra = NULL;
+ pthread_mutex_init(&block->mtx, NULL);
+ return block;
}
-void map_clear_block(MapBlock *block, v3u8 init_state)
+void map_clear_meta(MapBlock *block)
{
- for (u8 x = 0; x <= init_state.x; x++)
- for (u8 y = 0; y <= init_state.y; y++)
- for (u8 z = 0; z <= init_state.z; z++)
- map_node_clear(&block->data[x][y][z]);
+ pthread_mutex_lock(&block->mtx);
+ ITERATE_MAPBLOCK list_clear(&block->metadata[x][y][z]);
+ pthread_mutex_unlock(&block->mtx);
}
void map_free_block(MapBlock *block)
{
- map_clear_block(block, (v3u8) {15, 15, 15});
+ map_clear_meta(block);
+ pthread_mutex_destroy(&block->mtx);
+ if (block->free_extra)
+ block->free_extra(block->extra);
free(block);
}
return true;
}
-bool map_serialize_block(int fd, MapBlock *block)
+void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr)
{
- if (! write_v3s32(fd, block->pos))
- return false;
+ size_t uncompressed_size = sizeof(MapBlockData);
+ char uncompressed_data[uncompressed_size];
+ MapBlockData blockdata;
ITERATE_MAPBLOCK {
- if (! write_u32(fd, block->data[x][y][z].type))
- return false;
+ MapNode node = block->data[x][y][z];
+ node.type = htobe32(node.type);
+ blockdata[x][y][z] = node;
}
+ memcpy(uncompressed_data, blockdata, sizeof(MapBlockData));
- return true;
+ char compressed_data[uncompressed_size];
+
+ z_stream stream;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ stream.avail_in = stream.avail_out = uncompressed_size;
+ stream.next_in = (Bytef *) uncompressed_data;
+ stream.next_out = (Bytef *) compressed_data;
+
+ deflateInit(&stream, Z_BEST_COMPRESSION);
+ deflate(&stream, Z_FINISH);
+ deflateEnd(&stream);
+
+ size_t compressed_size = stream.total_out;
+
+ size_t size = sizeof(MapBlockHeader) + sizeof(MapBlockHeader) + compressed_size;
+ char *data = malloc(size);
+ *(MapBlockHeader *) data = htobe16(sizeof(MapBlockHeader) + compressed_size);
+ *(MapBlockHeader *) (data + sizeof(MapBlockHeader)) = htobe16(uncompressed_size);
+ memcpy(data + sizeof(MapBlockHeader) + sizeof(MapBlockHeader), compressed_data, compressed_size);
+
+ *sizeptr = size;
+ *dataptr = data;
}
-MapBlock *map_deserialize_block(int fd)
+bool map_deserialize_block(MapBlock *block, const char *data, size_t size)
{
- v3s32 pos;
+ if (size < sizeof(MapBlockHeader))
+ return false;
- if (! read_v3s32(fd, &pos))
- return NULL;
+ MapBlockHeader uncompressed_size = be16toh(*(MapBlockHeader *) data);
+
+ if (uncompressed_size < sizeof(MapBlockData))
+ return false;
+
+ char decompressed_data[uncompressed_size];
- MapBlock *block = allocate_block(pos);
+ z_stream stream;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ stream.avail_in = size - sizeof(MapBlockHeader);
+ stream.next_in = (Bytef *) (data + sizeof(MapBlockHeader));
+ stream.avail_out = uncompressed_size;
+ stream.next_out = (Bytef *) decompressed_data;
+
+ inflateInit(&stream);
+ inflate(&stream, Z_NO_FLUSH);
+ inflateEnd(&stream);
+
+ if (stream.total_out < uncompressed_size)
+ return false;
+
+ MapBlockData blockdata;
+ memcpy(blockdata, decompressed_data, sizeof(MapBlockData));
ITERATE_MAPBLOCK {
- if (! map_deserialize_node(fd, &block->data[x][y][z])) {
- map_clear_block(block, (v3u8) {x, y, z});
- free(block);
- return NULL;
- }
- }
+ MapNode node = blockdata[x][y][z];
+ node.type = be32toh(node.type);
- return block;
-}
+ if (node.type >= NODE_UNLOADED)
+ node.type = NODE_INVALID;
-bool map_serialize(int fd, Map *map)
-{
- for (size_t s = 0; s < map->sectors.siz; s++) {
- MapSector *sector = *get_sector_ptr(map, s);
- for (size_t b = 0; b < sector->blocks.siz; b++)
- if (! map_serialize_block(fd, *get_block_ptr(sector, b)))
- return false;
+ block->data[x][y][z] = node;
+
+ block->metadata[x][y][z] = list_create(&list_compare_string);
}
- return true;
-}
-void map_deserialize(int fd, Map *map)
-{
- MapBlock *block;
+ block->state = MBS_MODIFIED;
- while ((block = map_deserialize_block(fd)) != NULL)
- map_add_block(map, block);
+ return true;
}
v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
v3u8 offset;
MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), false);
if (block) {
- MapNode *current_node = &block->data[offset.x][offset.y][offset.z];
- map_node_clear(current_node);
- *current_node = node;
+ pthread_mutex_lock(&block->mtx);
+ block->state = MBS_MODIFIED;
+ list_clear(&block->metadata[offset.x][offset.y][offset.z]);
+ block->data[offset.x][offset.y][offset.z] = node;
+ pthread_mutex_unlock(&block->mtx);
}
}
MapNode map_node_create(Node type)
{
- return (MapNode) {type, list_create(&list_compare_string)};
-}
-
-void map_node_clear(MapNode *node)
-{
- list_clear(&node->meta);
+ return (MapNode) {type};
}