#include <stdbool.h>
#include <unistd.h>
#include <math.h>
-#include <endian.h>
-#include <zlib.h>
+#include <endian.h/endian.h>
+#include <string.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);
-}
-
-Map *map_create()
+Map *map_create(MapCallbacks callbacks)
{
Map *map = malloc(sizeof(Map));
pthread_rwlock_init(&map->rwlck, NULL);
- map->sectors = array_create(sizeof(MapSector *));
- map->sectors.cmp = §or_compare;
+ pthread_rwlock_init(&map->cached_rwlck, NULL);
+ map->sectors = bintree_create(sizeof(v2s32), NULL);
+ map->cached = NULL;
+ map->callbacks = callbacks;
return map;
}
-void map_delete(Map *map, void (*callback)(void *extra))
+static void free_block(BintreeNode *node, void *arg)
{
- pthread_rwlock_destroy(&map->rwlck);
- 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++) {
- MapBlock *block = map_get_block_raw(sector, b);
- if (callback)
- callback(block->extra);
- map_free_block(block);
- }
- if (sector->blocks.ptr)
- free(sector->blocks.ptr);
- pthread_rwlock_destroy(§or->rwlck);
- free(sector);
- }
- if (map->sectors.ptr)
- free(map->sectors.ptr);
- free(map);
+ Map *map = arg;
+
+ if (map->callbacks.delete_block)
+ map->callbacks.delete_block(node->value);
+
+ map_free_block(node->value);
}
-MapSector *map_get_sector_raw(Map *map, size_t idx)
+static void free_sector(BintreeNode *node, void *arg)
{
- return ((MapSector **) map->sectors.ptr)[idx];
+ MapSector *sector = node->value;
+
+ bintree_clear(§or->blocks, &free_block, arg);
+ 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, map);
+ free(map);
}
MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
{
- u64 hash = ((u64) pos.x << 32) + (u64) pos.y;
-
if (create)
pthread_rwlock_wrlock(&map->rwlck);
else
pthread_rwlock_rdlock(&map->rwlck);
- ArraySearchResult res = array_search(&map->sectors, &hash);
+ BintreeNode **nodeptr = bintree_search(&map->sectors, &pos);
MapSector *sector = NULL;
- if (res.success) {
- sector = map_get_sector_raw(map, res.index);
+ if (*nodeptr) {
+ sector = (*nodeptr)->value;
} else if (create) {
sector = malloc(sizeof(MapSector));
pthread_rwlock_init(§or->rwlck, NULL);
sector->pos = pos;
- sector->hash = hash;
- sector->blocks = array_create(sizeof(MapBlock *));
- sector->blocks.cmp = &block_compare;
+ sector->blocks = bintree_create(sizeof(s32), NULL);
- array_insert(&map->sectors, §or, res.index);
+ bintree_add_node(&map->sectors, nodeptr, &pos, sector);
}
pthread_rwlock_unlock(&map->rwlck);
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 && v3s32_equals(cached->pos, pos))
+ return cached;
+
MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
if (! sector)
return NULL;
else
pthread_rwlock_rdlock(§or->rwlck);
- ArraySearchResult res = array_search(§or->blocks, &pos.y);
+ BintreeNode **nodeptr = bintree_search(§or->blocks, &pos.y);
MapBlock *block = NULL;
- if (res.success) {
- block = map_get_block_raw(sector, res.index);
- if (block->state < MBS_READY && ! create)
+ if (*nodeptr) {
+ block = (*nodeptr)->value;
+
+ pthread_mutex_lock(&block->mtx);
+ if (map->callbacks.get_block && ! map->callbacks.get_block(block, create)) {
+ pthread_mutex_unlock(&block->mtx);
block = NULL;
+ } else {
+ pthread_mutex_unlock(&block->mtx);
+ 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);
+ bintree_add_node(§or->blocks, nodeptr, &pos.y, block = map_allocate_block(pos));
+
+ if (map->callbacks.create_block)
+ map->callbacks.create_block(block);
}
pthread_rwlock_unlock(§or->rwlck);
{
MapBlock *block = malloc(sizeof(MapBlock));
block->pos = pos;
- block->state = MBS_CREATED;
block->extra = NULL;
- pthread_mutex_init(&block->mtx, NULL);
- return block;
-}
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&block->mtx, &attr);
-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);
+ ITERATE_MAPBLOCK block->data[x][y][z] = map_node_create(NODE_UNKNOWN, NULL, 0);
+
+ return block;
}
void map_free_block(MapBlock *block)
{
- map_clear_meta(block);
+ ITERATE_MAPBLOCK map_node_delete(block->data[x][y][z]);
+
pthread_mutex_destroy(&block->mtx);
free(block);
}
-bool map_deserialize_node(int fd, MapNode *node)
+void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr, size_t *rawsizeptr)
{
- Node type;
+ unsigned char *uncompressed = NULL;
+ size_t uncompressed_size = 0;
- if (! read_u32(fd, &type))
- return false;
-
- if (type >= NODE_UNLOADED)
- type = NODE_INVALID;
-
- *node = map_node_create(type);
-
- return true;
-}
-
-void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr)
-{
- size_t uncompressed_size = sizeof(MapBlockData);
- char uncompressed_data[uncompressed_size];
-
- 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));
+ NodeDefinition *def = &node_definitions[node.type];
- char compressed_data[uncompressed_size];
+ u32 type = htobe32(node.type);
+ buffer_write(&uncompressed, &uncompressed_size, &type, sizeof(u32));
- z_stream stream;
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
+ unsigned char *data_buffer = NULL;
+ size_t data_bufsiz = 0;
- stream.avail_in = stream.avail_out = uncompressed_size;
- stream.next_in = (Bytef *) uncompressed_data;
- stream.next_out = (Bytef *) compressed_data;
+ if (def->serialize)
+ def->serialize(&node, &data_buffer, &data_bufsiz);
- deflateInit(&stream, Z_BEST_COMPRESSION);
- deflate(&stream, Z_FINISH);
- deflateEnd(&stream);
+ u16 data_size = htobe16(data_bufsiz);
+ buffer_write(&uncompressed, &uncompressed_size, &data_size, sizeof(u16));
+ buffer_write(&uncompressed, &uncompressed_size, data_buffer, data_bufsiz);
- size_t compressed_size = stream.total_out;
+ if (data_buffer)
+ free(data_buffer);
+ }
- size_t size = sizeof(MapBlockHeader) + sizeof(MapBlockHeader) + compressed_size;
- char *data = malloc(size);
- *(MapBlockHeader *) data = htobe64(sizeof(MapBlockHeader) + compressed_size);
- *(MapBlockHeader *) (data + sizeof(MapBlockHeader)) = htobe64(uncompressed_size);
- memcpy(data + sizeof(MapBlockHeader) + sizeof(MapBlockHeader), compressed_data, compressed_size);
+ my_compress(uncompressed, uncompressed_size, dataptr, sizeptr);
+ *rawsizeptr = uncompressed_size;
- *sizeptr = size;
- *dataptr = data;
+ if (uncompressed)
+ free(uncompressed);
}
-bool map_deserialize_block(MapBlock *block, const char *data, size_t size)
+bool map_deserialize_block(MapBlock *block, const char *data, size_t size, size_t rawsize)
{
- if (size < sizeof(MapBlockHeader))
- return false;
-
- MapBlockHeader uncompressed_size = be64toh(*(MapBlockHeader *) data);
+ unsigned char decompressed[rawsize];
+ size_t decompressed_size = rawsize;
- if (uncompressed_size < sizeof(MapBlockData))
+ if (! my_decompress(data, size, decompressed, decompressed_size))
return false;
- char decompressed_data[uncompressed_size];
+ unsigned char *ptr = decompressed;
- z_stream stream;
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
+ ITERATE_MAPBLOCK {
+ // node type
+ u32 *type_ptr = buffer_read(&ptr, &decompressed_size, sizeof(u32));
- stream.avail_in = size - sizeof(u64);
- stream.next_in = (Bytef *) (data + sizeof(u64));
- stream.avail_out = uncompressed_size;
- stream.next_out = (Bytef *) decompressed_data;
+ if (! type_ptr)
+ return false;
- inflateInit(&stream);
- inflate(&stream, Z_NO_FLUSH);
- inflateEnd(&stream);
+ u32 type = be32toh(*type_ptr);
- if (stream.total_out < uncompressed_size)
- return false;
+ // data size
+ u16 *data_size_ptr = buffer_read(&ptr, &decompressed_size, sizeof(u16));
- MapBlockData blockdata;
- memcpy(blockdata, decompressed_data, sizeof(MapBlockData));
+ if (! data_size_ptr)
+ return false;
- ITERATE_MAPBLOCK {
- MapNode node = blockdata[x][y][z];
- node.type = be32toh(node.type);
+ u16 data_size = be16toh(*data_size_ptr);
- if (node.type >= NODE_UNLOADED)
- node.type = NODE_INVALID;
+ // data
+ void *data = buffer_read(&ptr, &decompressed_size, data_size);
- block->data[x][y][z] = node;
+ if (! data && data_size)
+ return false;
- block->metadata[x][y][z] = list_create(&list_compare_string);
+ // set node
+ block->data[x][y][z] = map_node_create(type, data, data_size);
}
- block->state = MBS_MODIFIED;
-
return true;
}
v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
{
if (offset)
- *offset = (v3u8) {(u32) pos.x % 16, (u32) pos.y % 16, (u32) pos.z % 16};
- return (v3s32) {floor((double) pos.x / 16.0), floor((double) pos.y / 16.0), floor((double) pos.z / 16.0)};
+ *offset = (v3u8) {(u32) pos.x % MAPBLOCK_SIZE, (u32) pos.y % MAPBLOCK_SIZE, (u32) pos.z % MAPBLOCK_SIZE};
+ return (v3s32) {floor((double) pos.x / (double) MAPBLOCK_SIZE), floor((double) pos.y / (double) MAPBLOCK_SIZE), floor((double) pos.z / (double) MAPBLOCK_SIZE)};
}
MapNode map_get_node(Map *map, v3s32 pos)
v3s32 blockpos = map_node_to_block_pos(pos, &offset);
MapBlock *block = map_get_block(map, blockpos, false);
if (! block)
- return map_node_create(NODE_UNLOADED);
+ return map_node_create(NODE_UNLOADED, NULL, 0);
return block->data[offset.x][offset.y][offset.z];
}
-void map_set_node(Map *map, v3s32 pos, MapNode node)
+void map_set_node(Map *map, v3s32 pos, MapNode node, bool create, void *arg)
{
v3u8 offset;
- MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), false);
+ MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), create);
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;
+ if (! map->callbacks.set_node || map->callbacks.set_node(block, offset, &node, arg)) {
+ block->data[offset.x][offset.y][offset.z] = node;
+ if (map->callbacks.after_set_node)
+ map->callbacks.after_set_node(block, offset, arg);
+ } else {
+ map_node_delete(node);
+ }
pthread_mutex_unlock(&block->mtx);
}
}
-MapNode map_node_create(Node type)
+MapNode map_node_create(Node type, void *data, size_t size)
{
- return (MapNode) {type};
+ if (type >= NODE_UNLOADED)
+ type = NODE_UNKNOWN;
+
+ NodeDefinition *def = &node_definitions[type];
+
+ MapNode node;
+ node.type = type;
+ node.data = def->data_size ? malloc(def->data_size) : NULL;
+
+ if (def->create)
+ def->create(&node);
+
+ if (def->deserialize && size)
+ def->deserialize(&node, data, size);
+
+ return node;
+}
+
+void map_node_delete(MapNode node)
+{
+ NodeDefinition *def = &node_definitions[node.type];
+
+ if (def->delete)
+ def->delete(&node);
+
+ if (node.data)
+ free(node.data);
}