#include <stdlib.h>
#include <stdbool.h>
+#include <unistd.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 = §or_compare;
- return map;
+ MapSector *sector = node->value;
+
+ bintree_clear(§or->blocks, &free_block, arg);
+ pthread_rwlock_destroy(§or->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(§or->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, §or, 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(§or->blocks, &pos.y);
+ if (create)
+ pthread_rwlock_wrlock(§or->rwlck);
+ else
+ pthread_rwlock_rdlock(§or->rwlck);
+
+ BintreeNode **nodeptr = bintree_search(§or->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(§or->blocks, &block, res.index);
- } else {
- return NULL;
+ bintree_add_node(§or->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(§or->rwlck);
-void map_free_block(MapBlock *block)
-{
- ITERATE_MAPBLOCK map_node_clear(&block->data[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 {
- 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);
}
-bool map_deserialize_block(int fd, Map *map, MapBlock **blockptr, bool dummy)
+Blob map_serialize_block(MapBlock *block)
{
- v3s32 pos;
+ SerializedMapBlock block_data;
- if (! read_v3s32(fd, &pos))
- return false;
-
- MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, true);
- ArraySearchResult res = array_search(§or->blocks, &pos.y);
+ ITERATE_MAPBLOCK {
+ MapNode *node = &block->data[x][y][z];
+ SerializedMapNode *node_data = &block_data.raw.nodes[x][y][z];
- MapBlock *block;
+ *node_data = (SerializedMapNode) {
+ .type = node->type,
+ .data = {
+ .siz = 0,
+ .data = 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(§or->blocks, &block, res.index);
- }
+ NodeDefinition *def = &node_definitions[node->type];
- ITERATE_MAPBLOCK {
- if (! map_deserialize_node(fd, &block->data[x][y][z])) {
- if (dummy)
- map_free_block(block);
- return false;
- }
+ if (def->serialize)
+ def->serialize(&node_data->data, node->data);
}
- 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 true;
+ 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)
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) {
- MapNode *current_node = &block->data[offset.x][offset.y][offset.z];
- map_node_clear(current_node);
- *current_node = 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, 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);
}