]> git.lizzy.rs Git - dragonblocks_alpha.git/blobdiff - src/map.c
Update meshgen threads to use queue waiting
[dragonblocks_alpha.git] / src / map.c
index e40fe62bc0eb9df95c151da722551b7741773de0..a9a83befb97c8502fc90ccdaecbcaf7d390edc70 100644 (file)
--- a/src/map.c
+++ b/src/map.c
 #include <stdlib.h>
 #include <stdbool.h>
 #include <unistd.h>
-#include <stdio.h>
 #include <math.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)
+Map *map_create(MapCallbacks callbacks)
 {
-       s64 d = *((u64 *) hash) - (*(MapSector **) sector)->hash;
-       return CMPBOUNDS(d);
+       Map *map = malloc(sizeof(Map));
+       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 s8 block_compare(void *level, void *block)
+static void free_block(BintreeNode *node, void *arg)
 {
-       s32 d = *((s32 *) level) - (*(MapBlock **) block)->pos.y;
-       return CMPBOUNDS(d);
-}
+       Map *map = arg;
 
-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;
+       if (map->callbacks.delete_block)
+               map->callbacks.delete_block(node->value);
+
+       map_free_block(node->value);
 }
 
-Map *map_create()
+static void free_sector(BintreeNode *node, void *arg)
 {
-       Map *map = malloc(sizeof(Map));
-       map->sectors = array_create(sizeof(MapSector *));
-       map->sectors.cmp = &sector_compare;
-       return map;
+       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 = 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);
+       pthread_rwlock_destroy(&map->rwlck);
+       pthread_rwlock_destroy(&map->cached_rwlck);
+       bintree_clear(&map->sectors, &free_sector, map);
        free(map);
 }
 
-MapSector *map_get_sector_raw(Map *map, size_t idx)
+MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
 {
-       return ((MapSector **) map->sectors.ptr)[idx];
-}
+       if (create)
+               pthread_rwlock_wrlock(&map->rwlck);
+       else
+               pthread_rwlock_rdlock(&map->rwlck);
 
-MapBlock *map_get_block_raw(MapSector *sector, size_t idx)
-{
-       return ((MapBlock **) sector->blocks.ptr)[idx];
-}
+       BintreeNode **nodeptr = bintree_search(&map->sectors, &pos);
 
-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);
+       MapSector *sector = NULL;
 
-       if (res.success)
-               return map_get_sector_raw(map, res.index);
-       if (! create)
-               return NULL;
+       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);
 
-       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, &sector, 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 && v3s32_equals(cached->pos, pos))
+               return cached;
+
        MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
        if (! sector)
                return NULL;
 
-       ArraySearchResult res = array_search(&sector->blocks, &pos.y);
+       if (create)
+               pthread_rwlock_wrlock(&sector->rwlck);
+       else
+               pthread_rwlock_rdlock(&sector->rwlck);
+
+       BintreeNode **nodeptr = bintree_search(&sector->blocks, &pos.y);
 
        MapBlock *block = NULL;
 
-       if (res.success) {
-               block = map_get_block_raw(sector, res.index);
+       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 = allocate_block(pos);
-               array_insert(&sector->blocks, &block, res.index);
-       } else {
-               return NULL;
+               bintree_add_node(&sector->blocks, nodeptr, &pos.y, block = map_allocate_block(pos));
+
+               if (map->callbacks.create_block)
+                       map->callbacks.create_block(block);
        }
 
-       return (create || block->state == MBS_READY) ? block : NULL;
-}
+       pthread_rwlock_unlock(&sector->rwlck);
 
-void map_free_block(MapBlock *block)
-{
-       ITERATE_MAPBLOCK list_clear(&block->metadata[x][y][z]);
-       pthread_mutex_destroy(&block->mtx);
-       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_UNLOADED)
-               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 map_node_delete(block->data[x][y][z]);
 
-       if (write(fd, block->data, sizeof(block->data)) == -1)
-               perror("write");
-       else
-               return true;
-
-       return false;
+       pthread_mutex_destroy(&block->mtx);
+       free(block);
 }
 
-bool map_deserialize_block(int fd, Map *map, MapBlock **blockptr, bool dummy)
+Blob map_serialize_block(MapBlock *block)
 {
-       v3s32 pos;
-
-       if (! read_v3s32(fd, &pos))
-               return false;
-
-       MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, true);
-       ArraySearchResult res = array_search(&sector->blocks, &pos.y);
+       SerializedMapBlock block_data;
 
-       MapBlock *block;
-
-       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(&sector->blocks, &block, res.index);
-       }
-
-       bool ret = true;
-       size_t n_read_total = 0;
-       int n_read;
+       ITERATE_MAPBLOCK {
+               MapNode *node = &block->data[x][y][z];
+               SerializedMapNode *node_data = &block_data.raw.nodes[x][y][z];
 
-       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;
-               }
+               *node_data = (SerializedMapNode) {
+                       .type = node->type,
+                       .data = {
+                               .siz = 0,
+                               .data = NULL,
+                       },
+               };
 
-               n_read_total += n_read;
-       }
+               NodeDefinition *def = &node_definitions[node->type];
 
-       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);
+               if (def->serialize)
+                       def->serialize(&node_data->data, node->data);
        }
 
-       // ToDo: Deserialize meta
-
-       if (dummy)
-               map_free_block(block);
-       else if (blockptr)
-               *blockptr = block;
+       Blob buffer = {0, NULL};
+       SerializedMapBlock_write(&buffer, &block_data);
+       SerializedMapBlock_free(&block_data);
 
-       return ret;
+       return buffer;
 }
 
-bool map_serialize(int fd, Map *map)
+bool map_deserialize_block(MapBlock *block, Blob buffer)
 {
-       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;
-       }
-       return true;
-}
+       // 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);
 
-void map_deserialize(int fd, Map *map)
-{
-       while (map_deserialize_block(fd, map, NULL, false))
-               ;
+       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)
@@ -222,23 +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), false);
+       MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), create);
        if (block) {
-               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_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};
+       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_delete(MapNode node)
+{
+       NodeDefinition *def = &node_definitions[node.type];
+
+       if (def->delete)
+               def->delete(&node);
+
+       if (node.data)
+               free(node.data);
 }