]> git.lizzy.rs Git - dragonblocks_alpha.git/blobdiff - src/map.c
Add mountain generation
[dragonblocks_alpha.git] / src / map.c
index ce69123f5f7ef2798223fe8d44da9488ec09eb70..ca8d2839f7f0bfab267b71f39e032c3c879f7018 100644 (file)
--- a/src/map.c
+++ b/src/map.c
 #include <stdlib.h>
 #include <stdbool.h>
+#include <unistd.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->extra = NULL;
-       return block;
-}
-
-static MapBlock **get_block_ptr(MapSector *sector, size_t idx)
+Map *map_create()
 {
-       return (MapBlock **) sector->blocks.ptr + idx;
+       Map *map = malloc(sizeof(Map));
+       pthread_rwlock_init(&map->rwlck, NULL);
+       pthread_rwlock_init(&map->cached_rwlck, NULL);
+       map->sectors = bintree_create(sizeof(v2s32));
+       map->cached = NULL;
+       return map;
 }
 
-static MapSector **get_sector_ptr(Map *map, size_t idx)
+static void free_block(void *block)
 {
-       return (MapSector **) map->sectors.ptr + idx;
+       map_free_block(block);
 }
 
-Map *map_create()
+static void free_sector(void *sector_void)
 {
-       Map *map = malloc(sizeof(Map));
-       map->sectors = array_create(sizeof(MapSector *));
-       map->sectors.cmp = &sector_compare;
-       return map;
+       MapSector *sector = sector_void;
+       bintree_clear(&sector->blocks, &free_block);
+       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);
        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 *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 *));
-       sector->blocks.cmp = &block_compare;
+       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));
+
+               bintree_add_node(&map->sectors, nodeptr, &pos, sector);
+       }
+
+       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(&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 = *get_block_ptr(sector, res.index);
-       } else if (create) {
-               block = allocate_block(pos);
+       if (*nodeptr) {
+               block = (*nodeptr)->value;
 
-               if (map->on_block_create)
-                       map->on_block_create(block);
+               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 = map_allocate_block(pos);
 
-               array_insert(&sector->blocks, &block, res.index);
-       } else {
-               return NULL;
+               bintree_add_node(&sector->blocks, nodeptr, &pos.y, block);
        }
 
-       return block->ready ? block : NULL;
+       pthread_rwlock_unlock(&sector->rwlck);
+
+       return block;
 }
 
-void map_add_block(Map *map, MapBlock *block)
+MapBlock *map_allocate_block(v3s32 pos)
 {
-       MapSector *sector = map_get_sector(map, (v2s32) {block->pos.x, block->pos.z}, true);
-       ArraySearchResult res = array_search(&sector->blocks, &block->pos.y);
-       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->on_block_add)
-               map->on_block_add(block);
+       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_block(MapBlock *block, v3u8 init_state)
+void map_clear_meta(MapBlock *block)
 {
-       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_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_block(block, (v3u8) {15, 15, 15});
+       map_clear_meta(block);
+       pthread_mutex_destroy(&block->mtx);
+       if (block->free_extra)
+               block->free_extra(block->extra);
        free(block);
 }
 
@@ -150,56 +153,93 @@ bool map_deserialize_node(int fd, MapNode *node)
        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];
 
+       MapBlockData blockdata;
        ITERATE_MAPBLOCK {
-               if (! write_u32(fd, block->data[x][y][z].type))
-                       return false;
+               MapNode node = block->data[x][y][z];
+               node.type = htobe32(node.type);
+               blockdata[x][y][z] = node;
        }
+       memcpy(uncompressed_data, blockdata, sizeof(MapBlockData));
 
-       return true;
+       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);
+
+       *sizeptr = size;
+       *dataptr = data;
 }
 
-MapBlock *map_deserialize_block(int fd)
+bool map_deserialize_block(MapBlock *block, const char *data, size_t size)
 {
-       v3s32 pos;
+       if (size < sizeof(MapBlockHeader))
+               return false;
 
-       if (! read_v3s32(fd, &pos))
-               return NULL;
+       MapBlockHeader uncompressed_size = be16toh(*(MapBlockHeader *) data);
+
+       if (uncompressed_size < sizeof(MapBlockData))
+               return false;
+
+       char decompressed_data[uncompressed_size];
 
-       MapBlock *block = allocate_block(pos);
+       z_stream stream;
+       stream.zalloc = Z_NULL;
+    stream.zfree = Z_NULL;
+    stream.opaque = Z_NULL;
+
+       stream.avail_in = size - sizeof(MapBlockHeader);
+    stream.next_in = (Bytef *) (data + sizeof(MapBlockHeader));
+    stream.avail_out = uncompressed_size;
+    stream.next_out = (Bytef *) decompressed_data;
+
+    inflateInit(&stream);
+    inflate(&stream, Z_NO_FLUSH);
+    inflateEnd(&stream);
+
+    if (stream.total_out < uncompressed_size)
+               return false;
+
+       MapBlockData blockdata;
+       memcpy(blockdata, decompressed_data, sizeof(MapBlockData));
 
        ITERATE_MAPBLOCK {
-               if (! map_deserialize_node(fd, &block->data[x][y][z])) {
-                       map_clear_block(block, (v3u8) {x, y, z});
-                       free(block);
-                       return NULL;
-               }
-       }
+               MapNode node = blockdata[x][y][z];
+               node.type = be32toh(node.type);
 
-       return block;
-}
+               if (node.type >= NODE_UNLOADED)
+                       node.type = NODE_INVALID;
 
-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;
+               block->data[x][y][z] = node;
+
+               block->metadata[x][y][z] = list_create(&list_compare_string);
        }
-       return true;
-}
 
-void map_deserialize(int fd, Map *map)
-{
-       MapBlock *block;
+       block->state = MBS_MODIFIED;
 
-       while ((block = map_deserialize_block(fd)) != NULL)
-               map_add_block(map, block);
+       return true;
 }
 
 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
@@ -224,18 +264,15 @@ void map_set_node(Map *map, v3s32 pos, MapNode node)
        v3u8 offset;
        MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), false);
        if (block) {
-               MapNode *current_node = &block->data[offset.x][offset.y][offset.z];
-               map_node_clear(current_node);
-               *current_node = node;
+               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;
+               pthread_mutex_unlock(&block->mtx);
        }
 }
 
 MapNode map_node_create(Node type)
 {
-       return (MapNode) {type, list_create(&list_compare_string)};
-}
-
-void map_node_clear(MapNode *node)
-{
-       list_clear(&node->meta);
+       return (MapNode) {type};
 }