]> git.lizzy.rs Git - dragonblocks_alpha.git/blobdiff - src/map.c
Add mountain generation
[dragonblocks_alpha.git] / src / map.c
index e40fe62bc0eb9df95c151da722551b7741773de0..ca8d2839f7f0bfab267b71f39e032c3c879f7018 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 <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->state = MBS_CREATED;
-       block->extra = NULL;
-       pthread_mutex_init(&block->mtx, NULL);
-       return block;
-}
-
 Map *map_create()
 {
        Map *map = malloc(sizeof(Map));
-       map->sectors = array_create(sizeof(MapSector *));
-       map->sectors.cmp = &sector_compare;
+       pthread_rwlock_init(&map->rwlck, NULL);
+       pthread_rwlock_init(&map->cached_rwlck, NULL);
+       map->sectors = bintree_create(sizeof(v2s32));
+       map->cached = NULL;
        return map;
 }
 
-void map_delete(Map *map)
+static void free_block(void *block)
 {
-       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);
-       free(map);
+       map_free_block(block);
 }
 
-MapSector *map_get_sector_raw(Map *map, size_t idx)
+static void free_sector(void *sector_void)
 {
-       return ((MapSector **) map->sectors.ptr)[idx];
+       MapSector *sector = sector_void;
+       bintree_clear(&sector->blocks, &free_block);
+       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);
+       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 map_get_sector_raw(map, res.index);
-       if (! create)
-               return NULL;
+       BintreeNode **nodeptr = bintree_search(&map->sectors, &pos);
+
+       MapSector *sector = 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));
 
-       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 && 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 = map_get_block_raw(sector, res.index);
+       if (*nodeptr) {
+               block = (*nodeptr)->value;
+
+               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 = allocate_block(pos);
-               array_insert(&sector->blocks, &block, res.index);
-       } else {
-               return NULL;
+               block = map_allocate_block(pos);
+
+               bintree_add_node(&sector->blocks, nodeptr, &pos.y, block);
        }
 
-       return (create || block->state == MBS_READY) ? block : NULL;
+       pthread_rwlock_unlock(&sector->rwlck);
+
+       return block;
 }
 
-void map_free_block(MapBlock *block)
+MapBlock *map_allocate_block(v3s32 pos)
+{
+       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_meta(MapBlock *block)
 {
+       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_meta(block);
        pthread_mutex_destroy(&block->mtx);
+       if (block->free_extra)
+               block->free_extra(block->extra);
        free(block);
 }
 
@@ -128,85 +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];
 
-       if (write(fd, block->data, sizeof(block->data)) == -1)
-               perror("write");
-       else
-               return true;
+       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));
+
+       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);
 
-       return false;
+       *sizeptr = size;
+       *dataptr = data;
 }
 
-bool map_deserialize_block(int fd, Map *map, MapBlock **blockptr, bool dummy)
+bool map_deserialize_block(MapBlock *block, const char *data, size_t size)
 {
-       v3s32 pos;
+       if (size < sizeof(MapBlockHeader))
+               return false;
+
+       MapBlockHeader uncompressed_size = be16toh(*(MapBlockHeader *) data);
 
-       if (! read_v3s32(fd, &pos))
+       if (uncompressed_size < sizeof(MapBlockData))
                return false;
 
-       MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, true);
-       ArraySearchResult res = array_search(&sector->blocks, &pos.y);
+       char decompressed_data[uncompressed_size];
 
-       MapBlock *block;
+       z_stream stream;
+       stream.zalloc = Z_NULL;
+    stream.zfree = Z_NULL;
+    stream.opaque = Z_NULL;
 
-       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);
-       }
+       stream.avail_in = size - sizeof(MapBlockHeader);
+    stream.next_in = (Bytef *) (data + sizeof(MapBlockHeader));
+    stream.avail_out = uncompressed_size;
+    stream.next_out = (Bytef *) decompressed_data;
 
-       bool ret = true;
-       size_t n_read_total = 0;
-       int n_read;
+    inflateInit(&stream);
+    inflate(&stream, Z_NO_FLUSH);
+    inflateEnd(&stream);
 
-       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;
-               }
+    if (stream.total_out < uncompressed_size)
+               return false;
 
-               n_read_total += n_read;
-       }
+       MapBlockData blockdata;
+       memcpy(blockdata, decompressed_data, sizeof(MapBlockData));
 
        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);
-       }
+               MapNode node = blockdata[x][y][z];
+               node.type = be32toh(node.type);
 
-       // ToDo: Deserialize meta
+               if (node.type >= NODE_UNLOADED)
+                       node.type = NODE_INVALID;
 
-       if (dummy)
-               map_free_block(block);
-       else if (blockptr)
-               *blockptr = block;
-
-       return ret;
-}
+               block->data[x][y][z] = node;
 
-bool map_serialize(int fd, 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++)
-                       if (! map_serialize_block(fd, map_get_block_raw(sector, b)))
-                               return false;
+               block->metadata[x][y][z] = list_create(&list_compare_string);
        }
-       return true;
-}
 
-void map_deserialize(int fd, Map *map)
-{
-       while (map_deserialize_block(fd, map, NULL, false))
-               ;
+       block->state = MBS_MODIFIED;
+
+       return true;
 }
 
 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
@@ -231,10 +264,11 @@ 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) {
+               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;
-
-               block->state = MBS_MODIFIED;
+               pthread_mutex_unlock(&block->mtx);
        }
 }