X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmap.c;h=a9a83befb97c8502fc90ccdaecbcaf7d390edc70;hb=66db44bf1d9429ecc69d5670fe584ab752a14910;hp=b239fbd5aad3c4d4041b6253c102e784a6a0cde1;hpb=873efe17d2f0b5ef556909c9a85da9470971f55b;p=dragonblocks_alpha.git diff --git a/src/map.c b/src/map.c index b239fbd..a9a83be 100644 --- a/src/map.c +++ b/src/map.c @@ -1,211 +1,197 @@ #include #include +#include #include -#include "binsearch.h" +#include #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(§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 = *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, §or_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, §or, res.index); + 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); - 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 (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 = *get_block_ptr(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); + bintree_add_node(§or->blocks, nodeptr, &pos.y, block = map_allocate_block(pos)); - if (map->on_block_create) - map->on_block_create(block); - - array_insert(§or->blocks, &block, res.index); - } else { - return NULL; + if (map->callbacks.create_block) + map->callbacks.create_block(block); } - return block->ready ? block : NULL; -} + pthread_rwlock_unlock(§or->rwlck); -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(§or->blocks, &block, res.index); - } - if (map->on_block_add) - map->on_block_add(block); + return block; } -void map_clear_block(MapBlock *block, v3u8 init_state) +MapBlock *map_allocate_block(v3s32 pos) { - 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]); -} + 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); -void map_free_block(MapBlock *block) -{ - map_clear_block(block, (v3u8) {15, 15, 15}); - free(block); + ITERATE_MAPBLOCK block->data[x][y][z] = map_node_create(NODE_UNKNOWN, (Blob) {0, NULL}); + + return block; } -bool map_deserialize_node(int fd, MapNode *node) +void map_free_block(MapBlock *block) { - Node type; - - if (! read_u32(fd, &type)) - return false; + ITERATE_MAPBLOCK map_node_delete(block->data[x][y][z]); - if (type > NODE_INVALID) - type = NODE_INVALID; - - *node = map_node_create(type); - - return true; + pthread_mutex_destroy(&block->mtx); + free(block); } -bool map_serialize_block(int fd, MapBlock *block) +Blob map_serialize_block(MapBlock *block) { - if (! write_v3s32(fd, block->pos)) - return false; + SerializedMapBlock block_data; ITERATE_MAPBLOCK { - if (! write_u32(fd, block->data[x][y][z].type)) - return false; - } + MapNode *node = &block->data[x][y][z]; + SerializedMapNode *node_data = &block_data.raw.nodes[x][y][z]; - return true; -} - -MapBlock *map_deserialize_block(int fd) -{ - v3s32 pos; + *node_data = (SerializedMapNode) { + .type = node->type, + .data = { + .siz = 0, + .data = NULL, + }, + }; - if (! read_v3s32(fd, &pos)) - return NULL; + NodeDefinition *def = &node_definitions[node->type]; - MapBlock *block = allocate_block(pos); - - ITERATE_MAPBLOCK { - if (! map_deserialize_node(fd, &block->data[x][y][z])) { - map_clear_block(block, (v3u8) {x, y, z}); - free(block); - return NULL; - } + 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); + + 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); - while ((block = map_deserialize_block(fd)) != NULL) - map_add_block(map, block); + 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) @@ -214,27 +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) { - MapNode *current_node = &block->data[offset.x][offset.y][offset.z]; - map_node_clear(current_node); - *current_node = node; + 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); }