]> git.lizzy.rs Git - dragonblocks_alpha.git/blobdiff - src/map.c
Use spaces for alignment
[dragonblocks_alpha.git] / src / map.c
index 8c009e11b517d3398be9ca2979a431a25f92231a..59097b86260f0167153ab1f0c6a4f7086b2c0f0a 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -2,89 +2,69 @@
 #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 = &sector_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(&sector->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(&sector->blocks, &free_block, arg);
+       pthread_rwlock_destroy(&sector->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(&sector->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, &sector, res.index);
+               bintree_add_node(&map->sectors, nodeptr, &pos, sector);
        }
 
        pthread_rwlock_unlock(&map->rwlck);
@@ -94,6 +74,15 @@ MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
 
 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;
@@ -103,17 +92,28 @@ MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
        else
                pthread_rwlock_rdlock(&sector->rwlck);
 
-       ArraySearchResult res = array_search(&sector->blocks, &pos.y);
+       BintreeNode **nodeptr = bintree_search(&sector->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(&sector->blocks, &block, res.index);
+               bintree_add_node(&sector->blocks, nodeptr, &pos.y, block = map_allocate_block(pos));
+
+               if (map->callbacks.create_block)
+                       map->callbacks.create_block(block);
        }
 
        pthread_rwlock_unlock(&sector->rwlck);
@@ -125,135 +125,103 @@ MapBlock *map_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;
-}
+       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)
@@ -262,24 +230,54 @@ 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);
 }