#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
-#include <stdio.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->state = MBS_CREATED;
- block->extra = NULL;
- pthread_mutex_init(&block->mtx, NULL);
- return block;
-}
-
Map *map_create()
{
Map *map = malloc(sizeof(Map));
- map->sectors = array_create(sizeof(MapSector *));
- map->sectors.cmp = §or_compare;
+ pthread_rwlock_init(&map->rwlck, NULL);
+ pthread_rwlock_init(&map->cached_rwlck, NULL);
+ map->sectors = bintree_create(sizeof(v2s32));
+ map->cached = NULL;
return map;
}
-void map_delete(Map *map)
+static void free_block(void *block)
{
- for (size_t s = 0; s < map->sectors.siz; s++) {
- MapSector *sector = map_get_sector_raw(map, s);
- for (size_t b = 0; b < sector->blocks.siz; b++)
- map_free_block(map_get_block_raw(sector, b));
- if (sector->blocks.ptr)
- free(sector->blocks.ptr);
- free(sector);
- }
- if (map->sectors.ptr)
- free(map->sectors.ptr);
- free(map);
+ map_free_block(block);
}
-MapSector *map_get_sector_raw(Map *map, size_t idx)
+static void free_sector(void *sector_void)
{
- return ((MapSector **) map->sectors.ptr)[idx];
+ MapSector *sector = sector_void;
+ bintree_clear(§or->blocks, &free_block);
+ pthread_rwlock_destroy(§or->rwlck);
+ free(sector);
}
-MapBlock *map_get_block_raw(MapSector *sector, size_t idx)
+void map_delete(Map *map)
{
- return ((MapBlock **) sector->blocks.ptr)[idx];
+ 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 map_get_sector_raw(map, res.index);
- if (! create)
- return NULL;
+ BintreeNode **nodeptr = bintree_search(&map->sectors, &pos);
+
+ MapSector *sector = NULL;
+
+ 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));
- MapSector *sector = malloc(sizeof(MapSector));
- sector->pos = pos;
- sector->hash = hash;
- sector->blocks = array_create(sizeof(MapBlock *));
- sector->blocks.cmp = &block_compare;
+ bintree_add_node(&map->sectors, nodeptr, &pos, sector);
+ }
- array_insert(&map->sectors, §or, res.index);
+ 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 = map_get_block_raw(sector, res.index);
+ if (*nodeptr) {
+ block = (*nodeptr)->value;
+
+ 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 = allocate_block(pos);
- array_insert(§or->blocks, &block, res.index);
- } else {
- return NULL;
+ block = map_allocate_block(pos);
+
+ bintree_add_node(§or->blocks, nodeptr, &pos.y, block);
}
- return (create || block->state == MBS_READY) ? block : NULL;
+ pthread_rwlock_unlock(§or->rwlck);
+
+ return block;
}
-void map_free_block(MapBlock *block)
+MapBlock *map_allocate_block(v3s32 pos)
+{
+ 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_meta(MapBlock *block)
{
+ 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_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];
- if (write(fd, block->data, sizeof(block->data)) == -1)
- perror("write");
- else
- return true;
+ MapBlockData blockdata;
+ ITERATE_MAPBLOCK {
+ MapNode node = block->data[x][y][z];
+ node.type = htobe32(node.type);
+ blockdata[x][y][z] = node;
+ }
+ memcpy(uncompressed_data, blockdata, sizeof(MapBlockData));
+
+ 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);
- return false;
+ *sizeptr = size;
+ *dataptr = data;
}
-bool map_deserialize_block(int fd, Map *map, MapBlock **blockptr, bool dummy)
+bool map_deserialize_block(MapBlock *block, const char *data, size_t size)
{
- v3s32 pos;
+ if (size < sizeof(MapBlockHeader))
+ return false;
+
+ MapBlockHeader uncompressed_size = be16toh(*(MapBlockHeader *) data);
- if (! read_v3s32(fd, &pos))
+ if (uncompressed_size < sizeof(MapBlockData))
return false;
- MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, true);
- ArraySearchResult res = array_search(§or->blocks, &pos.y);
+ char decompressed_data[uncompressed_size];
- MapBlock *block;
+ z_stream stream;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
- if (dummy) {
- block = allocate_block(pos);
- } else if (res.success) {
- block = map_get_block_raw(sector, res.index);
- } else {
- block = allocate_block(pos);
- array_insert(§or->blocks, &block, res.index);
- }
+ stream.avail_in = size - sizeof(MapBlockHeader);
+ stream.next_in = (Bytef *) (data + sizeof(MapBlockHeader));
+ stream.avail_out = uncompressed_size;
+ stream.next_out = (Bytef *) decompressed_data;
- bool ret = true;
- size_t n_read_total = 0;
- int n_read;
+ inflateInit(&stream);
+ inflate(&stream, Z_NO_FLUSH);
+ inflateEnd(&stream);
- while (n_read_total < sizeof(block->data)) {
- if ((n_read = read(fd, (char *) block->data + n_read_total, sizeof(block->data) - n_read_total)) == -1) {
- perror("read");
- ret = false;
- break;
- }
+ if (stream.total_out < uncompressed_size)
+ return false;
- n_read_total += n_read;
- }
+ MapBlockData blockdata;
+ memcpy(blockdata, decompressed_data, sizeof(MapBlockData));
ITERATE_MAPBLOCK {
- if (block->data[x][y][z].type >= NODE_UNLOADED)
- block->data[x][y][z].type = NODE_INVALID;
- block->metadata[x][y][z] = list_create(&list_compare_string);
- }
+ MapNode node = blockdata[x][y][z];
+ node.type = be32toh(node.type);
- // ToDo: Deserialize meta
+ if (node.type >= NODE_UNLOADED)
+ node.type = NODE_INVALID;
- if (dummy)
- map_free_block(block);
- else if (blockptr)
- *blockptr = block;
-
- return ret;
-}
+ block->data[x][y][z] = node;
-bool map_serialize(int fd, Map *map)
-{
- for (size_t s = 0; s < map->sectors.siz; s++) {
- MapSector *sector = map_get_sector_raw(map, s);
- for (size_t b = 0; b < sector->blocks.siz; b++)
- if (! map_serialize_block(fd, map_get_block_raw(sector, b)))
- return false;
+ block->metadata[x][y][z] = list_create(&list_compare_string);
}
- return true;
-}
-void map_deserialize(int fd, Map *map)
-{
- while (map_deserialize_block(fd, map, NULL, false))
- ;
+ block->state = MBS_MODIFIED;
+
+ 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) {
+ 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;
-
- block->state = MBS_MODIFIED;
+ pthread_mutex_unlock(&block->mtx);
}
}