]> git.lizzy.rs Git - dragonblocks_alpha.git/blobdiff - src/map.c
Fix UB/leaks in server
[dragonblocks_alpha.git] / src / map.c
index 847b45342fbd839893a2e54b50fab1e1a3538d7f..a9a83befb97c8502fc90ccdaecbcaf7d390edc70 100644 (file)
--- a/src/map.c
+++ b/src/map.c
 #include <stdlib.h>
 #include <stdbool.h>
+#include <unistd.h>
 #include <math.h>
-#include "binsearch.h"
+#include <string.h>
 #include "map.h"
 #include "util.h"
 
-Map *map_create()
+Map *map_create(MapCallbacks callbacks)
 {
        Map *map = malloc(sizeof(Map));
-       map->sectors = array_create(sizeof(MapSector *));
+       pthread_rwlock_init(&map->rwlck, NULL);
+       pthread_rwlock_init(&map->cached_rwlck, NULL);
+       map->sectors = bintree_create(sizeof(v2s32), NULL);
+       map->cached = NULL;
+       map->callbacks = callbacks;
        return map;
 }
 
-static MapBlock **get_block_ptr(MapSector *sector, size_t idx)
+static void free_block(BintreeNode *node, void *arg)
 {
-       return (MapBlock **) sector->blocks.ptr + idx;
+       Map *map = arg;
+
+       if (map->callbacks.delete_block)
+               map->callbacks.delete_block(node->value);
+
+       map_free_block(node->value);
 }
 
-static MapSector **get_sector_ptr(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);
 }
 
 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, map);
        free(map);
 }
 
-#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);
-}
-
 MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
 {
-       u64 hash = ((u64) pos.x << 32) + (u64) pos.y;
-       BinsearchResult res = binsearch(&hash, map->sectors.ptr, map->sectors.siz, &sector_compare);
+       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 *));
+       MapSector *sector = NULL;
 
-       array_insert(&map->sectors, &sector, res.index);
+       if (*nodeptr) {
+               sector = (*nodeptr)->value;
+       } else if (create) {
+               sector = malloc(sizeof(MapSector));
+               pthread_rwlock_init(&sector->rwlck, NULL);
+               sector->pos = pos;
+               sector->blocks = bintree_create(sizeof(s32), NULL);
 
-       return sector;
-}
+               bintree_add_node(&map->sectors, nodeptr, &pos, sector);
+       }
 
-static s8 block_compare(void *level, void *block)
-{
-       s32 d = *((s32 *) level) - ((MapBlock *) block)->pos.y;
-       return CMPBOUNDS(d);
-}
+       pthread_rwlock_unlock(&map->rwlck);
 
-static MapBlock *allocate_block(v3s32 pos)
-{
-       MapBlock *block = malloc(sizeof(MapBlock));
-       block->pos = pos;
-       block->extra = NULL;
-       return block;
+       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 && v3s32_equals(cached->pos, pos))
+               return cached;
+
        MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
        if (! sector)
                return NULL;
 
-       BinsearchResult res = binsearch(&pos.y, sector->blocks.ptr, sector->blocks.siz, &block_compare);
-
-       if (res.success)
-               return *get_block_ptr(sector, res.index);
-       if (! create)
-               return NULL;
+       if (create)
+               pthread_rwlock_wrlock(&sector->rwlck);
+       else
+               pthread_rwlock_rdlock(&sector->rwlck);
 
-       MapBlock *block = allocate_block(pos);
+       BintreeNode **nodeptr = bintree_search(&sector->blocks, &pos.y);
 
-       MapNode air = map_node_create(NODE_AIR);
-       ITERATE_MAPBLOCK block->data[x][y][z] = air;
+       MapBlock *block = NULL;
 
-       array_insert(&sector->blocks, &block, res.index);
+       if (*nodeptr) {
+               block = (*nodeptr)->value;
 
-       return block;
-}
+               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) {
+               bintree_add_node(&sector->blocks, nodeptr, &pos.y, block = map_allocate_block(pos));
 
-void map_add_block(Map *map, MapBlock *block)
-{
-       MapSector *sector = map_get_sector(map, (v2s32) {block->pos.x, block->pos.z}, true);
-       BinsearchResult res = binsearch(&block->pos.y, sector->blocks.ptr, sector->blocks.siz, &block_compare);
-       if (res.success) {
-               MapBlock **ptr = get_block_ptr(sector, res.index);
-               map_free_block(*ptr);
-               *ptr = block;
-       } else {
-               array_insert(&sector->blocks, &block, res.index);
+               if (map->callbacks.create_block)
+                       map->callbacks.create_block(block);
        }
-}
 
-void map_clear_block(MapBlock *block, v3u8 init_state)
-{
-       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_rwlock_unlock(&sector->rwlck);
 
-void map_free_block(MapBlock *block)
-{
-       map_clear_block(block, (v3u8) {15, 15, 15});
-       free(block);
+       return block;
 }
 
-bool map_deserialize_node(int fd, MapNode *node)
+MapBlock *map_allocate_block(v3s32 pos)
 {
-       Node type;
-
-       if (! read_u32(fd, &type))
-               return false;
-
-       if (type > NODE_INVALID)
-               type = NODE_INVALID;
+       MapBlock *block = malloc(sizeof(MapBlock));
+       block->pos = pos;
+       block->extra = NULL;
+       pthread_mutexattr_t attr;
+       pthread_mutexattr_init(&attr);
+       pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+       pthread_mutex_init(&block->mtx, &attr);
 
-       *node = map_node_create(type);
+       ITERATE_MAPBLOCK block->data[x][y][z] = map_node_create(NODE_UNKNOWN, (Blob) {0, NULL});
 
-       return true;
+       return block;
 }
 
-bool map_serialize_block(int fd, MapBlock *block)
+void map_free_block(MapBlock *block)
 {
-       if (! write_v3s32(fd, block->pos))
-               return false;
-
-       ITERATE_MAPBLOCK {
-               if (! write_u32(fd, block->data[x][y][z].type))
-                       return false;
-       }
+       ITERATE_MAPBLOCK map_node_delete(block->data[x][y][z]);
 
-       return true;
+       pthread_mutex_destroy(&block->mtx);
+       free(block);
 }
 
-MapBlock *map_deserialize_block(int fd)
+Blob map_serialize_block(MapBlock *block)
 {
-       v3s32 pos;
+       SerializedMapBlock block_data;
 
-       if (! read_v3s32(fd, &pos))
-               return NULL;
+       ITERATE_MAPBLOCK {
+               MapNode *node = &block->data[x][y][z];
+               SerializedMapNode *node_data = &block_data.raw.nodes[x][y][z];
 
-       MapBlock *block = allocate_block(pos);
+               *node_data = (SerializedMapNode) {
+                       .type = node->type,
+                       .data = {
+                               .siz = 0,
+                               .data = NULL,
+                       },
+               };
 
-       ITERATE_MAPBLOCK {
-               if (! map_deserialize_node(fd, &block->data[x][y][z])) {
-                       map_clear_block(block, (v3u8) {x, y, z});
-                       free(block);
-                       return NULL;
-               }
+               NodeDefinition *def = &node_definitions[node->type];
+
+               if (def->serialize)
+                       def->serialize(&node_data->data, node->data);
        }
 
-       return block;
-}
+       Blob buffer = {0, NULL};
+       SerializedMapBlock_write(&buffer, &block_data);
+       SerializedMapBlock_free(&block_data);
 
-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;
-       }
-       return true;
+       return buffer;
 }
 
-void map_deserialize(int fd, Map *map)
+bool map_deserialize_block(MapBlock *block, Blob buffer)
 {
-       MapBlock *block;
+       // it's important to copy Blobs that have been malloc'd before reading from them
+       // because reading from a Blob modifies its data and size pointer,
+       // but does not free anything
+       SerializedMapBlock block_data = {0};
+       bool success = SerializedMapBlock_read(&buffer, &block_data);
 
-       while ((block = map_deserialize_block(fd)) != NULL)
-               map_add_block(map, block);
+       if (success) ITERATE_MAPBLOCK
+               block->data[x][y][z] = map_node_create(block_data.raw.nodes[x][y][z].type, block_data.raw.nodes[x][y][z].data);
+
+       SerializedMapBlock_free(&block_data);
+       return success;
 }
 
 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)
@@ -209,25 +200,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, (Blob) {0, NULL});
        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), true);
-       MapNode *current_node = &block->data[offset.x][offset.y][offset.z];
-       map_node_clear(current_node);
-       *current_node = node;
+       MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), create);
+       if (block) {
+               pthread_mutex_lock(&block->mtx);
+               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, Blob buffer)
 {
-       return (MapNode) {type, list_create(&list_compare_string)};
+       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)
+               def->deserialize(&buffer, node.data);
+
+       return node;
 }
 
-void map_node_clear(MapNode *node)
+void map_node_delete(MapNode node)
 {
-       list_clear(&node->meta);
+       NodeDefinition *def = &node_definitions[node.type];
+
+       if (def->delete)
+               def->delete(&node);
+
+       if (node.data)
+               free(node.data);
 }