]> git.lizzy.rs Git - dragonblocks_alpha.git/commitdiff
Rework mapblock loading system and add mapgen stage buffer, 64bit floats for player...
authorElias Fleckenstein <eliasfleckenstein@web.de>
Thu, 19 Aug 2021 00:29:57 +0000 (02:29 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Thu, 19 Aug 2021 00:29:57 +0000 (02:29 +0200)
47 files changed:
src/CMakeLists.txt
src/bintree.c
src/bintree.h
src/client/blockmesh.c
src/client/blockmesh.h
src/client/camera.c
src/client/camera.h
src/client/client.c
src/client/client.h
src/client/client_commands.c
src/client/client_commands.h
src/client/client_map.c
src/client/client_map.h
src/client/client_player.c
src/client/client_player.h
src/client/facecache.c [new file with mode: 0644]
src/client/facecache.h [new file with mode: 0644]
src/client/game.c
src/client/hud.c
src/client/hud.h
src/client/input.c
src/client/object.c
src/client/object.h
src/client/scene.c
src/client/texture.c
src/map.c
src/map.h
src/node.c
src/node.h
src/server/facecache.c [deleted file]
src/server/facecache.h [deleted file]
src/server/mapdb.c
src/server/mapdb.h
src/server/mapgen.c
src/server/mapgen.h
src/server/perlin.c
src/server/perlin.h
src/server/server.c
src/server/server.h
src/server/server_commands.c
src/server/server_commands.h
src/server/server_map.c
src/server/server_map.h
src/types.c
src/types.h
src/util.c
src/util.h

index 814018eaa56cb029ef2936c00e94988c549fb0e3..68e93a7dbba5cc6f1064b232d0420df5a05036c3 100644 (file)
@@ -49,6 +49,7 @@ add_executable(Dragonblocks
        client/client_node.c
        client/client_player.c
        client/cube.c
+       client/facecache.c
        client/font.c
        client/game.c
        client/hud.c
@@ -75,7 +76,6 @@ target_include_directories(Dragonblocks PUBLIC
 
 add_executable(DragonblocksServer
        ${SOURCES_COMMON}
-       server/facecache.c
        server/mapdb.c
        server/mapgen.c
        server/perlin.c
index d1ba9a80be618de28d7d8921b64b3925ebf33fb6..f68fa1e29e54b0dfb39237d6045187c7b47d0c55 100644 (file)
@@ -40,20 +40,22 @@ void bintree_add_node(Bintree *tree, BintreeNode **nodeptr, void *key, void *val
        (*nodeptr)->left = (*nodeptr)->right = NULL;
 }
 
-static void free_recursive(BintreeNode *node, BintreeFreeFunction func)
+static void free_recursive(BintreeNode *node, BintreeFreeFunction func, void *arg)
 {
        if (node) {
+               free_recursive(node->left, func, arg);
+               free_recursive(node->right, func, arg);
                free(node->key);
-               free_recursive(node->left, func);
-               free_recursive(node->right, func);
+               if (func)
+                       func(node->value, arg);
                free(node);
        }
 }
 
-void bintree_clear(Bintree *tree, BintreeFreeFunction func)
+void bintree_clear(Bintree *tree, BintreeFreeFunction func, void *arg)
 {
        if (tree) {
-               free_recursive(tree->root, func);
+               free_recursive(tree->root, func, arg);
                tree->root = NULL;
        }
 }
index 3b5637a54b1884fc6375a4faab0fd7a567891796..49efe15cdb9f054b5f3419a07e7f7f5dee484f59 100644 (file)
@@ -17,11 +17,11 @@ typedef struct
        size_t key_size;
 } Bintree;
 
-typedef void (*BintreeFreeFunction)(void *value);
+typedef void (*BintreeFreeFunction)(void *value, void *arg);
 
 Bintree bintree_create(size_t key_size);
 BintreeNode **bintree_search(Bintree *tree, void *key);
 void bintree_add_node(Bintree *tree, BintreeNode **nodeptr, void *key, void *value);
-void bintree_clear(Bintree *tree, BintreeFreeFunction func);
+void bintree_clear(Bintree *tree, BintreeFreeFunction func, void *arg);
 
 #endif
index ef76424835edcac054b697bd599e22e45e1948a1..3ee7422a3a1d39514955b0a787ae164841dee36e 100644 (file)
@@ -1,4 +1,5 @@
 #include "client/blockmesh.h"
+#include "client/client_map.h"
 #include "client/client_node.h"
 #include "client/cube.h"
 
@@ -11,13 +12,13 @@ static v3s8 fdir[6] = {
        {+0, +1, +0},
 };
 
-static void make_vertices(Object *object, MapBlock *block, Map *map)
+static void make_vertices(Object *object, MapBlock *block)
 {
        ITERATE_MAPBLOCK {
                MapNode *node = &block->data[x][y][z];
 
                if (node_definitions[node->type].visible) {
-                       v3f offset = {x + 8.0f, y + 8.0f, z + 8.0f};
+                       v3f32 offset = {x + (f32) MAPBLOCK_SIZE / 2.0f, y + (f32) MAPBLOCK_SIZE / 2.0f, z + (f32) MAPBLOCK_SIZE / 2.0f};
 
                        ClientNodeDefintion *client_def = &client_node_definitions[node->type];
                        object_set_texture(object, client_def->texture);
@@ -31,10 +32,12 @@ static void make_vertices(Object *object, MapBlock *block, Map *map)
 
                                Node neighbor;
 
-                               if (npos.x >= 0 && npos.x < 16 && npos.y >= 0 && npos.y < 16 && npos.z >= 0 && npos.z < 16)
+                               if (npos.x >= 0 && npos.x < MAPBLOCK_SIZE && npos.y >= 0 && npos.y < MAPBLOCK_SIZE && npos.z >= 0 && npos.z < MAPBLOCK_SIZE)
                                        neighbor = block->data[npos.x][npos.y][npos.z].type;
-                               else
-                                       neighbor = map_get_node(map, (v3s32) {npos.x + block->pos.x * 16, npos.y + block->pos.y * 16, npos.z + block->pos.z * 16}).type;
+                               else {
+                                       MapNode nn = map_get_node(client_map.map, (v3s32) {npos.x + block->pos.x * MAPBLOCK_SIZE, npos.y + block->pos.y * MAPBLOCK_SIZE, npos.z + block->pos.z * MAPBLOCK_SIZE});
+                                       neighbor = nn.type;
+                               }
 
                                if (neighbor != NODE_UNLOADED && ! node_definitions[neighbor].visible) {
                                        for (int v = 0; v < 6; v++) {
@@ -54,20 +57,22 @@ static void make_vertices(Object *object, MapBlock *block, Map *map)
        }
 }
 
-void blockmesh_make(MapBlock *block, Map *map)
+void blockmesh_make(MapBlock *block)
 {
        Object *obj = object_create();
-       obj->pos = (v3f) {block->pos.x * 16.0f - 8.0f, block->pos.y * 16.0f - 8.0f, block->pos.z * 16.0f - 8.0f};
+       obj->pos = (v3f32) {block->pos.x * (f32) MAPBLOCK_SIZE - (f32) MAPBLOCK_SIZE / 2.0f, block->pos.y * (f32) MAPBLOCK_SIZE - (f32) MAPBLOCK_SIZE / 2.0f, block->pos.z * (f32) MAPBLOCK_SIZE - (f32) MAPBLOCK_SIZE / 2.0};
 
-       make_vertices(obj, block, map);
+       make_vertices(obj, block);
 
        if (! object_add_to_scene(obj)) {
                object_delete(obj);
                obj = NULL;
        }
 
-       if (block->extra)
-               ((Object *) block->extra)->remove = true;
+       MapBlockExtraData *extra = block->extra;
+
+       if (extra->obj)
+               extra->obj->remove = true;
 
-       block->extra = obj;
+       extra->obj = obj;
 }
index d2c3840a58448bb81b09fe334c2dba6e8a68257d..f469a06605209318290c79153f895066135ab75e 100644 (file)
@@ -1,9 +1,8 @@
 #ifndef _BLOCKMESH_H_
 #define _BLOCKMESH_H_
 
-#include "client/scene.h"
 #include "map.h"
 
-void blockmesh_make(MapBlock *block, Map *map);
+void blockmesh_make(MapBlock *block);
 
 #endif
index eda29f0c5dd8c26143e695d2df93647db02f9a4c..4b6198e16c7fbb115650126976dc9a0c0d00df32 100644 (file)
@@ -25,7 +25,7 @@ void camera_enable(GLint loc_view)
        glUniformMatrix4fv(loc_view, 1, GL_FALSE, camera.view[0]);
 }
 
-void camera_set_position(v3f pos)
+void camera_set_position(v3f32 pos)
 {
        camera.eye[0] = pos.x;
        camera.eye[1] = pos.y;
index 319a5fbcd1a1e78cc433b95443c2974a9487715b..3d4ebdcfc1413998338a92da79634b2deed7e72c 100644 (file)
@@ -6,7 +6,7 @@
 #include <linmath.h/linmath.h>
 #include "types.h"
 
-void camera_set_position(v3f pos);
+void camera_set_position(v3f32 pos);
 void camera_set_angle(f32 yaw, f32 pitch);
 void camera_on_resize(int width, int height);
 void camera_enable(GLint loc_view);
index 324514041f5b547e8d24c0cb06302b4a340c3eef..1fa9dbede58ef781cf745203e2f3547d45f16cef 100644 (file)
@@ -13,7 +13,7 @@
 #include "signal_handlers.h"
 #include "util.h"
 
-Client client;
+static Client client;
 
 void client_disconnect(bool send, const char *detail)
 {
@@ -29,16 +29,16 @@ void client_disconnect(bool send, const char *detail)
        pthread_mutex_unlock(&client.mtx);
 }
 
-void client_send_position(v3f pos)
+void client_send_position(v3f64 pos)
 {
        pthread_mutex_lock(&client.mtx);
-       (void) (write_u32(client.fd, SC_POS) && write_v3f32(client.fd, pos));
+       (void) (write_u32(client.fd, SC_POS) && write_v3f64(client.fd, pos));
        pthread_mutex_unlock(&client.mtx);
 }
 
 #include "network.c"
 
-static void *reciever_thread(__attribute__((unused)) void *unused)
+static void *reciever_thread(__attribute__((unused)) void *arg)
 {
        handle_packets(&client);
 
@@ -95,10 +95,9 @@ static void client_start(int fd)
        pthread_mutex_init(&client.mtx, NULL);
        client.state = CS_CREATED;
        client.name = NULL;
-       client.map = map_create();
 
-       client_player_init(client.map);
-       client_map_init(client.map);
+       client_map_init(&client);
+       client_player_init();
 
        pthread_t recv_thread;
        pthread_create(&recv_thread, NULL, &reciever_thread, NULL);
@@ -112,9 +111,10 @@ static void client_start(int fd)
        if (client.name)
                free(client.name);
 
-       client_map_deinit();
+       pthread_join(recv_thread, NULL);
 
-       map_delete(client.map);
+       client_player_deinit();
+       client_map_deinit();
 
        pthread_mutex_destroy(&client.mtx);
 }
index badac31fb4df9fd7388dcf588683303f11fc6484..d5ac964be8bde8d9f656759d9465a2ee14451e7b 100644 (file)
@@ -6,7 +6,6 @@
 #include "client/client_commands.h"
 #include "client/scene.h"
 #include "server/server_commands.h"
-#include "map.h"
 #include "network.h"
 #include "types.h"
 
@@ -22,10 +21,9 @@ typedef struct Client
        pthread_mutex_t mtx;
        ClientState state;
        char *name;
-       Map *map;
 } Client;
 
 void client_disconnect(bool send, const char *detail);
-void client_send_position(v3f pos);
+void client_send_position(v3f64 pos);
 
 #endif
index 5581c0f88a972ddb07ead1f970ded21578b580d3..ad253cfe7cda2d1560b833c2832e89e409c9e0cd 100644 (file)
@@ -38,38 +38,54 @@ static bool block_handler(Client *client, bool good)
        if (! read_v3s32(client->fd, &pos))
                return false;
 
-       MapBlockHeader header;
+       size_t size;
 
-       if (! read_u32(client->fd, &header))
+       if (! read_u64(client->fd, &size))
                return false;
 
-       char data[header];
-       if (! read_full(client->fd, data, header))
+       if (size > sizeof(MapBlockData))        // guard to prevent malicious or malfunctioning packets from allocating huge unnecessary amounts of memory
+               return false;
+
+       char data[size];
+       if (! read_full(client->fd, data, size))
                return false;
 
        MapBlock *block;
 
        if (good)
-               block = map_get_block(client->map, pos, true);
+               block = map_get_block(client_map.map, pos, true);
        else
                block = map_allocate_block(pos);
 
-       if (block->state != MBS_CREATED)
-               map_clear_meta(block);
+       map_clear_meta(block);
 
-       bool ret = map_deserialize_block(block, data, header);
+       bool ret = map_deserialize_block(block, data, size);
 
        if (good)
-               client_map_block_changed(block);
+               client_map_block_received(block);
        else
                map_free_block(block);
 
        return ret;
 }
 
+static bool simulation_distance_handler(Client *client, bool good)
+{
+       u32 simulation_distance;
+
+       if (! read_u32(client->fd, &simulation_distance))
+               return false;
+
+       if (good)
+               client_map_set_simulation_distance(simulation_distance);
+
+       return true;
+}
+
 CommandHandler command_handlers[CLIENT_COMMAND_COUNT] = {
        {0},
        {&disconnect_handler, "DISCONNECT", CS_CREATED | CS_AUTH | CS_ACTIVE},
        {&auth_handler, "AUTH", CS_AUTH},
        {&block_handler, "BLOCK", CS_ACTIVE},
+       {&simulation_distance_handler, "SIMULATION_DISTANCE", CS_ACTIVE},
 };
index 58bb9e4ae112a20a29a9ceaeb7416ebe6a4f371e..388da66c44550a36a734d416256695dbda5a13bf 100644 (file)
@@ -7,6 +7,7 @@ typedef enum
        CC_DISCONNECT,
        CC_AUTH,
        CC_BLOCK,
+       CC_SIMULATION_DISTANCE,
        CLIENT_COMMAND_COUNT
 } ClientCommand;
 
index 843cbdd94957a4630f201e849758039d3fd5f7af..1684c5c78e587f20ba1c5351decc256fd278cfff 100644 (file)
+#include <stdio.h>
 #include <stdlib.h>
-#include <pthread.h>
 #include "client/blockmesh.h"
+#include "client/facecache.h"
 #include "client/client_map.h"
-#include "queue.h"
+#include "client/client_player.h"
+#include "util.h"
+#define MAX_BLOCK_REQUESTS 4
 
-static struct
+struct ClientMap client_map;
+Client *client;
+
+// meshgen functions
+
+// dequeue callback to thread-safely update
+static void set_dequeued(void *arg)
+{
+       MapBlock *block = arg;
+
+       pthread_mutex_lock(&block->mtx);
+       ((MapBlockExtraData *) block->extra)->queue = false;
+       pthread_mutex_unlock(&block->mtx);
+}
+
+// mesh generator step
+static void meshgen_step()
+{
+       MapBlock *block;
+
+       if ((block = queue_dequeue_callback(client_map.queue, &set_dequeued)))
+               blockmesh_make(block);
+       else
+               sched_yield();
+}
+
+// pthread start routine for meshgen thread
+static void *meshgen_thread(__attribute__((unused)) void *arg)
 {
-       Map *map;
-       Queue *queue;
-       pthread_t thread;
-       bool cancel;
-} client_map;
+       while (! client_map.cancel)
+               meshgen_step();
+
+       return NULL;
+}
+
+// enqueue mesh to block update queue
+static void schedule_update_block_mesh(MapBlock *block)
+{
+       if (! block)
+               return;
 
-static void set_block_ready(void *block)
+       pthread_mutex_lock(&block->mtx);
+       MapBlockExtraData *extra = block->extra;
+       if (! extra->queue) {
+               extra->queue = true;
+               queue_enqueue(client_map.queue, block);
+       }
+       pthread_mutex_unlock(&block->mtx);
+}
+
+// sync functions
+
+// send block request command to server
+static void request_position(v3s32 pos)
 {
-       ((MapBlock *) block)->state = MBS_READY;
+       pthread_mutex_lock(&client->mtx);
+       (void) (write_u32(client->fd, SC_REQUEST_BLOCK) && write_v3s32(client->fd, pos));
+       pthread_mutex_unlock(&client->mtx);
 }
 
-static void *meshgen_thread(__attribute__((unused)) void *unused)
+// mapblock synchronisation step
+static void sync_step()
 {
-       while (! client_map.cancel) {
-               MapBlock *block;
-               if ((block = queue_dequeue_callback(client_map.queue, &set_block_ready)))
-                       blockmesh_make(block, client_map.map);
-               else
-                       sched_yield();
+       static u64 tick = 0;
+       static v3s32 *old_requested_positions = NULL;
+       static size_t old_requested_positions_count = 0;
+
+       u64 last_tick = tick++;
+
+       v3f64 player_pos = client_player_get_position();
+       v3s32 center = map_node_to_block_pos((v3s32) {player_pos.x, player_pos.y, player_pos.z}, NULL);
+
+       v3s32 *requested_positions = malloc(sizeof(v3s32) * MAX_BLOCK_REQUESTS);
+       size_t requested_positions_count = 0;
+
+       for (size_t i = 0; i < client_map.blocks_count; i++) {
+               v3s32 pos = facecache_face(i, &center);
+               MapBlock *block = map_get_block(client_map.map, pos, false);
+
+               if (block) {
+                       pthread_mutex_lock(&block->mtx);
+                       MapBlockExtraData *extra = block->extra;
+
+                       switch (extra->state) {
+                               case MBS_READY:
+                                       if (extra->last_synced < last_tick)
+                                               request_position(pos);
+                                       fallthrough;
+
+                               case MBS_FRESH:
+                                       extra->state = MBS_READY;
+                                       extra->last_synced = tick;
+                                       break;
+
+                               case MBS_RECIEVING:
+                                       break;
+                       }
+                       pthread_mutex_unlock(&block->mtx);
+               } else if (requested_positions_count < MAX_BLOCK_REQUESTS) {
+                       bool should_request = true;
+
+                       for (size_t i = 0; i < old_requested_positions_count; i++) {
+                               if (v3s32_equals(old_requested_positions[i], pos)) {
+                                       should_request = false;
+                                       break;
+                               }
+                       }
+
+                       if (should_request)
+                               request_position(pos);
+
+                       requested_positions[requested_positions_count++] = pos;
+               }
        }
 
+       if (old_requested_positions)
+               free(old_requested_positions);
+
+       old_requested_positions = requested_positions;
+       old_requested_positions_count = requested_positions_count;
+}
+
+// pthread start routine for sync thread
+static void *sync_thread(__attribute__((unused)) void *arg)
+{
+       while (! client_map.cancel)
+               sync_step();
+
        return NULL;
 }
 
-void client_map_init(Map *map)
+// map callbacks
+// note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
+
+// callback for initializing a newly created block
+// allocate and initialize extra data
+static void on_create_block(MapBlock *block)
 {
-       client_map.map = map;
-       client_map.queue = queue_create();
+       MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
+
+       extra->state = MBS_RECIEVING;
+       extra->queue = false;
+       extra->last_synced = 0;
+       extra->obj = NULL;
 }
 
-void client_map_start_meshgen()
+// callback for deleting a block
+// free extra data
+static void on_delete_block(MapBlock *block)
 {
-       pthread_create(&client_map.thread, NULL, &meshgen_thread, NULL);
+       free(block->extra);
+}
+
+// callback for determining whether a block should be returned by map_get_block
+// hold back blocks that have not been fully read from server yet when the create flag is set to true
+static bool on_get_block(MapBlock *block, bool create)
+{
+       return create || ((MapBlockExtraData *) block->extra)->state > MBS_RECIEVING;
+}
+
+// public functions
+
+// ClientMap singleton constructor
+void client_map_init(Client *cli)
+{
+       client = cli;
+
+       client_map.map = map_create((MapCallbacks) {
+               .create_block = &on_create_block,
+               .delete_block = &on_delete_block,
+               .get_block = &on_get_block,
+               .set_node = NULL,
+               .after_set_node = NULL,
+       });
+       client_map.queue = queue_create();
+       client_map.cancel = false;
+       client_map.meshgen_thread = client_map.sync_thread = 0;
+       client_map_set_simulation_distance(16);
 }
 
+// ClientMap singleton destructor
 void client_map_deinit()
 {
-       client_map.cancel = true;
        queue_delete(client_map.queue);
-       if (client_map.thread)
-               pthread_join(client_map.thread, NULL);
+       map_delete(client_map.map);
 }
 
-static void schedule_update_block(MapBlock *block)
+// start meshgen and sync threads
+void client_map_start()
 {
-       if (! block)
-               return;
+       pthread_create(&client_map.meshgen_thread, NULL, &meshgen_thread, NULL);
+       pthread_create(&client_map.sync_thread, NULL, &sync_thread, NULL);
+}
 
-       pthread_mutex_lock(&block->mtx);
-       if (block->state != MBS_PROCESSING) {
-               block->state = MBS_PROCESSING;
-               queue_enqueue(client_map.queue, block);
-       }
-       pthread_mutex_unlock(&block->mtx);
+// stop meshgen and sync threads
+void client_map_stop()
+{
+       client_map.cancel = true;
+
+       if (client_map.meshgen_thread)
+               pthread_join(client_map.meshgen_thread, NULL);
+
+       if (client_map.sync_thread)
+               pthread_join(client_map.sync_thread, NULL);
 }
 
-void client_map_block_changed(MapBlock *block)
+// update simulation distance
+void client_map_set_simulation_distance(u32 simulation_distance)
 {
-       schedule_update_block(block);
+       client_map.simulation_distance = simulation_distance;
+       client_map.blocks_count = facecache_count(simulation_distance);
+}
+
+// called when a block was actually recieved from server
+void client_map_block_received(MapBlock *block)
+{
+       pthread_mutex_lock(&block->mtx);
+       MapBlockExtraData *extra = block->extra;
+       if (extra->state == MBS_RECIEVING)
+               extra->state = MBS_FRESH;
+       pthread_mutex_unlock(&block->mtx);
+
+       schedule_update_block_mesh(block);
 
-       schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x + 1, block->pos.y + 0, block->pos.z + 0}, false));
-       schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 1, block->pos.z + 0}, false));
-       schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 0, block->pos.z + 1}, false));
-       schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x - 1, block->pos.y - 0, block->pos.z - 0}, false));
-       schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 1, block->pos.z - 0}, false));
-       schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 0, block->pos.z - 1}, false));
+       schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 1, block->pos.y + 0, block->pos.z + 0}, false));
+       schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 1, block->pos.z + 0}, false));
+       schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 0, block->pos.z + 1}, false));
+       schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 1, block->pos.y - 0, block->pos.z - 0}, false));
+       schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 1, block->pos.z - 0}, false));
+       schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 0, block->pos.z - 1}, false));
 }
index 40949cc68a9c239556dc99c948c662f29510c5e6..657dcb1dd4e15b9cdb49ee292215e1bc2d68b0bc 100644 (file)
@@ -1,11 +1,43 @@
 #ifndef _CLIENT_MAP_H_
 #define _CLIENT_MAP_H_
 
+#include <stdbool.h>
+#include <pthread.h>
 #include "map.h"
+#include "queue.h"
+#include "client/object.h"
 
-void client_map_init(Map *map);
-void client_map_start_meshgen();
-void client_map_deinit();
-void client_map_block_changed(MapBlock *block);
+typedef enum
+{
+       MBS_RECIEVING,  // currently deserializing
+       MBS_FRESH,              // first deserialisation finished, not processed by sync thread yet
+       MBS_READY,              // ready to use and processed by sync thread
+} MapBlockState;
+
+typedef struct
+{
+       MapBlockState state;    // keep track of the deserialisation and sync processing state
+       bool queue;                             // whether the block is in meshgen queue
+       u64 last_synced;                // keep track of when a block was synced the last time (used to detect when a block got out of and then back into range)
+       Object *obj;                    // mesh object, generated by blockmesh file
+} MapBlockExtraData;
+
+extern struct ClientMap
+{
+       Map *map;                                                                               // map object
+       Queue *queue;                                                                   // MapBlock * queue (thread safe)
+       bool cancel;                                                                    // used to notify meshgen and sync thread about quit
+       pthread_t meshgen_thread;                                               // consumer thread for meshgen queue
+       pthread_t sync_thread;                                                  // this thread requests new / changed blocks from server
+       u32 simulation_distance;                                                // simulation distance sent by server
+       size_t blocks_count;                                                    // cached number of facecache positions to process every sync step (matches simulation distance)
+} client_map;
+
+void client_map_init();                                                                                                // ClientMap singleton constructor
+void client_map_deinit();                                                                                      // ClientMap singleton destructor
+void client_map_set_simulation_distance(u32 simulation_distance);      // update simulation distance
+void client_map_start();                                                                                       // start meshgen and sync threads
+void client_map_stop();                                                                                                // stop meshgen and sync threads
+void client_map_block_received(MapBlock *block);                                       // called when a block was actually recieved from server
 
 #endif
index 0fdd10223b9cd543821142e299f63efd851b8eb5..43974a03e013b63ebe17f85a4f8e7caeca64ecda 100644 (file)
@@ -1,18 +1,21 @@
 #include <stdio.h>
 #include "client/camera.h"
 #include "client/client.h"
+#include "client/client_map.h"
 #include "client/client_player.h"
 #include "client/cube.h"
 #include "client/texture.h"
 
 struct ClientPlayer client_player;
 
+// to be called whenever the player position changes
+// rwlock has to be read or write locked
 static void update_pos()
 {
-       camera_set_position((v3f) {client_player.pos.x, client_player.pos.y + client_player.eye_height, client_player.pos.z});
+       camera_set_position((v3f32) {client_player.pos.x, client_player.pos.y + client_player.eye_height, client_player.pos.z});
        client_send_position(client_player.pos);
 
-       client_player.obj->pos = client_player.pos;
+       client_player.obj->pos = (v3f32) {client_player.pos.x, client_player.pos.y, client_player.pos.z};
        object_transform(client_player.obj);
 
        char pos_text[BUFSIZ];
@@ -21,20 +24,74 @@ static void update_pos()
        hud_change_text(client_player.pos_display, pos_text);
 }
 
-void client_player_init(Map *map)
+// get absolute player bounding box
+// rwlock has to be read- or write locked
+static aabb3f64 get_box()
 {
-       client_player.map = map;
-       client_player.pos = (v3f) {0.0f, 200.0f, 0.0f};
-       client_player.velocity = (v3f) {0.0f, 0.0f, 0.0f};
-       client_player.box = (aabb3f) {{-0.3f, 0.0f, -0.3f}, {0.3f, 1.75f, 0.3f}};
+       return (aabb3f64) {
+               {client_player.box.min.x + client_player.pos.x, client_player.box.min.y + client_player.pos.y, client_player.box.min.z + client_player.pos.z},
+               {client_player.box.max.x + client_player.pos.x, client_player.box.max.y + client_player.pos.y, client_player.box.max.z + client_player.pos.z},
+       };
+}
+
+// get absolute integer box that contains all nodes a float bounding box touches
+static aabb3s32 round_box(aabb3f64 box)
+{
+       return (aabb3s32) {
+               {floor(box.min.x + 0.5), floor(box.min.y + 0.5), floor(box.min.z + 0.5)},
+               {ceil(box.max.x - 0.5), ceil(box.max.y - 0.5), ceil(box.max.z - 0.5)},
+       };
+}
+
+// return true if node at x, y, z is solid (or unloaded)
+static bool is_solid(s32 x, s32 y, s32 z)
+{
+       Node node = map_get_node(client_map.map, (v3s32) {x, y, z}).type;
+       return node == NODE_UNLOADED || node_definitions[node].solid;
+}
+
+// determine if player can jump currently (must be standing on a solid block)
+// rwlock has to be read- or write locked
+static bool can_jump()
+{
+       aabb3f64 fbox = get_box();
+       fbox.min.y -= 0.5;
+
+       aabb3s32 box = round_box(fbox);
+
+       if (fbox.min.y - (f64) box.min.y > 0.01)
+               return false;
+
+       for (s32 x = box.min.x; x <= box.max.x; x++)
+               for (s32 z = box.min.z; z <= box.max.z; z++)
+                       if (is_solid(x, box.min.y, z))
+                               return true;
+
+       return false;
+}
+
+// ClientPlayer singleton constructor
+void client_player_init()
+{
+       client_player.pos = (v3f64) {0.0, 200.0, 0.0};
+       client_player.velocity = (v3f64) {0.0, 0.0, 0.0};
+       client_player.box = (aabb3f64) {{-0.3, 0.0, -0.3}, {0.3, 1.75, 0.3}};
        client_player.yaw = client_player.pitch = 0.0f;
-       client_player.eye_height = 1.5f;
+       client_player.eye_height = 1.5;
+       pthread_rwlock_init(&client_player.rwlock, NULL);
+}
+
+// ClientPlayer singleton destructor
+void client_player_deinit()
+{
+       pthread_rwlock_destroy(&client_player.rwlock);
 }
 
+// create mesh object and hud display
 void client_player_add_to_scene()
 {
        client_player.obj = object_create();
-       client_player.obj->scale = (v3f) {0.6f, 1.75f, 0.6f};
+       client_player.obj->scale = (v3f32) {0.6, 1.75, 0.6};
        client_player.obj->visible = false;
 
        object_set_texture(client_player.obj, texture_get(RESSOURCEPATH "textures/player.png"));
@@ -59,91 +116,71 @@ void client_player_add_to_scene()
                },
        });
 
+       pthread_rwlock_rdlock(&client_player.rwlock);
        update_pos();
+       pthread_rwlock_unlock(&client_player.rwlock);
 }
 
-static aabb3f get_box()
-{
-       return (aabb3f) {
-               {client_player.box.min.x + client_player.pos.x, client_player.box.min.y + client_player.pos.y, client_player.box.min.z + client_player.pos.z},
-               {client_player.box.max.x + client_player.pos.x, client_player.box.max.y + client_player.pos.y, client_player.box.max.z + client_player.pos.z},
-       };
-}
-
-static aabb3s32 round_box(aabb3f box)
+// jump if possible
+void client_player_jump()
 {
-       return (aabb3s32) {
-               {floor(box.min.x + 0.5f), floor(box.min.y + 0.5f), floor(box.min.z + 0.5f)},
-               {ceil(box.max.x - 0.5f), ceil(box.max.y - 0.5f), ceil(box.max.z - 0.5f)},
-       };
+       pthread_rwlock_wrlock(&client_player.rwlock);
+       if (can_jump())
+               client_player.velocity.y += 10.0;
+       pthread_rwlock_unlock(&client_player.rwlock);
 }
 
-static bool is_solid(s32 x, s32 y, s32 z)
+// get position (thread-safe)
+v3f64 client_player_get_position()
 {
-       Node node = map_get_node(client_player.map, (v3s32) {x, y, z}).type;
-       return node == NODE_UNLOADED || node_definitions[node].solid;
-}
+       v3f64 pos;
 
-static bool can_jump()
-{
-       aabb3f fbox = get_box();
-       fbox.min.y -= 0.5f;
+       pthread_rwlock_rdlock(&client_player.rwlock);
+       pos = client_player.pos;
+       pthread_rwlock_unlock(&client_player.rwlock);
 
-       aabb3s32 box = round_box(fbox);
-
-       if (fbox.min.y - (f32) box.min.y > 0.01f)
-               return false;
-
-       for (s32 x = box.min.x; x <= box.max.x; x++)
-               for (s32 z = box.min.z; z <= box.max.z; z++)
-                       if (is_solid(x, box.min.y, z))
-                               return true;
-
-       return false;
-}
-
-void client_player_jump()
-{
-       if (can_jump())
-               client_player.velocity.y += 10.0f;
+       return pos;
 }
 
+// to be called every frame
 void client_player_tick(f64 dtime)
 {
-       v3f old_pos = client_player.pos;
-       v3f old_velocity = client_player.velocity;
+       pthread_rwlock_wrlock(&client_player.rwlock);
 
-       client_player.velocity.y -= 32.0f * dtime;
+       v3f64 old_pos = client_player.pos;
+       v3f64 old_velocity = client_player.velocity;
+
+       client_player.velocity.y -= 32.0 * dtime;
 
 #define GETS(vec, comp) *(s32 *) ((char *) &vec + offsetof(v3s32, comp))
-#define GETF(vec, comp) *(f32 *) ((char *) &vec + offsetof(v3f32, comp))
+#define GETF(vec, comp) *(f64 *) ((char *) &vec + offsetof(v3f64, comp))
 #define PHYSICS(a, b, c) { \
-               f32 v = (GETF(client_player.velocity, a) + GETF(old_velocity, a)) / 2.0f; \
-               if (v == 0.0f) \
+               f64 v = (GETF(client_player.velocity, a) + GETF(old_velocity, a)) / 2.0f; \
+               if (v == 0.0) \
                        goto a ## _physics_done; \
                aabb3s32 box = round_box(get_box()); \
-               v3f old_pos = client_player.pos; \
+               v3f64 old_pos = client_player.pos; \
                GETF(client_player.pos, a) += v * dtime; \
                s32 dir; \
-               f32 offset; \
-               if (v > 0.0f) { \
+               f64 offset; \
+               if (v > 0.0) { \
                        dir = +1; \
                        offset = GETF(client_player.box.max, a); \
-                       GETS(box.min, a) = ceil(GETF(old_pos, a) + offset + 0.5f); \
-                       GETS(box.max, a) = floor(GETF(client_player.pos, a) + offset + 0.5f); \
+                       GETS(box.min, a) = ceil(GETF(old_pos, a) + offset + 0.5); \
+                       GETS(box.max, a) = floor(GETF(client_player.pos, a) + offset + 0.5); \
                } else { \
                        dir = -1; \
                        offset = GETF(client_player.box.min, a); \
-                       GETS(box.min, a) = floor(GETF(old_pos, a) + offset - 0.5f); \
-                       GETS(box.max, a) = ceil(GETF(client_player.pos, a) + offset - 0.5f); \
+                       GETS(box.min, a) = floor(GETF(old_pos, a) + offset - 0.5); \
+                       GETS(box.max, a) = ceil(GETF(client_player.pos, a) + offset - 0.5); \
                } \
                GETS(box.max, a) += dir; \
                for (s32 a = GETS(box.min, a); a != GETS(box.max, a); a += dir) { \
                        for (s32 b = GETS(box.min, b); b <= GETS(box.max, b); b++) { \
                                for (s32 c = GETS(box.min, c); c <= GETS(box.max, c); c++) { \
                                        if (is_solid(x, y, z)) { \
-                                               GETF(client_player.pos, a) = (f32) a - offset - 0.5f * (f32) dir; \
-                                               GETF(client_player.velocity, a) = 0.0f; \
+                                               GETF(client_player.pos, a) = (f64) a - offset - 0.5 * (f64) dir; \
+                                               GETF(client_player.velocity, a) = 0.0; \
                                                goto a ## _physics_done; \
                                        } \
                                } \
@@ -160,6 +197,8 @@ void client_player_tick(f64 dtime)
 #undef GETF
 #undef PHYSICS
 
-       if (old_pos.x != client_player.pos.x || old_pos.y != client_player.pos.y || old_pos.z != client_player.pos.z)
+       if (! v3f64_equals(old_pos, client_player.pos))
                update_pos();
+
+       pthread_rwlock_unlock(&client_player.rwlock);
 }
index 9d5d16f1aa776b4b549c2e0b99e05a3cfedb07bd..9554a6821c024dba836a31cb53466a97cafaf46c 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _CLIENT_PLAYER_H_
 #define _CLIENT_PLAYER_H_
 
+#include <pthread.h>
 #include "client/client.h"
 #include "client/hud.h"
 #include "client/object.h"
@@ -8,19 +9,21 @@
 
 extern struct ClientPlayer
 {
-       v3f pos;
-       v3f velocity;
-       aabb3f box;
-       f32 yaw, pitch;
-       f32 eye_height;
-       Object *obj;
-       Map *map;
-       HUDElement *pos_display;
+       v3f64 pos;                                      // feet position
+       v3f64 velocity;                         // current velocity
+       aabb3f64 box;                           // axis-aligned bounding box (used for collision), with 0, 0, 0 being the feet position
+       f32 yaw, pitch;                         // look direction
+       f64 eye_height;                         // eye height above feet
+       pthread_rwlock_t rwlock;        // used to protect the above properties
+       Object *obj;                            // 3D mesh object (currently always invisible), not thread safe
+       HUDElement *pos_display;        // display position on HUD, not thread safe
 } client_player;
 
-void client_player_init(Map *map);
-void client_player_add_to_scene();
-void client_player_jump();
-void client_player_tick(f64 dtime);
+void client_player_init();                             // ClientPlayer singleton constructor
+void client_player_deinit();                   // ClientPlayer singleton destructor
+void client_player_add_to_scene();             // create mesh object and hud display
+void client_player_jump();                             // jump if possible
+v3f64 client_player_get_position();            // get position (thread-safe)
+void client_player_tick(f64 dtime);            // to be called every frame
 
 #endif
diff --git a/src/client/facecache.c b/src/client/facecache.c
new file mode 100644 (file)
index 0000000..b902c90
--- /dev/null
@@ -0,0 +1,90 @@
+#include <stdlib.h>
+#include "client/facecache.h"
+#include "array.h"
+
+static struct
+{
+       Array positions;
+       u32 size;
+       pthread_mutex_t mtx;
+} facecache;
+
+__attribute((constructor)) static void face_cache_init()
+{
+       facecache.size = 0;
+       facecache.positions = array_create(sizeof(v3s32));
+       v3s32 pos = {0, 0, 0};
+       array_append(&facecache.positions, &pos);
+       pthread_mutex_init(&facecache.mtx, NULL);
+}
+
+__attribute((destructor)) void face_cache_deinit()
+{
+       if (facecache.positions.ptr)
+               free(facecache.positions.ptr);
+       pthread_mutex_destroy(&facecache.mtx);
+}
+
+static void face_cache_calculate(s32 size)
+{
+#define ADDPOS(a, b, c, va, vb, vc) \
+       { \
+               v3s32 pos; \
+               *(s32 *) ((char *) &pos + offsetof(v3s32, a)) = va; \
+               *(s32 *) ((char *) &pos + offsetof(v3s32, b)) = vb; \
+               *(s32 *) ((char *) &pos + offsetof(v3s32, c)) = vc; \
+               array_append(&facecache.positions, &pos); \
+       }
+#define SQUARES(a, b, c) \
+       for (s32 va = -size + 1; va < size; va++) { \
+               for (s32 vb = -size + 1; vb < size; vb++) { \
+                       ADDPOS(a, b, c, va, vb,  size) \
+                       ADDPOS(a, b, c, va, vb, -size) \
+               } \
+       }
+       SQUARES(x, z, y)
+       SQUARES(x, y, z)
+       SQUARES(z, y, x)
+#undef SQUARES
+#define EDGES(a, b, c) \
+       for (s32 va = -size + 1; va < size; va++) { \
+               ADDPOS(a, b, c, va,  size,  size) \
+               ADDPOS(a, b, c, va,  size, -size) \
+               ADDPOS(a, b, c, va, -size,  size) \
+               ADDPOS(a, b, c, va, -size, -size) \
+       }
+       EDGES(x, y, z)
+       EDGES(z, x, y)
+       EDGES(y, x, z)
+#undef EDGES
+       ADDPOS(x, y, z,  size,  size,  size)
+       ADDPOS(x, y, z,  size,  size, -size)
+       ADDPOS(x, y, z,  size, -size,  size)
+       ADDPOS(x, y, z,  size, -size, -size)
+       ADDPOS(x, y, z, -size,  size,  size)
+       ADDPOS(x, y, z, -size,  size, -size)
+       ADDPOS(x, y, z, -size, -size,  size)
+       ADDPOS(x, y, z, -size, -size, -size)
+#undef ADDPOS
+}
+
+v3s32 facecache_face(size_t i, v3s32 *base)
+{
+       pthread_mutex_lock(&facecache.mtx);
+       while (facecache.positions.siz <= i)
+               face_cache_calculate(++facecache.size);
+       v3s32 pos = ((v3s32 *) facecache.positions.ptr)[i];
+       pthread_mutex_unlock(&facecache.mtx);
+       if (base) {
+               pos.x += base->x;
+               pos.y += base->y;
+               pos.z += base->z;
+       }
+       return pos;
+}
+
+size_t facecache_count(u32 size)
+{
+       size_t len = 1 + size * 2;
+       return len * len * len;
+}
diff --git a/src/client/facecache.h b/src/client/facecache.h
new file mode 100644 (file)
index 0000000..847b739
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _FACECACHE_H_
+#define _FACECACHE_H_
+
+#include <pthread.h>
+#include "types.h"
+
+v3s32 facecache_face(size_t i, v3s32 *base);
+size_t facecache_count(u32 size);
+
+#endif
index f247034d73b67a3bfa2cb8074c6adf1849d8f202..13524b04f9946769d2e5b45987ff7d847b2a6847 100644 (file)
@@ -61,7 +61,6 @@ static void game_loop(Client *client)
 
                scene_render();
                hud_render();
-               // font_render();
 
                glfwSwapBuffers(window.handle);
                glfwPollEvents();
@@ -86,9 +85,9 @@ void game(Client *client)
        scene_on_resize(width, height);
 
        client_node_init();
-       client_map_start_meshgen();
+       client_map_start();
 
-       camera_set_position((v3f) {0.0f, 0.0f, 0.0f});
+       camera_set_position((v3f32) {0.0f, 0.0f, 0.0f});
        camera_set_angle(0.0f, 0.0f);
 
        hud_init();
@@ -113,7 +112,7 @@ void game(Client *client)
                .offset = {2, 2},
                .type_def = {
                        .text = {
-                               .text = "Dragonblocks Alpha",
+                               .text = "Dragonblocks Alpha",   // ToDo: add version
                                .color = {1.0f, 1.0f, 1.0f},
                        },
                },
@@ -125,6 +124,8 @@ void game(Client *client)
 
        game_loop(client);
 
+       client_map_stop();
+
        font_deinit();
        hud_deinit();
        scene_deinit();
index 6d646337189281996cfb751bd5e1baf0b0b6e8dc..61effb5d3ed165d58c5461ab519268f5b7c648e3 100644 (file)
@@ -125,7 +125,7 @@ void hud_deinit()
 
 static void element_transform(HUDElement *element)
 {
-       v3f pos = {
+       v3f32 pos = {
                (f32) element->def.offset.x + (1.0f + element->def.pos.x) / 2.0f * (f32) hud.width,
                (f32) element->def.offset.y + (1.0f + element->def.pos.y) / 2.0f * (f32) hud.height,
                element->def.pos.z,
@@ -134,7 +134,7 @@ static void element_transform(HUDElement *element)
        mat4x4_translate(element->transform, pos.x, pos.y, pos.z);
 
        if (element->def.type == HUD_IMAGE) {
-               v2f scale = element->def.type_def.image.scale;
+               v2f32 scale = element->def.type_def.image.scale;
 
                switch (element->def.type_def.image.scale_type) {
                        case HUD_SCALE_TEXTURE:
index 9224212da1f8e0c0fe88c732123d70effcd2b3a4..8f81eb93b05453bbb373e96df344c300e6a774af 100644 (file)
@@ -5,6 +5,7 @@
 #include <linmath.h/linmath.h>
 #include "client/font.h"
 #include "client/texture.h"
+#include "list.h"
 #include "types.h"
 
 typedef enum
@@ -23,18 +24,18 @@ typedef enum
 typedef struct
 {
        HUDElementType type;
-       v3f pos;
+       v3f32 pos;
        v2s32 offset;
        union
        {
                struct {
                        Texture *texture;
-                       v2f scale;
+                       v2f32 scale;
                        HUDImageScaleType scale_type;
                } image;
                struct {
                        char *text;
-                       v3f color;
+                       v3f32 color;
                } text;
        } type_def;
 } HUDElementDefinition;
index 08208867146993c6c1c12d83fa074d33f4718dae..59831830c05d1e7aa999687663b52fbc2a679730 100644 (file)
@@ -43,8 +43,8 @@ void input_on_cursor_pos(double current_x, double current_y)
 
 static bool move(int forward, int backward, vec3 dir)
 {
-       f32 sign;
-       f32 speed = 4.317f;
+       f64 sign;
+       f64 speed = 4.317f;
 
        if (glfwGetKey(window.handle, forward) == GLFW_PRESS)
                sign = +1.0f;
index bd26e1c3225355f8b7acd8093932246b8d2fcd1c..2f27897176b136c646d5f930b4f40c1db57c79c8 100644 (file)
@@ -33,9 +33,9 @@ static VertexLayout vertex_layout = {
 Object *object_create()
 {
        Object *obj = malloc(sizeof(Object));
-       obj->pos = (v3f) {0.0f, 0.0f, 0.0f};
-       obj->rot = (v3f) {0.0f, 0.0f, 0.0f};
-       obj->scale = (v3f) {1.0f, 1.0f, 1.0f};
+       obj->pos = (v3f32) {0.0f, 0.0f, 0.0f};
+       obj->rot = (v3f32) {0.0f, 0.0f, 0.0f};
+       obj->scale = (v3f32) {1.0f, 1.0f, 1.0f};
        obj->angle = 0.0f;
        obj->remove = false;
        obj->meshes = NULL;
index a09d6a8fb0046a352c75614ec026df6ba9a4214f..37221ead3f9db9065b9cf0862b9d46e84022617d 100644 (file)
@@ -39,7 +39,7 @@ typedef struct
 
 typedef struct
 {
-       v3f pos, rot, scale;
+       v3f32 pos, rot, scale;
        f32 angle;
        bool remove;
        Mesh **meshes;
index e19533e496ea2b88aa8c7f9011487ef19425cab4..4d6757871486496fd2ee393dd3422676cf8c6f52 100644 (file)
@@ -41,7 +41,7 @@ bool scene_init()
        return true;
 }
 
-static void list_delete_object(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *unused)
+static void list_delete_object(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *arg)
 {
        object_delete(key);
 }
index 0feb4f041fd1200f60645d5deb8e7ca52f53a7fa..b0c615cdc441d838ca8cae5e953c643726694db3 100644 (file)
@@ -13,7 +13,7 @@ __attribute((constructor(101))) static void textures_init()
        textures = list_create(&list_compare_string);
 }
 
-static void list_delete_texture(__attribute__((unused)) void *key, void *value, __attribute__((unused)) void *unused)
+static void list_delete_texture(__attribute__((unused)) void *key, void *value, __attribute__((unused)) void *arg)
 {
        texture_delete(value);
 }
index 4c73cc812962dae8390788ad380971ef90a18820..82af06420cc34a96f334f3fda261334fa70daf33 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -3,29 +3,36 @@
 #include <unistd.h>
 #include <math.h>
 #include <endian.h>
-#include <zlib.h>
+#include <string.h>
 #include "map.h"
 #include "util.h"
 
-Map *map_create()
+Map *map_create(MapCallbacks callbacks)
 {
        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;
+       map->callbacks = callbacks;
        return map;
 }
 
-static void free_block(void *block)
+static void free_block(void *value, void *arg)
 {
-       map_free_block(block);
+       Map *map = arg;
+
+       if (map->callbacks.delete_block)
+               map->callbacks.delete_block(value);
+
+       map_free_block(value);
 }
 
-static void free_sector(void *sector_void)
+static void free_sector(void *value, void *arg)
 {
-       MapSector *sector = sector_void;
-       bintree_clear(&sector->blocks, &free_block);
+       MapSector *sector = value;
+
+       bintree_clear(&sector->blocks, &free_block, arg);
        pthread_rwlock_destroy(&sector->rwlck);
        free(sector);
 }
@@ -34,7 +41,7 @@ void map_delete(Map *map)
 {
        pthread_rwlock_destroy(&map->rwlck);
        pthread_rwlock_destroy(&map->cached_rwlck);
-       bintree_clear(&map->sectors, &free_sector);
+       bintree_clear(&map->sectors, &free_sector, map);
        free(map);
 }
 
@@ -73,7 +80,7 @@ MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
        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)
+       if (cached && v3s32_equals(cached->pos, pos))
                return cached;
 
        MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
@@ -92,18 +99,21 @@ MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
        if (*nodeptr) {
                block = (*nodeptr)->value;
 
-               if (block->state < MBS_READY) {
-                       if (! create)
-                               block = NULL;
+               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 = map_allocate_block(pos);
+               bintree_add_node(&sector->blocks, nodeptr, &pos.y, block = map_allocate_block(pos));
 
-               bintree_add_node(&sector->blocks, nodeptr, &pos.y, block);
+               if (map->callbacks.create_block)
+                       map->callbacks.create_block(block);
        }
 
        pthread_rwlock_unlock(&sector->rwlck);
@@ -115,10 +125,11 @@ 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);
+       pthread_mutexattr_t attr;
+       pthread_mutexattr_init(&attr);
+       pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+       pthread_mutex_init(&block->mtx, &attr);
        return block;
 }
 
@@ -133,8 +144,6 @@ 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);
 }
 
@@ -155,10 +164,7 @@ bool map_deserialize_node(int fd, MapNode *node)
 
 void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr)
 {
-       size_t uncompressed_size = sizeof(MapBlockData);
-       char uncompressed_data[uncompressed_size];
-
-       MapBlockData blockdata;
+       MapBlockData uncompressed;
        ITERATE_MAPBLOCK {
                MapNode node = block->data[x][y][z];
 
@@ -166,71 +172,21 @@ void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr)
                        node_definitions[node.type].serialize(&node);
 
                node.type = htobe32(node.type);
-               blockdata[x][y][z] = node;
+               uncompressed[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 = htobe32(sizeof(MapBlockHeader) + compressed_size);
-       *(MapBlockHeader *) (data + sizeof(MapBlockHeader)) = htobe32(uncompressed_size);
-       memcpy(data + sizeof(MapBlockHeader) + sizeof(MapBlockHeader), compressed_data, compressed_size);
-
-       *sizeptr = size;
-       *dataptr = data;
+       my_compress(&uncompressed, sizeof(MapBlockData), dataptr, sizeptr);
 }
 
 bool map_deserialize_block(MapBlock *block, const char *data, size_t size)
 {
-       if (size < sizeof(MapBlockHeader))
-               return false;
+       MapBlockData decompressed;
 
-       MapBlockHeader uncompressed_size = be32toh(*(MapBlockHeader *) data);
-
-       if (uncompressed_size < sizeof(MapBlockData))
+       if (! my_decompress(data, size, &decompressed, sizeof(MapBlockData)))
                return false;
 
-       char decompressed_data[uncompressed_size];
-
-       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 {
-               MapNode node = blockdata[x][y][z];
+               MapNode node = decompressed[x][y][z];
                node.type = be32toh(node.type);
 
                if (node.type >= NODE_UNLOADED)
@@ -240,20 +196,17 @@ bool map_deserialize_block(MapBlock *block, const char *data, size_t size)
                        node_definitions[node.type].deserialize(&node);
 
                block->data[x][y][z] = node;
-
                block->metadata[x][y][z] = list_create(&list_compare_string);
        }
 
-       block->state = MBS_MODIFIED;
-
        return true;
 }
 
 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)
@@ -266,15 +219,18 @@ MapNode map_get_node(Map *map, v3s32 pos)
        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) {
                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;
+               if (! map->callbacks.set_node || map->callbacks.set_node(block, offset, &node, arg)) {
+                       if (map->callbacks.after_set_node)
+                               map->callbacks.after_set_node(block, offset, arg);
+                       list_clear(&block->metadata[offset.x][offset.y][offset.z]);
+                       block->data[offset.x][offset.y][offset.z] = node;
+               }
                pthread_mutex_unlock(&block->mtx);
        }
 }
index 5de242b68619d8d631989c09ff5b59b4071c7690..2cb3613a0f815fee36d7c3f4d7bc3b4dbcfd1afe 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -8,7 +8,8 @@
 #include "node.h"
 #include "types.h"
 
-#define ITERATE_MAPBLOCK for (u8 x = 0; x < 16; x++) for (u8 y = 0; y < 16; y++) for (u8 z = 0; z < 16; z++)
+#define MAPBLOCK_SIZE 16
+#define ITERATE_MAPBLOCK for (u8 x = 0; x < MAPBLOCK_SIZE; x++) for (u8 y = 0; y < MAPBLOCK_SIZE; y++) for (u8 z = 0; z < MAPBLOCK_SIZE; z++)
 
 typedef struct MapNode
 {
@@ -16,27 +17,15 @@ typedef struct MapNode
        NodeState state;
 } MapNode;
 
-typedef enum
-{
-       MBS_CREATED,
-       MBS_READY,
-       MBS_MODIFIED,
-       MBS_PROCESSING,
-} MapBlockState;
-
-typedef MapNode MapBlockData[16][16][16];
-
-typedef u32 MapBlockHeader;
+typedef MapNode MapBlockData[MAPBLOCK_SIZE][MAPBLOCK_SIZE][MAPBLOCK_SIZE];
 
 typedef struct
 {
        MapBlockData data;
-       List metadata[16][16][16];
+       List metadata[MAPBLOCK_SIZE][MAPBLOCK_SIZE][MAPBLOCK_SIZE];
        v3s32 pos;
-       MapBlockState state;
        pthread_mutex_t mtx;
        void *extra;
-       void (*free_extra)(void *ptr);
 } MapBlock;
 
 typedef struct
@@ -44,18 +33,27 @@ typedef struct
        pthread_rwlock_t rwlck;
        Bintree blocks;
        v2s32 pos;
-       u64 hash;
 } MapSector;
 
+typedef struct
+{
+       void (*create_block)(MapBlock *block);
+       void (*delete_block)(MapBlock *block);
+       bool (*get_block)(MapBlock *block, bool create);
+       bool (*set_node) (MapBlock *block, v3u8 offset, MapNode *node, void *arg);
+       void (*after_set_node)(MapBlock *block, v3u8 offset, void *arg);
+} MapCallbacks;
+
 typedef struct
 {
        pthread_rwlock_t rwlck;
        Bintree sectors;
        pthread_rwlock_t cached_rwlck;
        MapBlock *cached;
+       MapCallbacks callbacks;
 } Map;
 
-Map *map_create();
+Map *map_create(MapCallbacks callbacks);
 void map_delete(Map *map);
 
 MapSector *map_get_sector(Map *map, v2s32 pos, bool create);
@@ -72,7 +70,7 @@ bool map_deserialize_block(MapBlock *block, const char *data, size_t size);
 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset);
 
 MapNode map_get_node(Map *map, v3s32 pos);
-void map_set_node(Map *map, v3s32 pos, MapNode node);
+void map_set_node(Map *map, v3s32 pos, MapNode node, bool create, void *arg);
 MapNode map_node_create(Node type);
 void map_node_clear(MapNode *node);
 
index 92b2bb1ab3d832ac1077559b2ba893525009e93a..7bdc8b4035d2d872a3f7ae44618e61f27862cc44 100644 (file)
@@ -4,7 +4,7 @@
 
 static void create_state_biome(MapNode *node)
 {
-       node->state.biome = (v3f) {1.0f, 0.0f, 1.0f};
+       node->state.biome = (v3f32) {1.0f, 0.0f, 1.0f};
 }
 
 NodeDefintion node_definitions[NODE_UNLOADED] = {
index 8ef9108810c0fd79bcfe32b49e96ecebe3544041..e9f67ed4cf75ef1f56bfc022e0b4b2d2f51f85e6 100644 (file)
@@ -4,7 +4,7 @@
 #include <stdbool.h>
 #include "types.h"
 
-typedef v3f NodeStateBiome;
+typedef v3f32 NodeStateBiome;
 
 typedef union
 {
diff --git a/src/server/facecache.c b/src/server/facecache.c
deleted file mode 100644 (file)
index c15e878..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#include <stdlib.h>
-#include "server/facecache.h"
-#include "array.h"
-
-static struct
-{
-       Array positions;
-       u32 size;
-       pthread_mutex_t mtx;
-} facecache;
-
-__attribute((constructor)) static void face_cache_init()
-{
-       facecache.size = 0;
-       facecache.positions = array_create(sizeof(v3s32));
-       v3s32 pos = {0, 0, 0};
-       array_append(&facecache.positions, &pos);
-       pthread_mutex_init(&facecache.mtx, NULL);
-}
-
-__attribute((destructor)) void face_cache_deinit()
-{
-       if (facecache.positions.ptr)
-               free(facecache.positions.ptr);
-       pthread_mutex_destroy(&facecache.mtx);
-}
-
-static void face_cache_calculate(s32 size)
-{
-#define ADDPOS(a, b, c, va, vb, vc) \
-       { \
-               v3s32 pos; \
-               *(s32 *) ((char *) &pos + offsetof(v3s32, a)) = va; \
-               *(s32 *) ((char *) &pos + offsetof(v3s32, b)) = vb; \
-               *(s32 *) ((char *) &pos + offsetof(v3s32, c)) = vc; \
-               array_append(&facecache.positions, &pos); \
-       }
-#define SQUARES(a, b, c) \
-       for (s32 va = -size + 1; va < size; va++) { \
-               for (s32 vb = -size + 1; vb < size; vb++) { \
-                       ADDPOS(a, b, c, va, vb,  size) \
-                       ADDPOS(a, b, c, va, vb, -size) \
-               } \
-       }
-       SQUARES(x, z, y)
-       SQUARES(x, y, z)
-       SQUARES(z, y, x)
-#undef SQUARES
-#define EDGES(a, b, c) \
-       for (s32 va = -size + 1; va < size; va++) { \
-               ADDPOS(a, b, c, va,  size,  size) \
-               ADDPOS(a, b, c, va,  size, -size) \
-               ADDPOS(a, b, c, va, -size,  size) \
-               ADDPOS(a, b, c, va, -size, -size) \
-       }
-       EDGES(x, y, z)
-       EDGES(z, x, y)
-       EDGES(y, x, z)
-#undef EDGES
-       ADDPOS(x, y, z,  size,  size,  size)
-       ADDPOS(x, y, z,  size,  size, -size)
-       ADDPOS(x, y, z,  size, -size,  size)
-       ADDPOS(x, y, z,  size, -size, -size)
-       ADDPOS(x, y, z, -size,  size,  size)
-       ADDPOS(x, y, z, -size,  size, -size)
-       ADDPOS(x, y, z, -size, -size,  size)
-       ADDPOS(x, y, z, -size, -size, -size)
-#undef ADDPOS
-}
-
-v3s32 facecache_face(size_t i, v3s32 *base)
-{
-       pthread_mutex_lock(&facecache.mtx);
-       while (facecache.positions.siz <= i)
-               face_cache_calculate(++facecache.size);
-       v3s32 pos = ((v3s32 *) facecache.positions.ptr)[i];
-       pthread_mutex_unlock(&facecache.mtx);
-       if (base) {
-               pos.x += base->x;
-               pos.y += base->y;
-               pos.z += base->z;
-       }
-       return pos;
-}
-
-size_t facecache_count(u32 size)
-{
-       size_t len = 1 + size * 2;
-       return len * len * len;
-}
diff --git a/src/server/facecache.h b/src/server/facecache.h
deleted file mode 100644 (file)
index 847b739..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef _FACECACHE_H_
-#define _FACECACHE_H_
-
-#include <pthread.h>
-#include "types.h"
-
-v3s32 facecache_face(size_t i, v3s32 *base);
-size_t facecache_count(u32 size);
-
-#endif
index fc0bf428e504643b81c6f63372497e81b47f0dda..7f30eea144d75c8b8c1ab202d891a5a6d9c93a20 100644 (file)
@@ -1,29 +1,21 @@
 #include <stdio.h>
 #include <endian.h>
 #include <stdlib.h>
+#include <string.h>
+#include <assert.h>
 #include "server/mapdb.h"
 #include "server/server_map.h"
+#include "util.h"
 
+// utility functions
+
+// print SQLite3 error message for failed block SQL statement
 static void print_error(sqlite3 *db, MapBlock *block, const char *action)
 {
-       printf("Database error with %s block at %d %d %d: %s\n", action, block->pos.x, block->pos.y, block->pos.z, sqlite3_errmsg(db));
-}
-
-sqlite3 *mapdb_open(const char *path)
-{
-       sqlite3 *db;
-       char *err;
-
-       if (sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
-               printf("Failed to open database: %s\n", sqlite3_errmsg(db));
-       } else if (sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS blocks (pos BLOB PRIMARY KEY, data BLOB NOT NULL);", NULL, NULL, &err) != SQLITE_OK) {
-               printf("Failed to initialize database: %s\n", err);
-               sqlite3_free(err);
-       }
-
-       return db;
+       printf("Database error with %s block at (%d, %d, %d): %s\n", action, block->pos.x, block->pos.y, block->pos.z, sqlite3_errmsg(db));
 }
 
+// prepare a SQLite3 block statement and bind the position
 static sqlite3_stmt *prepare_statement(sqlite3 *db, MapBlock *block, const char *action, const char *sql)
 {
        sqlite3_stmt *stmt;
@@ -44,19 +36,50 @@ static sqlite3_stmt *prepare_statement(sqlite3 *db, MapBlock *block, const char
        return stmt;
 }
 
+// public functions
+
+// open and initialize SQLite3 database at path
+sqlite3 *mapdb_open(const char *path)
+{
+       sqlite3 *db;
+       char *err;
+
+       if (sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
+               printf("Failed to open database: %s\n", sqlite3_errmsg(db));
+       } else if (sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS blocks (pos BLOB PRIMARY KEY, generated INT, size INT, data BLOB, mgsb_size INT, mgsb_data BLOB);", NULL, NULL, &err) != SQLITE_OK) {
+               printf("Failed to initialize database: %s\n", err);
+               sqlite3_free(err);
+       }
+
+       return db;
+}
+
+// load a block from map database (initializes state, mgs buffer and data), returns false on failure
 bool mapdb_load_block(sqlite3 *db, MapBlock *block)
 {
        sqlite3_stmt *stmt;
 
-       if (! (stmt = prepare_statement(db, block, "loading", "SELECT data FROM blocks WHERE pos=?")))
+       if (! (stmt = prepare_statement(db, block, "loading", "SELECT generated, size, data, mgsb_size, mgsb_data FROM blocks WHERE pos=?")))
                return false;
 
        int rc = sqlite3_step(stmt);
        bool found = rc == SQLITE_ROW;
 
        if (found) {
-               const char *data = sqlite3_column_blob(stmt, 0);
-               map_deserialize_block(block, data + sizeof(MapBlockHeader), be32toh(*(MapBlockHeader *) data));
+               MapBlockExtraData *extra = block->extra;
+
+               extra->state = sqlite3_column_int(stmt, 0) ? MBS_READY : MBS_CREATED;
+               extra->size = sqlite3_column_int64(stmt, 1);
+               extra->data = malloc(extra->size);
+               memcpy(extra->data, sqlite3_column_blob(stmt, 2), extra->size);
+
+               MapgenStageBuffer decompressed_mgsb;
+               my_decompress(sqlite3_column_blob(stmt, 4), sqlite3_column_int64(stmt, 3), &decompressed_mgsb, sizeof(MapgenStageBuffer));
+
+               ITERATE_MAPBLOCK extra->mgs_buffer[x][y][z] = be32toh(decompressed_mgsb[x][y][z]);
+
+               if (! map_deserialize_block(block, extra->data, extra->size))
+                       printf("Error with deserializing block at (%d, %d, %d)\n", block->pos.x, block->pos.y, block->pos.z);
        } else if (rc != SQLITE_DONE) {
                print_error(db, block, "loading");
        }
@@ -65,16 +88,29 @@ bool mapdb_load_block(sqlite3 *db, MapBlock *block)
        return found;
 }
 
+// save a block to database
 void mapdb_save_block(sqlite3 *db, MapBlock *block)
 {
        sqlite3_stmt *stmt;
 
-       if (! (stmt = prepare_statement(db, block, "saving", "REPLACE INTO blocks (pos, data) VALUES(?1, ?2)")))
+       if (! (stmt = prepare_statement(db, block, "saving", "REPLACE INTO blocks (pos, generated, size, data, mgsb_size, mgsb_data) VALUES(?1, ?2, ?3, ?4, ?5, ?6)")))
                return;
 
        MapBlockExtraData *extra = block->extra;
 
-       sqlite3_bind_blob(stmt, 2, extra->data, extra->size, SQLITE_TRANSIENT);
+       MapgenStageBuffer uncompressed_mgsb;
+       ITERATE_MAPBLOCK uncompressed_mgsb[x][y][z] = htobe32(extra->mgs_buffer[x][y][z]);
+
+       char *mgsb_data;
+       size_t mgsb_size;
+
+       my_compress(&uncompressed_mgsb, sizeof(MapgenStageBuffer), &mgsb_data, &mgsb_size);
+
+       sqlite3_bind_int(stmt, 2, extra->state > MBS_CREATED);
+       sqlite3_bind_int64(stmt, 3, extra->size);
+       sqlite3_bind_blob(stmt, 4, extra->data, extra->size, SQLITE_TRANSIENT);
+       sqlite3_bind_int64(stmt, 5, mgsb_size);
+       sqlite3_bind_blob(stmt, 6, mgsb_data, mgsb_size, &free);
 
        if (sqlite3_step(stmt) != SQLITE_DONE)
                print_error(db, block, "saving");
index 41187ba00da025dc4fb123158f407d80a8103a3c..29633032d073103809bb7234da170d08b67cfd5f 100644 (file)
@@ -5,8 +5,8 @@
 #include <stdbool.h>
 #include "map.h"
 
-sqlite3 *mapdb_open(const char *path);
-bool mapdb_load_block(sqlite3 *db, MapBlock *block);
-void mapdb_save_block(sqlite3 *db, MapBlock *block);
+sqlite3 *mapdb_open(const char *path);                                 // open and initialize SQLite3 database at path
+bool mapdb_load_block(sqlite3 *db, MapBlock *block);   // load a block from map database (initializes state, mgs buffer and data), returns false on failure
+void mapdb_save_block(sqlite3 *db, MapBlock *block);   // save a block to database
 
 #endif
index c2145553318c4c3556447accae8b53b0c3aacf22..e21a68c82f7936f8e31b3e9a3493110c5308323f 100644 (file)
@@ -1,13 +1,29 @@
+#include <stdio.h>
 #include <math.h>
 #include "server/mapgen.h"
 #include "server/perlin.h"
+#include "server/server_map.h"
 
-void mapgen_generate_block(MapBlock *block)
+static void set_node(v3s32 pos, MapNode node, MapgenStage mgs, List *changed_blocks)
 {
-       for (u8 x = 0; x < 16; x++) {
-               u32 ux = x + block->pos.x * 16 + ((u32) 1 << 31);
-               for (u8 z = 0; z < 16; z++) {
-                       u32 uz = z + block->pos.z * 16 + ((u32) 1 << 31);
+       MapgenSetNodeArg arg = {
+               .mgs = mgs,
+               .changed_blocks = changed_blocks,
+       };
+       map_set_node(server_map.map, pos, node, true, &arg);
+}
+
+// generate a block (does not manage block state or threading)
+void mapgen_generate_block(MapBlock *block, List *changed_blocks)
+{
+       printf("Generating block at (%d, %d, %d)\n", block->pos.x, block->pos.y, block->pos.z);
+
+       MapBlockExtraData *extra = block->extra;
+
+       for (u8 x = 0; x < MAPBLOCK_SIZE; x++) {
+               u32 ux = x + block->pos.x * MAPBLOCK_SIZE + ((u32) 1 << 31);
+               for (u8 z = 0; z < MAPBLOCK_SIZE; z++) {
+                       u32 uz = z + block->pos.z * MAPBLOCK_SIZE + ((u32) 1 << 31);
                        s32 height = smooth2d(ux / 32.0, uz / 32.0, 0, 0) * 16.0 + 128.0;
                        bool is_mountain = false;
 
@@ -18,8 +34,8 @@ void mapgen_generate_block(MapBlock *block)
                                is_mountain = true;
                        }
 
-                       for (u8 y = 0; y < 16; y++) {
-                               s32 ay = block->pos.y * 16 + y;
+                       for (u8 y = 0; y < MAPBLOCK_SIZE; y++) {
+                               s32 ay = block->pos.y * MAPBLOCK_SIZE + y;
                                s32 diff = ay - height;
 
                                Node node = NODE_AIR;
@@ -33,16 +49,32 @@ void mapgen_generate_block(MapBlock *block)
                                else if (diff < 1)
                                        node = (is_mountain && ay > 256) ? NODE_SNOW : NODE_AIR;
 
-                               block->data[x][y][z] = map_node_create(node);
-                               block->metadata[x][y][z] = list_create(&list_compare_string);
+                               if (! is_mountain && diff == 0 && (smooth2d(x + block->pos.x * 16, z + block->pos.z * 16, 0, 3) * 0.5 + 0.5) < 0.01f) {
+                                       for (s8 bx = -1; bx <= 1; bx++) {
+                                               for (s8 by = -1; by <= 1; by++) {
+                                                       for (s8 bz = -1; bz <= 1; bz++) {
+                                                               v3s32 pos = {block->pos.x * MAPBLOCK_SIZE + x + bx, block->pos.y * MAPBLOCK_SIZE + y + by, block->pos.z * MAPBLOCK_SIZE + z + bz};
+                                                               if (smooth3d(pos.x, pos.y, pos.z, 0, 4) > 0.0)
+                                                                       set_node(pos, map_node_create(NODE_STONE), MGS_BOULDERS, changed_blocks);
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               pthread_mutex_lock(&block->mtx);
+                               if (extra->mgs_buffer[x][y][z] <= MGS_TERRAIN) {
+                                       block->data[x][y][z] = map_node_create(node);
+                                       extra->mgs_buffer[x][y][z] = MGS_TERRAIN;
 
-                               if (node == NODE_GRASS) {
-                                       double min, max;
-                                       min = 0.15;
-                                       max = 0.45;
-                                       block->data[x][y][z].state.biome.x = (smooth2d(ux / 128.0, uz / 128.0, 0, 3) * 0.5 + 0.5) * (max - min) + min;
-                                       block->data[x][y][z].state.biome.y = 1.0;
+                                       if (node == NODE_GRASS) {
+                                               double min, max;
+                                               min = 0.15;
+                                               max = 0.45;
+                                               block->data[x][y][z].state.biome.x = (smooth2d(ux / 128.0, uz / 128.0, 0, 3) * 0.5 + 0.5) * (max - min) + min;
+                                               block->data[x][y][z].state.biome.y = 1.0;
+                                       }
                                }
+                               pthread_mutex_unlock(&block->mtx);
                        }
                }
        }
index 8f1b20de06d81d2503ae70749d744a914e737db9..1b425a0a16b7f7c6ad42f4f0f980f22d108f775c 100644 (file)
@@ -3,6 +3,6 @@
 
 #include "map.h"
 
-void mapgen_generate_block(MapBlock *block);
+void mapgen_generate_block(MapBlock *block, List *changed_blocks);     // generate a block (does not manage block state or threading)
 
 #endif
index c6d415c7988ade0eeabfdf6cd041aeb9ab2d73c6..f9654e01ce5f89f212335804157f26ea2ce5559a 100644 (file)
@@ -1,2 +1,3 @@
-#include <perlin/perlin.c>
+// include perlin submodule implementation
 
+#include <perlin/perlin.c>
index b328f614a2b260209be7abe3cec110dcf908e84c..99cec7b0bfd2ec0cdddf912c546631d5b4c549dc 100644 (file)
@@ -1,6 +1,6 @@
 #ifndef _PERLIN_H_
 #define _PERLIN_H_
 
-#include <perlin/perlin.h>
+#include <perlin/perlin.h> // include perlin submodule header file
 
 #endif
index 70ba1c03e619435d0cec724a54c566d01d2dc107..a74b86272f54eba2c301b974cade83425c5fd036 100644 (file)
@@ -8,49 +8,18 @@
 #include "signal_handlers.h"
 #include "util.h"
 
-Server server;
+static Server server;
 
-void server_disconnect_client(Client *client, int flags, const char *detail)
-{
-       client->state = CS_DISCONNECTED;
-
-       if (! (flags & DISCO_NO_REMOVE)) {
-               if (client->name) {
-                       pthread_rwlock_wrlock(&server.players_rwlck);
-                       list_delete(&server.players, client->name);
-                       pthread_rwlock_unlock(&server.players_rwlck);
-               }
-               pthread_rwlock_wrlock(&server.clients_rwlck);
-               list_delete(&server.clients, client);
-               pthread_rwlock_unlock(&server.clients_rwlck);
-       }
-
-       if (! (flags & DISCO_NO_MESSAGE))
-               printf("Disconnected %s %s%s%s\n", client->name, INBRACES(detail));
-
-       if (! (flags & DISCO_NO_SEND))
-               send_command(client, CC_DISCONNECT);
-
-       pthread_mutex_lock(&client->mtx);
-       close(client->fd);
-       pthread_mutex_unlock(&client->mtx);
-
-       if (! (flags & DISCO_NO_JOIN))
-               pthread_join(client->net_thread, NULL);
-
-       if (client->name != client->address)
-               free(client->name);
-       free(client->address);
-
-       pthread_mutex_destroy(&client->mtx);
-       free(client);
-}
+// include handle_packets implementation
 
 #include "network.c"
 
-static void *server_reciever_thread(void *cli)
+// utility functions
+
+// pthread start routine for reciever thread
+static void *reciever_thread(void *arg)
 {
-       Client *client = cli;
+       Client *client = arg;
 
        if (! handle_packets(client))
                server_disconnect_client(client, DISCO_NO_SEND | DISCO_NO_JOIN, "network error");
@@ -58,7 +27,8 @@ static void *server_reciever_thread(void *cli)
        return NULL;
 }
 
-static void server_accept_client()
+// accept a new connection, initialize client and start reciever thread
+static void accept_client()
 {
        struct sockaddr_storage client_address = {0};
        socklen_t client_addrlen = sizeof(client_address);
@@ -78,9 +48,8 @@ static void server_accept_client()
        client->address = address_string((struct sockaddr_in6 *) &client_address);
        client->name = client->address;
        client->server = &server;
-       client->pos = (v3f) {0.0f, 0.0f, 0.0f};
-       pthread_create(&client->net_thread, NULL, &server_reciever_thread, client);
-       client->sent_blocks = list_create(NULL);
+       client->pos = (v3f64) {0.0f, 0.0f, 0.0f};
+       pthread_create(&client->net_thread, NULL, &reciever_thread, client);
 
        pthread_rwlock_wrlock(&server.clients_rwlck);
        list_put(&server.clients, client, NULL);
@@ -89,15 +58,18 @@ static void server_accept_client()
        printf("Connected %s\n", client->address);
 }
 
-static void list_disconnect_client(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *unused)
+// list_clear_func callback used on server shutdown to disconnect all clients properly
+static void list_disconnect_client(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *arg)
 {
        server_disconnect_client(key, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
 }
 
-void server_start(int fd)
+// start up the server after socket was created, then accept connections until interrupted, then shutdown server
+static void server_run(int fd)
 {
+       server.config.simulation_distance = 16;
+
        server.sockfd = fd;
-       server.map = map_create(NULL);
        pthread_rwlock_init(&server.clients_rwlck, NULL);
        server.clients = list_create(NULL);
        pthread_rwlock_init(&server.players_rwlck, NULL);
@@ -106,15 +78,14 @@ void server_start(int fd)
        server_map_init(&server);
 
        while (! interrupted)
-               server_accept_client();
+               accept_client();
 
        printf("Shutting down\n");
 
-       server_map_deinit();
-
        pthread_rwlock_wrlock(&server.clients_rwlck);
        list_clear_func(&server.clients, &list_disconnect_client, NULL);
        pthread_rwlock_unlock(&server.clients_rwlck);
+
        pthread_rwlock_wrlock(&server.players_rwlck);
        list_clear(&server.players);
        pthread_rwlock_unlock(&server.players_rwlck);
@@ -125,11 +96,51 @@ void server_start(int fd)
        shutdown(server.sockfd, SHUT_RDWR);
        close(server.sockfd);
 
-       map_delete(server.map);
+       server_map_deinit();
 
        exit(EXIT_SUCCESS);
 }
 
+// public functions
+
+// disconnect a client with various options an an optional detail message (flags: DiscoFlag bitmask)
+void server_disconnect_client(Client *client, int flags, const char *detail)
+{
+       client->state = CS_DISCONNECTED;
+
+       if (! (flags & DISCO_NO_REMOVE)) {
+               if (client->name) {
+                       pthread_rwlock_wrlock(&server.players_rwlck);
+                       list_delete(&server.players, client->name);
+                       pthread_rwlock_unlock(&server.players_rwlck);
+               }
+               pthread_rwlock_wrlock(&server.clients_rwlck);
+               list_delete(&server.clients, client);
+               pthread_rwlock_unlock(&server.clients_rwlck);
+       }
+
+       if (! (flags & DISCO_NO_MESSAGE))
+               printf("Disconnected %s %s%s%s\n", client->name, INBRACES(detail));
+
+       if (! (flags & DISCO_NO_SEND))
+               send_command(client, CC_DISCONNECT);
+
+       pthread_mutex_lock(&client->mtx);
+       close(client->fd);
+       pthread_mutex_unlock(&client->mtx);
+
+       if (! (flags & DISCO_NO_JOIN))
+               pthread_join(client->net_thread, NULL);
+
+       if (client->name != client->address)
+               free(client->name);
+       free(client->address);
+
+       pthread_mutex_destroy(&client->mtx);
+       free(client);
+}
+
+// server entry point
 int main(int argc, char **argv)
 {
        program_name = argv[0];
@@ -174,7 +185,7 @@ int main(int argc, char **argv)
        freeaddrinfo(info);
 
        signal_handlers_init();
-       server_start(fd);
+       server_run(fd);
 
        return EXIT_SUCCESS;
 }
index 89b91cc441bf88cc5bb48d35a28164638c5278ab..9190d0945ae06abedf21f350a0231d8e46278814 100644 (file)
@@ -6,41 +6,41 @@
 #include "client/client_commands.h"
 #include "server/server_commands.h"
 #include "list.h"
-#include "map.h"
 #include "network.h"
+#include "types.h"
 
 typedef struct
 {
-       int sockfd;
-       pthread_rwlock_t clients_rwlck;
-       List clients;
-       pthread_rwlock_t players_rwlck;
-       List players;
-       Map *map;
+       int sockfd;                                             // TCP socket to accept new connections
+       pthread_rwlock_t clients_rwlck; // lock to protect client list
+       List clients;                                   // Client * -> NULL map with all connected clients
+       pthread_rwlock_t players_rwlck; // lock to protect player list
+       List players;                                   // char * -> Client * map with clients that have finished auth
+       struct {
+               u32 simulation_distance;        // perimeter of the cube that players can see blocks in is simulation_distance * 2 + 1
+       } config;                                               // configuration, ToDo: read from config file
 } Server;
 
 typedef struct Client
 {
-       int fd;
-       pthread_mutex_t mtx;
-       ClientState state;
-       char *address;
-       char *name;
-       Server *server;
-       pthread_t net_thread;
-       List sent_blocks;
-       v3f pos;
+       int fd;                                         // TCP socket for connection
+       pthread_mutex_t mtx;            // mutex to protect socket
+       ClientState state;                      // state of the client (created, auth, active, disconnected)
+       char *address;                          // address string to use as identifier for log messages until auth is completed
+       char *name;                                     // player name (must be unique)
+       Server *server;                         // pointer to server object (essentially the same for all clients)
+       pthread_t net_thread;           // reciever thread ID
+       v3f64 pos;                                      // player position
 } Client;
 
 typedef enum
 {
-       DISCO_NO_REMOVE = 0x01,
-       DISCO_NO_SEND = 0x02,
-       DISCO_NO_MESSAGE = 0x04,
-       DISCO_NO_JOIN = 0x08,
+       DISCO_NO_REMOVE = 0x01,         // don't remove from client and player list (to save extra work on server shutdown)
+       DISCO_NO_SEND = 0x02,           // don't notfiy client about the disconnect (if client sent disconnect themselves or the TCP connection died)
+       DISCO_NO_MESSAGE = 0x04,        // don't log a message about the disconnect (used on server shutdown)
+       DISCO_NO_JOIN = 0x08,           // don't wait for the reciever thread to finish (if TCP connection death was reported by reciever thread, the thread is already dead)
 } DiscoFlag;
 
-void server_disconnect_client(Client *client, int flags, const char *detail);
-void server_shutdown();
+void server_disconnect_client(Client *client, int flags, const char *detail);  // disconnect a client with various options an an optional detail message (flags: DiscoFlag bitmask)
 
 #endif
index f0dafa94a0da8e4b1e5a7ff74d3323584cae6722..14254889909a70a59f33981292540aa5a0235d8c 100644 (file)
@@ -4,6 +4,9 @@
 #include "server/server_map.h"
 #include "util.h"
 
+// command callbacks
+
+// disconnect client without sending a packet (client won't recieve it)
 static bool disconnect_handler(Client *client, bool good)
 {
        if (good)
@@ -11,6 +14,7 @@ static bool disconnect_handler(Client *client, bool good)
        return true;
 }
 
+// insert client into player list and update state (if checks pass)
 static bool auth_handler(Client *client, bool good)
 {
        char *name = read_string(client->fd, PLAYER_NAME_MAX);
@@ -35,6 +39,8 @@ static bool auth_handler(Client *client, bool good)
 
        pthread_mutex_lock(&client->mtx);
        bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success);
+       if (ret && success)
+               ret = ret && write_u32(client->fd, CC_SIMULATION_DISTANCE) && write_u32(client->fd, client->server->config.simulation_distance);
        pthread_mutex_unlock(&client->mtx);
 
        pthread_rwlock_unlock(&client->server->players_rwlck);
@@ -44,6 +50,7 @@ static bool auth_handler(Client *client, bool good)
        return ret;
 }
 
+// set a node on the map
 static bool setnode_handler(Client *client, bool good)
 {
        v3s32 pos;
@@ -57,16 +64,17 @@ static bool setnode_handler(Client *client, bool good)
                return false;
 
        if (good)
-               map_set_node(client->server->map, pos, node);
+               map_set_node(server_map.map, pos, node, false, NULL);
 
        return true;
 }
 
+// update player's position
 static bool pos_handler(Client *client, bool good)
 {
-       v3f pos;
+       v3f64 pos;
 
-       if (! read_v3f32(client->fd, &pos))
+       if (! read_v3f64(client->fd, &pos))
                return false;
 
        if (good)
@@ -75,10 +83,26 @@ static bool pos_handler(Client *client, bool good)
        return true;
 }
 
+// tell server map manager client requested the block
+static bool request_block_handler(Client *client, bool good)
+{
+       v3s32 pos;
+
+       if (! read_v3s32(client->fd, &pos))
+               return false;
+
+       if (good)
+               server_map_requested_block(client, pos);
+
+       return true;
+}
+
+// declared in network.h
 CommandHandler command_handlers[SERVER_COMMAND_COUNT] = {
        {0},
        {&disconnect_handler, "DISCONNECT", CS_CREATED | CS_ACTIVE},
        {&auth_handler, "AUTH", CS_CREATED},
        {&setnode_handler, "SETNODE", CS_ACTIVE},
        {&pos_handler, "POS", CS_ACTIVE},
+       {&request_block_handler, "REQUEST_BLOCK", CS_ACTIVE},
 };
index ba782783517c587a6389d75f5c7676c13b224a0f..35025d5ff690adde274b2b5f1292b3199600184a 100644 (file)
@@ -1,14 +1,17 @@
 #ifndef _SERVER_COMMANDS_H_
-#define _SCOMMANDS_H_
+#define _SERVER_COMMANDS_H_
+
+// this file must be included after client.h or server.h and before network.h
 
 typedef enum
 {
-       SERVER_COMMAND_NULL,
-       SC_DISCONNECT,
-       SC_AUTH,
-       SC_SETNODE,
-       SC_POS,
-       SERVER_COMMAND_COUNT,
+       SERVER_COMMAND_NULL,    // invalid command
+       SC_DISCONNECT,                  // client notifies server about disconnecting
+       SC_AUTH,                                // client wants to authentify [body: name (zero terminated string)]
+       SC_SETNODE,                             // player placed a node [body: pos (v3s32), node (MapNode)]
+       SC_POS,                                 // player moved [body: pos (v3f)]
+       SC_REQUEST_BLOCK,               // request to send a block [body: pos (v3s32)]
+       SERVER_COMMAND_COUNT,   // count of available commands
 } ServerCommand;
 
 #ifdef _CLIENT_H_
index f0494ed303f680bda8b2417578a29db812417847..f9b65214164eef35bb7a617fe2e66a58b721d156 100644 (file)
 #include <string.h>
 #include <unistd.h>
 #include <stdio.h>
-#include "server/facecache.h"
 #include "server/mapdb.h"
 #include "server/mapgen.h"
 #include "server/server_map.h"
 #include "map.h"
+#include "util.h"
 
-static struct {
-       Server *server;
-       size_t max_blocks;
-       pthread_t thread;
-       sqlite3 *db;
-       bool cancel;
-} server_map;
+struct ServerMap server_map;
+static Server *server;
 
-static void initialize_block(MapBlock *block)
-{
-       if (! mapdb_load_block(server_map.db, block))
-               mapgen_generate_block(block);
-       block->state = MBS_MODIFIED;
-}
+// utility functions
 
-static void reset_client_block(void *key, __attribute__((unused)) void *value, void *block)
+// send a block to a client and reset block request
+static void send_block(Client *client, MapBlock *block)
 {
-       Client *client = list_get(&server_map.server->players, key);
-       if (client)
-               list_delete(&client->sent_blocks, block);
-       free(key);
+       MapBlockExtraData *extra = block->extra;
+
+       pthread_mutex_lock(&client->mtx);
+       if (client->state == CS_ACTIVE)
+               (void) (write_u32(client->fd, CC_BLOCK) && write_v3s32(client->fd, block->pos) && write_u64(client->fd, extra->size) && write(client->fd, extra->data, extra->size) != -1);
+       pthread_mutex_unlock(&client->mtx);
 }
 
-static void list_delete_extra_data(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *unused)
+// send block to near clients
+// block mutex has to be locked
+static void send_block_to_near(MapBlock *block)
 {
-       free(key);
+       MapBlockExtraData *extra = block->extra;
+
+       if (extra->state == MBS_GENERATING)
+               return;
+
+       map_serialize_block(block, &extra->data, &extra->size);
+       mapdb_save_block(server_map.db, block);
+
+       if (extra->state == MBS_CREATED)
+               return;
+
+       pthread_rwlock_rdlock(&server->players_rwlck);
+       ITERATE_LIST(&server->players, pair) {
+               Client *client = pair->value;
+
+               if (within_simulation_distance(client->pos, block->pos, server->config.simulation_distance))
+                       send_block(client, block);
+       }
+       pthread_rwlock_unlock(&server->players_rwlck);
 }
 
-static void free_extra_data(void *ext)
+// list_clear_func callback for sending changed blocks to near clients
+static void list_send_block(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *arg)
 {
-       MapBlockExtraData *extra = ext;
+       MapBlock *block = key;
 
-       if (extra) {
-               list_clear_func(&extra->clients, &list_delete_extra_data, NULL);
-               free(extra->data);
-               free(extra);
-       }
+       pthread_mutex_lock(&block->mtx);
+       send_block_to_near(block);
+       pthread_mutex_unlock(&block->mtx);
 }
 
-static void reset_block(MapBlock *block)
+// pthread start routine for mapgen thread
+static void *mapgen_thread(void *arg)
 {
+       MapBlock *block = arg;
        MapBlockExtraData *extra = block->extra;
 
-       if (extra) {
-               free(extra->data);
-       } else {
-               extra = malloc(sizeof(MapBlockExtraData));
-               extra->clients = list_create(&list_compare_string);
-       }
+       pthread_mutex_lock(&block->mtx);
+       extra->state = MBS_GENERATING;
+       pthread_mutex_unlock(&block->mtx);
 
-       map_serialize_block(block, &extra->data, &extra->size);
+       List changed_blocks = list_create(NULL);
+       list_put(&changed_blocks, block, NULL);
 
-       block->extra = extra;
-       block->free_extra = &free_extra_data;
+       mapgen_generate_block(block, &changed_blocks);
 
-       mapdb_save_block(server_map.db, block);
+       pthread_mutex_lock(&block->mtx);
+       extra->state = MBS_READY;
+       pthread_mutex_unlock(&block->mtx);
+
+       list_clear_func(&changed_blocks, &list_send_block, NULL);
 
-       list_clear_func(&extra->clients, &reset_client_block, block);
-       list_clear(&extra->clients);
+       if (! server_map.shutting_down) {
+               pthread_mutex_lock(&server_map.mapgen_threads_mtx);
+               list_delete(&server_map.mapgen_threads, &extra->mapgen_thread);
+               pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
+       }
 
-       block->state = MBS_READY;
+       return NULL;
 }
 
-static void send_block(Client *client, MapBlock *block)
+// launch mapgen thread for block
+// block mutex has to be locked
+static void launch_mapgen_thread(MapBlock *block)
 {
-       MapBlockExtraData *extra = block->extra;
-
-       list_put(&client->sent_blocks, block, block);
-       list_put(&extra->clients, strdup(client->name), NULL);
-
-       pthread_mutex_lock(&client->mtx);
-       if (client->state == CS_ACTIVE)
-               (void) (write_u32(client->fd, CC_BLOCK) && write_v3s32(client->fd, block->pos) && write(client->fd, extra->data, extra->size) != -1);
-       pthread_mutex_unlock(&client->mtx);
+       pthread_mutex_lock(&server_map.mapgen_threads_mtx);
+       pthread_t *thread_ptr = &((MapBlockExtraData *) block->extra)->mapgen_thread;
+       pthread_create(thread_ptr, NULL, mapgen_thread, block);
+       list_put(&server_map.mapgen_threads, thread_ptr, NULL);
+       pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
 }
 
-static void reset_modified_blocks(Client *client)
+// list_clear_func callback used to join running generator threads on shutdown
+static void list_join_thread(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *arg)
 {
-       for (ListPair **pairptr = &client->sent_blocks.first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
+       pthread_join(*(pthread_t *) key, NULL);
+}
 
-               MapBlock *block = (*pairptr)->key;
+// map callbacks
+// note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
 
-               pthread_mutex_lock(&block->mtx);
-               if (block->state == MBS_MODIFIED) {
-                       reset_block(block);
+// callback for initializing a newly created block
+// load block from database or initialize state, mgstage buffer and data
+static void on_create_block(MapBlock *block)
+{
+       MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
 
-                       ListPair *next = (*pairptr)->next;
-                       free(*pairptr);
-                       *pairptr = next;
+       if (! mapdb_load_block(server_map.db, block)) {
+               extra->state = MBS_CREATED;
+               extra->data = NULL;
+
+               ITERATE_MAPBLOCK {
+                       block->data[x][y][z] = map_node_create(NODE_AIR);
+                       block->metadata[x][y][z] = list_create(&list_compare_string);
+                       extra->mgs_buffer[x][y][z] = MGS_VOID;
                }
-               pthread_mutex_unlock(&block->mtx);
        }
 }
 
-static void send_blocks(Client *client)
+// callback for deleting a block
+// free extra data
+static void on_delete_block(MapBlock *block)
 {
-       v3s32 pos = map_node_to_block_pos((v3s32) {client->pos.x, client->pos.y, client->pos.z}, NULL);
+       MapBlockExtraData *extra = block->extra;
 
-       for (size_t i = 0; i < server_map.max_blocks; i++) {
-               MapBlock *block = map_get_block(client->server->map, facecache_face(i, &pos), true);
+       if (extra->data)
+               free(extra->data);
 
-               pthread_mutex_lock(&block->mtx);
-               switch (block->state) {
-                       case MBS_CREATED:
+       free(extra);
+}
 
-                               initialize_block(block);
+// callback for determining whether a block should be returned by map_get_block
+// hold back blocks that are not fully generated except when the create flag is set to true
+static bool on_get_block(MapBlock *block, bool create)
+{
+       MapBlockExtraData *extra = block->extra;
 
-                       __attribute__ ((fallthrough)); case MBS_MODIFIED:
+       if (extra->state < MBS_READY && ! create)
+               return false;
 
-                               reset_block(block);
+       return true;
+}
 
-                               send:
-                               send_block(client, block);
+// callback for deciding whether a set_node call succeeds or not
+// reject set_node calls that try to override nodes placed by later mapgen stages, else update mgs buffer - also make sure block is inserted into changed blocks list
+static bool on_set_node(MapBlock *block, v3u8 offset, __attribute__((unused)) MapNode *node, void *arg)
+{
+       MapgenSetNodeArg *msn_arg = arg;
 
-                               pthread_mutex_unlock(&block->mtx);
-                               return;
+       MapgenStage mgs;
 
-                       case MBS_READY:
+       if (msn_arg)
+               mgs = msn_arg->mgs;
+       else
+               mgs = MGS_PLAYER;
 
-                               if (! list_get(&client->sent_blocks, block))
-                                       goto send;
-                               else
-                                       break;
+       MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgs_buffer[offset.x][offset.y][offset.z];
 
-                       default:
+       if (mgs >= *old_mgs) {
+               *old_mgs = mgs;
 
-                               break;
-               }
-               pthread_mutex_unlock(&block->mtx);
+               if (msn_arg)
+                       list_put(msn_arg->changed_blocks, block, NULL);
+
+               return true;
        }
+
+       return false;
 }
 
-static void map_step()
+// callback for when a block changes
+// send block to near clients if not part of map generation
+static void on_after_set_node(MapBlock *block, __attribute__((unused)) v3u8 offset, void *arg)
 {
-       pthread_rwlock_rdlock(&server_map.server->players_rwlck);
-       ITERATE_LIST(&server_map.server->players, pair) reset_modified_blocks(pair->value);
-       ITERATE_LIST(&server_map.server->players, pair) send_blocks(pair->value);
-       pthread_rwlock_unlock(&server_map.server->players_rwlck);
+       if (! arg)
+               send_block_to_near(block);
 }
 
-static void *map_thread(__attribute__((unused)) void *unused)
+// public functions
+
+// ServerMap singleton constructor
+void server_map_init(Server *srv)
 {
+       server = srv;
+
+       server_map.map = map_create((MapCallbacks) {
+               .create_block = &on_create_block,
+               .delete_block = &on_delete_block,
+               .get_block = &on_get_block,
+               .set_node = &on_set_node,
+               .after_set_node = &on_after_set_node,
+       });
+       server_map.shutting_down = false;
        server_map.db = mapdb_open("map.sqlite");
-
-       while (! server_map.cancel)
-               map_step();
-
-       return NULL;
+       server_map.mapgen_threads = list_create(NULL);
+       pthread_mutex_init(&server_map.mapgen_threads_mtx, NULL);
 }
 
-void server_map_init(Server *server)
+// ServerMap singleton destructor
+void server_map_deinit()
 {
-       server_map.server = server;
-       server_map.max_blocks = facecache_count(16);
+       server_map.shutting_down = true;
+
+       pthread_mutex_lock(&server_map.mapgen_threads_mtx);
+       list_clear_func(&server_map.mapgen_threads, &list_join_thread, NULL);
+       // pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
+       pthread_mutex_destroy(&server_map.mapgen_threads_mtx);
 
-       pthread_create(&server_map.thread, NULL, &map_thread, NULL);
+       sqlite3_close(server_map.db);
+       map_delete(server_map.map);
 }
 
-void server_map_deinit()
+// handle block request from client (thread safe)
+void server_map_requested_block(Client *client, v3s32 pos)
 {
-       server_map.cancel = true;
-       pthread_join(server_map.thread, NULL);
-       sqlite3_close(server_map.db);
+       if (within_simulation_distance(client->pos, pos, server->config.simulation_distance)) {
+               MapBlock *block = map_get_block(server_map.map, pos, true);
+
+               pthread_mutex_lock(&block->mtx);
+               MapBlockExtraData *extra = block->extra;
+
+               switch (extra->state) {
+                       case MBS_CREATED:
+                               launch_mapgen_thread(block);
+                               break;
+
+                       case MBS_GENERATING:
+                               break;
+
+                       case MBS_READY:
+                               send_block(client, block);
+               };
+               pthread_mutex_unlock(&block->mtx);
+       }
 }
index b98e86646fdb7ed70fe750d53d3ecc7b2829eaef..1ad1a561ac97e53c39e1ed7cca9d774780b86efe 100644 (file)
@@ -1,16 +1,53 @@
 #ifndef _SERVER_MAP_H_
 #define _SERVER_MAP_H_
 
+#include <stddef.h>
+#include <pthread.h>
+#include "map.h"
 #include "server/server.h"
+#include "server/mapdb.h"
+
+typedef enum
+{
+       MBS_CREATED,    // block exists but was not yet generated
+       MBS_GENERATING, // currently generating in a seperate thread
+       MBS_READY,              // generation finished
+} MapBlockState;
+
+typedef enum
+{
+       MGS_VOID,               // initial air, can be overridden by anything
+       MGS_TERRAIN,    // basic terrain, can be overridden by anything except the void
+       MGS_BOULDERS,   // boulders, replace terrain
+       MGS_PLAYER,             // player-placed nodes or things placed after map generation
+} MapgenStage;
+
+typedef MapgenStage MapgenStageBuffer[MAPBLOCK_SIZE][MAPBLOCK_SIZE][MAPBLOCK_SIZE];
+
+typedef struct {
+       MapgenStage mgs;
+       List *changed_blocks;
+} MapgenSetNodeArg;
 
 typedef struct
 {
-       List clients;
-       char *data;
-       size_t size;
+       char *data;                                             // cached serialized data
+       size_t size;                                    // size of data
+       MapBlockState state;                    // generation state of the block
+       pthread_t mapgen_thread;                // thread that is generating block
+       MapgenStageBuffer mgs_buffer;   // buffer to make sure mapgen only overrides things it should
 } MapBlockExtraData;
 
-void server_map_init(Server *server);
-void server_map_deinit();
+extern struct ServerMap {
+       Map *map;                                                               // map object, data is stored here
+       sqlite3 *db;                                                    // SQLite3 database to save data to database
+       bool shutting_down;                                             // is a shutdown in progress?
+       List mapgen_threads;                                    // a list of mapgen threads (need to be joined before shutdown)
+       pthread_mutex_t mapgen_threads_mtx;             // mutex to protect mapgen thread list
+} server_map; // ServerMap singleton
+
+void server_map_init(Server *server);                                          // ServerMap singleton constructor
+void server_map_deinit();                                                                      // ServerMap singleton destructor
+void server_map_requested_block(Client *client, v3s32 pos);    // handle block request from client (thread safe)
 
 #endif
index 94e9fd379f2dbd0c62b240545a2d82c21174e55f..10685f0821a71b55226631c8d84f5c002587cd2a 100644 (file)
@@ -48,6 +48,10 @@ bool read_full(int fd, char *buffer, size_t size)
                type vec[2] = {val.x, val.y}; \
                WRITEVEC(type, 2) \
        } \
+       bool v2 ## type ## _equals(v2 ## type a, v2 ## type b) \
+       { \
+               return a.x == b.x && a.y == b.y; \
+       } \
        bool read_v3 ## type(int fd, v3 ## type *ptr) \
        { \
                READVEC(type, 3) \
@@ -60,6 +64,10 @@ bool read_full(int fd, char *buffer, size_t size)
        { \
                type vec[3] = {val.x, val.y, val.z}; \
                WRITEVEC(type, 3) \
+       } \
+       bool v3 ## type ## _equals(v3 ## type a, v3 ## type b) \
+       { \
+               return a.x == b.x && a.y == b.y && a.z == b.z; \
        }
 
 #define DEFTYP(type, bits) \
index f99736bd10847c4124b20891d42c4a049847d046..52b8f348a293d5131ffec4518bb7889dacd530dd 100644 (file)
@@ -18,9 +18,11 @@ bool read_full(int fd, char *buffer, size_t size);
        typedef struct {type x, y;} v2 ## type; \
        DEFRW(v2 ## type) \
        DEFBOX(2 ## type) \
+       bool v2 ## type ## _equals(v2 ## type a, v2 ## type b); \
        typedef struct {type x, y, z;} v3 ## type; \
        DEFRW(v3 ## type) \
-       DEFBOX(3 ## type)
+       DEFBOX(3 ## type) \
+       bool v3 ## type ## _equals(v3 ## type a, v3 ## type b); \
 
 #define DEFTYP(from, to) \
        typedef from to; \
@@ -42,12 +44,6 @@ typedef double f64;
 DEFTYP(float, f32)
 DEFTYP(double, f64)
 
-typedef v2f32 v2f;
-typedef v3f32 v3f;
-
-typedef aabb2f32 aabb2f;
-typedef aabb3f32 aabb3f;
-
 #undef DEFRW
 #undef DEFBOX
 #undef DEFVEC
index bbfd0bd621d0595ccd581613cd4caa7e31f953a9..1329c83e171b79e6fa4eaea6443719562f34be7d 100644 (file)
@@ -2,22 +2,28 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
+#include <zlib.h>
+#include "map.h"
 #include "util.h"
 
 const char *program_name;
 
+// print system call related error message and exit
 void syscall_error(const char *err)
 {
        perror(err);
        exit(EXIT_FAILURE);
 }
 
+// print general error message and exit
 void internal_error(const char *err)
 {
        fprintf(stderr, "%s: %s\n", program_name, err);
        exit(EXIT_FAILURE);
 }
 
+// read from fd until \0 or EOF terminator
+// store result including terminator into allocated buffer until bufsiz+1 is hit, return NULL on read error
 char *read_string(int fd, size_t bufsiz)
 {
        char buf[bufsiz + 1];
@@ -36,6 +42,7 @@ char *read_string(int fd, size_t bufsiz)
        return strdup(buf);
 }
 
+// convert IPv6 address to human readable, return allocated buffer
 char *address_string(struct sockaddr_in6 *addr)
 {
        char address[INET6_ADDRSTRLEN] = {0};
@@ -50,9 +57,60 @@ char *address_string(struct sockaddr_in6 *addr)
        return result;
 }
 
-v3f html_to_v3f(const char *html)
+// convert #RRGGBB color to v3f32
+v3f32 html_to_v3f32(const char *html)
 {
        unsigned int r, g, b;
        sscanf(html, "#%2x%2x%2x", &r, &g, &b);
-       return (v3f) {(f32) r / 255.0f, (f32) g / 255.0f, (f32) b / 255.0f};
+       return (v3f32) {(f32) r / 255.0f, (f32) g / 255.0f, (f32) b / 255.0f};
+}
+
+// compress data using ZLib and store result(buffer allocated by malloc) in compressed
+void my_compress(const void *uncompressed, size_t uncompressed_size, char **compressed, size_t *compressed_size)
+{
+       char compressed_buffer[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;
+       stream.next_out = (Bytef *) compressed_buffer;
+
+       deflateInit(&stream, Z_BEST_COMPRESSION);
+       deflate(&stream, Z_FINISH);
+       deflateEnd(&stream);
+
+       *compressed_size = stream.total_out;
+       *compressed = malloc(*compressed_size);
+       memcpy(*compressed, compressed_buffer, *compressed_size);
+}
+
+// decompress data and put result into decompressed, return false if decompressed size does not match expected_decompressed_size
+bool my_decompress(const char *compressed, size_t compressed_size, void *decompressed, size_t expected_decompressed_size)
+{
+       z_stream stream;
+       stream.zalloc = Z_NULL;
+       stream.zfree = Z_NULL;
+       stream.opaque = Z_NULL;
+
+       stream.avail_in = compressed_size;
+       stream.next_in = (Bytef *) compressed;
+       stream.avail_out = expected_decompressed_size;
+       stream.next_out = (Bytef *) decompressed;
+
+       inflateInit(&stream);
+       inflate(&stream, Z_NO_FLUSH);
+       inflateEnd(&stream);
+
+       return stream.total_out == expected_decompressed_size;
+}
+
+// return true if a player is close enough to a block to access it
+bool within_simulation_distance(v3f64 player_pos, v3s32 block_pos, u32 simulation_distance)
+{
+       v3s32 player_block_pos = map_node_to_block_pos((v3s32) {player_pos.x, player_pos.y, player_pos.z}, NULL);
+       return abs(player_block_pos.x - block_pos.x) <= simulation_distance && abs(player_block_pos.y - block_pos.y) <= simulation_distance && abs(player_block_pos.z - block_pos.z) <= simulation_distance;
 }
index 9d76b7eee7dcbcf98acf5b459154cc8bb381182c..e39da9d78799337ff1832673ff06c13f8b18bd48 100644 (file)
@@ -1,20 +1,24 @@
 #ifndef _UTIL_H_
 #define _UTIL_H_
 
+#include <stdbool.h>
 #include <arpa/inet.h>
-#include <linmath.h/linmath.h>
 #include "types.h"
 
-#define ever (;;)
-#define INBRACES(str) str ? "(" : "", str ? str : "", str ? ")" : ""
-#define CMPBOUNDS(x) x == 0 ? 0 : x > 0 ? 1 : -1
+#define ever (;;)                                                                                                              // infinite for loop with style
+#define INBRACES(str) str ? "(" : "", str ? str : "", str ? ")" : ""   // wrapper for printf to optionally add a message in braces if message is not NULL
+#define CMPBOUNDS(x) x == 0 ? 0 : x > 0 ? 1 : -1                                               // resolves 1 if x > 0, 0 if x == 0 and -1 if x < 0
+#define fallthrough __attribute__ ((fallthrough))                                              // prevent compiler warning about implicit fallthrough with style
 
-extern const char *program_name;
+extern const char *program_name;       // this has to be set to program name on startup
 
-void syscall_error(const char *err);
-void internal_error(const char *err);
-char *read_string(int fd, size_t bufsiz);
-char *address_string(struct sockaddr_in6 *addr);
-v3f html_to_v3f(const char *html);
+void syscall_error(const char *err);                                                                                                                                                                           // print system call related error message and exit
+void internal_error(const char *err);                                                                                                                                                                          // print general error message and exit
+char *read_string(int fd, size_t bufsiz);                                                                                                                                                                      // read from fd until \0 or EOF terminator
+char *address_string(struct sockaddr_in6 *addr);                                                                                                                                                       // convert IPv6 address to human readable, return allocated buffer
+v3f32 html_to_v3f32(const char *html);                                                                                                                                                                         // convert #RRGGBB color to v3f32
+void my_compress(const void *uncompressed, size_t uncompressed_size, char **compressed, size_t *compressed_size);                      // compress data using ZLib and store result(buffer allocated by malloc) in compressed
+bool my_decompress(const char *compressed, size_t compressed_size, void *decompressed, size_t expected_decompressed_size);     // decompress data and put result into decompressed, return false if decompressed size does not match expected_decompressed_size
+bool within_simulation_distance(v3f64 player_pos, v3s32 block_pos, u32 simulation_distance);                                                           // return true if a player is close enough to a block to access it
 
 #endif