]> git.lizzy.rs Git - dragonblocks_alpha.git/commitdiff
Save MapBlocks in SQLite3 database; Redesign MapBlock loading & sending code
authorElias Fleckenstein <eliasfleckenstein@web.de>
Thu, 1 Apr 2021 14:07:41 +0000 (16:07 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Thu, 1 Apr 2021 14:07:41 +0000 (16:07 +0200)
22 files changed:
.github/workflows/build.yml
.github/workflows/snapshot.yml
.gitignore
BUILDING.md
README.md
src/Makefile
src/blockmesh.c [new file with mode: 0644]
src/blockmesh.h [new file with mode: 0644]
src/client.c
src/clientcommands.c
src/clientmap.c
src/map.c
src/map.h
src/mapdb.c [new file with mode: 0644]
src/mapdb.h [new file with mode: 0644]
src/queue.c [new file with mode: 0644]
src/queue.h [new file with mode: 0644]
src/server.c
src/server.h
src/servercommands.c
src/servermap.c
src/servermap.h

index 4318688e8030b6b6ce3766057d398cbf82ebdd97..1de8262912ce171862c189e7b1cc698bad2a4510 100644 (file)
@@ -11,7 +11,7 @@ jobs:
       - name: Install deps
         run: |
           sudo apt-get update
-          sudo apt-get install -y build-essential make libgl1-mesa-dev libglfw3-dev libglew-dev
+          sudo apt-get install -y build-essential make libgl1-mesa-dev libglfw3-dev libglew-dev libsqlite3-dev
       - name: Build
         run: |
           cd src
index a2d03e498e9a5acf6a330b7ce2a4ab6899b983ae..42b8b1cf8c107c29946284e547761ec945130c21 100644 (file)
@@ -11,7 +11,7 @@ jobs:
       - name: Install deps
         run: |
           sudo apt-get update
-          sudo apt-get install -y build-essential make libgl1-mesa-dev libglfw3-dev libglew-dev curl zip
+          sudo apt-get install -y build-essential make libgl1-mesa-dev libglfw3-dev libglew-dev libsqlite3-dev curl zip
       - name: Build
         run: |
           ./snapshot.sh
index 8c9bda4b51106f8be0a68cde7e1347cc71ba3062..fc35a3c04ea002844da240606808f95ab1faa7b7 100644 (file)
@@ -1,5 +1,5 @@
 *.o
 DragonblocksServer
 Dragonblocks
-map
+map.sqlite
 DragonblocksAlpha-*.zip
index 4e42aa63f03f5df7e2805871da62bb9561b6e11e..810549362d1978c2e7795cd5cdc3bec8d6f4aeac 100644 (file)
@@ -4,16 +4,25 @@ GNU make is used for compiling. The code and the Makefile are located in the src
 
 ## Dependencies
 To build anything you need g++ and GNU make.
+
 ```bash
 sudo apt install build-essential make
 ```
 
 The development versions OpenGL, GLFW3, GLEW are required to build the client.
+
 ```bash
 sudo apt install libgl1-mesa-dev libglfw3-dev libglew-dev
 ```
 
+For building the server, the SQLite3 development library is required.
+
+```bash
+sudo apt install libsqlite3-dev
+```
+
 Don't forget to pull the submodules before building.
+
 ``bash
 git submodule update --init
 ```
@@ -28,6 +37,7 @@ git submodule update --init
 The debug flag (`-g`) is set by default (RELEASE=TRUE will disable it).
 
 ## Release
+
 ```bash
 ./snapshot.sh
 ```
index 51e8c1790f02beced14efc2399d7043923ac67b6..0e91630dcc07aa37fb8953a58626f8204ea112c9 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,8 +1,9 @@
 # Dragonblocks alpha
 
-A multiplayer voxelgame for POSIX.1-2008 systems.
+A multiplayer voxelgame for POSIX systems.
 
 ## Usage
+
 ```bash
 ./DragonblocksServer <port>
 ./Dragonblocks <address> <port>
@@ -29,3 +30,9 @@ The client depends on GLFW3, OpenGL and GLEW.
 ```bash
 sudo apt install libgl1-mesa-dri libglfw3 libglew2.1
 ```
+
+The server depends on SQLite3.
+
+```bash
+sudo apt install libsqlite3-0
+```
index a21289b752b15b69dd27aff0f6e0280fe69b0f8c..3e24099147abe432da1a8c03a4edaebb6b7b3f63 100644 (file)
@@ -1,6 +1,6 @@
-COMMON = array.o list.o map.o signal.o util.o types.o node.o
-SERVER = $(COMMON) server.o servercommands.o servermap.o perlin.o facecache.o mapgen.o
-CLIENT = $(COMMON) client.o clientcommands.o clientmap.o mesh.o scene.o shaders.o
+COMMON = array.o list.o map.o signal.o util.o types.o node.o queue.o
+SERVER = $(COMMON) server.o servercommands.o servermap.o perlin.o facecache.o mapgen.o mapdb.o
+CLIENT = $(COMMON) client.o clientcommands.o clientmap.o mesh.o scene.o shaders.o blockmesh.o
 LIBRARIES = -lpthread -lm
 FLAGS = -g -fmax-errors=4
 
@@ -14,7 +14,7 @@ Dragonblocks: $(CLIENT)
        cc $(FLAGS) -o Dragonblocks $(CLIENT) $(LIBRARIES) -lGL -lGLEW -lglfw
 
 DragonblocksServer: $(SERVER)
-       cc $(FLAGS) -o DragonblocksServer $(SERVER) $(LIBRARIES)
+       cc $(FLAGS) -o DragonblocksServer $(SERVER) $(LIBRARIES) -lsqlite3
 
 %.o: %.c
        cc $(FLAGS) -Wall -Wextra -Wpedantic -Werror -isystem ../deps -c -o $@ $<
diff --git a/src/blockmesh.c b/src/blockmesh.c
new file mode 100644 (file)
index 0000000..af4675f
--- /dev/null
@@ -0,0 +1,121 @@
+#include "blockmesh.h"
+
+static v3f vpos[6][6] = {
+       {
+               {-0.5f, -0.5f, -0.5f},
+               {+0.5f, -0.5f, -0.5f},
+               {+0.5f, +0.5f, -0.5f},
+               {+0.5f, +0.5f, -0.5f},
+               {-0.5f, +0.5f, -0.5f},
+               {-0.5f, -0.5f, -0.5f},
+       },
+       {
+               {-0.5f, -0.5f, +0.5f},
+               {+0.5f, +0.5f, +0.5f},
+               {+0.5f, -0.5f, +0.5f},
+               {+0.5f, +0.5f, +0.5f},
+               {-0.5f, -0.5f, +0.5f},
+               {-0.5f, +0.5f, +0.5f},
+       },
+       {
+               {-0.5f, +0.5f, +0.5f},
+               {-0.5f, -0.5f, -0.5f},
+               {-0.5f, +0.5f, -0.5f},
+               {-0.5f, -0.5f, -0.5f},
+               {-0.5f, +0.5f, +0.5f},
+               {-0.5f, -0.5f, +0.5f},
+       },
+       {
+               {+0.5f, +0.5f, +0.5f},
+               {+0.5f, +0.5f, -0.5f},
+               {+0.5f, -0.5f, -0.5f},
+               {+0.5f, -0.5f, -0.5f},
+               {+0.5f, -0.5f, +0.5f},
+               {+0.5f, +0.5f, +0.5f},
+       },
+       {
+               {-0.5f, -0.5f, -0.5f},
+               {+0.5f, -0.5f, -0.5f},
+               {+0.5f, -0.5f, +0.5f},
+               {+0.5f, -0.5f, +0.5f},
+               {-0.5f, -0.5f, +0.5f},
+               {-0.5f, -0.5f, -0.5f},
+       },
+       {
+               {-0.5f, +0.5f, -0.5f},
+               {+0.5f, +0.5f, -0.5f},
+               {+0.5f, +0.5f, +0.5f},
+               {+0.5f, +0.5f, +0.5f},
+               {-0.5f, +0.5f, +0.5f},
+               {-0.5f, +0.5f, -0.5f},
+       },
+};
+
+static v3s8 fdir[6] = {
+       {+0, +0, -1},
+       {+0, +0, +1},
+       {-1, +0, +0},
+       {+1, +0, +0},
+       {+0, -1, +0},
+       {+0, +1, +0},
+};
+
+#define GNODDEF(block, x, y, z) node_definitions[block->data[x][y][z].type]
+#define VALIDPOS(pos) (pos.x >= 0 && pos.x < 16 && pos.y >= 0 && pos.y < 16 && pos.z >= 0 && pos.z < 16)
+
+static Array make_vertices(MapBlock *block)
+{
+       Array vertices = array_create(sizeof(Vertex));
+
+       ITERATE_MAPBLOCK {
+               NodeDefintion *def = &GNODDEF(block, x, y, z);
+               if (def->visible) {
+                       v3u8 pos = {x, y, z};
+                       v3f offset = {x + 8.5f, y + 8.5f, z + 8.5f};
+                       v3f color = get_node_color(def);
+                       for (int f = 0; f < 6; f++) {
+                               v3s8 *noff = &fdir[f];
+                               v3s8 npos = {
+                                       pos.x + noff->x,
+                                       pos.y + noff->y,
+                                       pos.z + noff->z,
+                               };
+                               if (! VALIDPOS(npos) || ! GNODDEF(block, npos.x, npos.y, npos.z).visible) {
+                                       for (int v = 0; v < 6; v++) {
+                                               Vertex vertex = {
+                                                       vpos[f][v].x + offset.x,
+                                                       vpos[f][v].y + offset.y,
+                                                       vpos[f][v].z + offset.z,
+                                                       color.x,
+                                                       color.y,
+                                                       color.z,
+                                               };
+                                               array_append(&vertices, &vertex);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return vertices;
+}
+
+#undef GNODDEF
+#undef VALIDPOS
+
+void make_block_mesh(MapBlock *block, Scene *scene)
+{
+       Array vertices = make_vertices(block);
+       Mesh *mesh = NULL;
+
+       if (vertices.siz > 0) {
+               mesh = mesh_create(vertices.ptr, vertices.siz);
+               mesh->pos = (v3f) {block->pos.x * 16.0f - 8.0f, block->pos.y * 16.0f - 8.0f, block->pos.z * 16.0f - 8.0f};
+               mesh_transform(mesh);
+               scene_add_mesh(scene, mesh);
+       }
+
+       if (block->extra)
+               ((Mesh *) block->extra)->remove = true;
+       block->extra = mesh;
+}
diff --git a/src/blockmesh.h b/src/blockmesh.h
new file mode 100644 (file)
index 0000000..0cefc7e
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _BLOCKMESH_H_
+#define _BLOCKMESH_H_
+
+#include "map.h"
+#include "scene.h"
+
+void make_block_mesh(MapBlock *block, Scene *scene);
+
+#endif
index 0d581c61a9c342370c8da6b15271dd928fa8c9ed..70d87277bd5e215560e34217e7272f69f5f88a0f 100644 (file)
@@ -216,7 +216,7 @@ static void client_start(int fd)
 
        clientmap_deinit();
 
-       map_delete(client.map);
+       map_delete(client.map, NULL);
        scene_delete(client.scene);
 
        pthread_mutex_destroy(&client.mtx);
index d3d816bb2d2fb5d613e07a0343c4d963b4bcc817..648505ad9df37fa535c82020212c96c4be513932 100644 (file)
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <unistd.h>
 #include "client.h"
 #include "clientmap.h"
 #include "types.h"
@@ -34,12 +35,45 @@ static bool auth_handler(Client *client, bool good)
 
 static bool block_handler(Client *client, bool good)
 {
-       MapBlock *block;
-       if (! map_deserialize_block(client->fd, client->map, &block, ! good))
+       v3s32 pos;
+
+       if (! read_v3s32(client->fd, &pos))
+               return false;
+
+       u64 size;
+
+       if (! read_u64(client->fd, &size))
                return false;
+
+       char data[size];
+       size_t n_read_total = 0;
+       int n_read;
+       while (n_read_total < size) {
+               if ((n_read = read(client->fd, data + n_read_total, size - n_read_total)) == -1) {
+                       perror("read");
+                       return false;
+               }
+               n_read_total += n_read;
+       }
+
+       MapBlock *block;
+
+       if (good)
+               block = map_get_block(client->map, pos, true);
+       else
+               block = map_allocate_block(pos);
+
+       if (block->state != MBS_CREATED)
+               map_clear_meta(block);
+
+       bool ret = map_deserialize_block(block, data, size);
+
        if (good)
                clientmap_block_changed(block);
-       return true;
+       else
+               map_free_block(block);
+
+       return ret;
 }
 
 CommandHandler command_handlers[CLIENT_COMMAND_COUNT] = {
index 5180b0ca0332b34d67f57e795c5c0f24de1c84d1..4f65239713b0fc5571b10d47bb96e35a20bb0009 100644 (file)
 #include <stdlib.h>
 #include "clientmap.h"
+#include "blockmesh.h"
+#include "queue.h"
 
 static struct
 {
-       List queue;
-       pthread_mutex_t mtx;
+       Queue *queue;
        pthread_t thread;
        bool cancel;
 } meshgen;
 
 static Client *client = NULL;
 
-static v3f vpos[6][6] = {
-       {
-               {-0.5f, -0.5f, -0.5f},
-               {+0.5f, -0.5f, -0.5f},
-               {+0.5f, +0.5f, -0.5f},
-               {+0.5f, +0.5f, -0.5f},
-               {-0.5f, +0.5f, -0.5f},
-               {-0.5f, -0.5f, -0.5f},
-       },
-       {
-               {-0.5f, -0.5f, +0.5f},
-               {+0.5f, +0.5f, +0.5f},
-               {+0.5f, -0.5f, +0.5f},
-               {+0.5f, +0.5f, +0.5f},
-               {-0.5f, -0.5f, +0.5f},
-               {-0.5f, +0.5f, +0.5f},
-       },
-       {
-               {-0.5f, +0.5f, +0.5f},
-               {-0.5f, -0.5f, -0.5f},
-               {-0.5f, +0.5f, -0.5f},
-               {-0.5f, -0.5f, -0.5f},
-               {-0.5f, +0.5f, +0.5f},
-               {-0.5f, -0.5f, +0.5f},
-       },
-       {
-               {+0.5f, +0.5f, +0.5f},
-               {+0.5f, +0.5f, -0.5f},
-               {+0.5f, -0.5f, -0.5f},
-               {+0.5f, -0.5f, -0.5f},
-               {+0.5f, -0.5f, +0.5f},
-               {+0.5f, +0.5f, +0.5f},
-       },
-       {
-               {-0.5f, -0.5f, -0.5f},
-               {+0.5f, -0.5f, -0.5f},
-               {+0.5f, -0.5f, +0.5f},
-               {+0.5f, -0.5f, +0.5f},
-               {-0.5f, -0.5f, +0.5f},
-               {-0.5f, -0.5f, -0.5f},
-       },
-       {
-               {-0.5f, +0.5f, -0.5f},
-               {+0.5f, +0.5f, -0.5f},
-               {+0.5f, +0.5f, +0.5f},
-               {+0.5f, +0.5f, +0.5f},
-               {-0.5f, +0.5f, +0.5f},
-               {-0.5f, +0.5f, -0.5f},
-       },
-};
-
-static v3s8 fdir[6] = {
-       {+0, +0, -1},
-       {+0, +0, +1},
-       {-1, +0, +0},
-       {+1, +0, +0},
-       {+0, -1, +0},
-       {+0, +1, +0},
-};
-
-#define GNODDEF(block, x, y, z) node_definitions[block->data[x][y][z].type]
-#define VALIDPOS(pos) (pos.x >= 0 && pos.x < 16 && pos.y >= 0 && pos.y < 16 && pos.z >= 0 && pos.z < 16)
-
-static Array make_vertices(MapBlock *block)
+static void set_block_ready(void *block)
 {
-       Array vertices = array_create(sizeof(Vertex));
-
-       ITERATE_MAPBLOCK {
-               NodeDefintion *def = &GNODDEF(block, x, y, z);
-               if (def->visible) {
-                       v3u8 pos = {x, y, z};
-                       v3f offset = {x + 8.5f, y + 8.5f, z + 8.5f};
-                       v3f color = get_node_color(def);
-                       for (int f = 0; f < 6; f++) {
-                               v3s8 *noff = &fdir[f];
-                               v3s8 npos = {
-                                       pos.x + noff->x,
-                                       pos.y + noff->y,
-                                       pos.z + noff->z,
-                               };
-                               if (! VALIDPOS(npos) || ! GNODDEF(block, npos.x, npos.y, npos.z).visible) {
-                                       for (int v = 0; v < 6; v++) {
-                                               Vertex vertex = {
-                                                       vpos[f][v].x + offset.x,
-                                                       vpos[f][v].y + offset.y,
-                                                       vpos[f][v].z + offset.z,
-                                                       color.x,
-                                                       color.y,
-                                                       color.z,
-                                               };
-                                               array_append(&vertices, &vertex);
-                                       }
-                               }
-                       }
-               }
-       }
-
-       return vertices;
+       ((MapBlock *) block)->state = MBS_READY;
 }
 
-#undef GNODDEF
-#undef VALIDPOS
-
 static void *meshgen_thread(void *unused)
 {
        (void) unused;
 
        while (! meshgen.cancel) {
-               ListPair **lptr = &meshgen.queue.first;
-               if (*lptr) {
-                       pthread_mutex_lock(&meshgen.mtx);
-                       MapBlock *block = (*lptr)->key;
-                       ListPair *next = (*lptr)->next;
-                       free(*lptr);
-                       *lptr = next;
-                       pthread_mutex_unlock(&meshgen.mtx);
-
-                       Array vertices = make_vertices(block);
-                       Mesh *mesh = NULL;
-
-                       if (vertices.siz > 0) {
-                               mesh = mesh_create(vertices.ptr, vertices.siz);
-                               mesh->pos = (v3f) {block->pos.x * 16.0f - 8.0f, block->pos.y * 16.0f - 8.0f, block->pos.z * 16.0f - 8.0f};
-                               mesh_transform(mesh);
-                               scene_add_mesh(client->scene, mesh);
-                       }
-
-                       if (block->extra)
-                               ((Mesh *) block->extra)->remove = true;
-
-                       block->extra = mesh;
-               } else {
+               MapBlock *block;
+               if ((block = dequeue_callback(meshgen.queue, &set_block_ready)))
+                       make_block_mesh(block, client->scene);
+               else
                        sched_yield();
-               }
        }
 
        return NULL;
@@ -153,8 +35,7 @@ static void *meshgen_thread(void *unused)
 void clientmap_init(Client *cli)
 {
        client = cli;
-       meshgen.queue = list_create(NULL);
-       pthread_mutex_init(&meshgen.mtx, NULL);
+       meshgen.queue = create_queue();
        pthread_create(&meshgen.thread, NULL, &meshgen_thread, NULL);
 }
 
@@ -162,13 +43,14 @@ void clientmap_deinit()
 {
        meshgen.cancel = true;
        pthread_join(meshgen.thread, NULL);
-       pthread_mutex_destroy(&meshgen.mtx);
-       list_clear(&meshgen.queue);
 }
 
 void clientmap_block_changed(MapBlock *block)
 {
-       pthread_mutex_lock(&meshgen.mtx);
-       list_put(&meshgen.queue, block, NULL);
-       pthread_mutex_unlock(&meshgen.mtx);
+       pthread_mutex_lock(&block->mtx);
+       if (block->state == MBS_MODIFIED) {
+               block->state = MBS_PROCESSING;
+               enqueue(meshgen.queue, block);
+       }
+       pthread_mutex_unlock(&block->mtx);
 }
index 157c0378654e22ecc4ce33c1d60038761450c543..269111d017f4c07c1b24471461a918a7a79c3beb 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -20,40 +20,6 @@ static s8 block_compare(void *level, void *block)
        return CMPBOUNDS(d);
 }
 
-static MapBlock *allocate_block(v3s32 pos)
-{
-       MapBlock *block = malloc(sizeof(MapBlock));
-       block->pos = pos;
-       block->state = MBS_CREATED;
-       block->extra = NULL;
-       pthread_mutex_init(&block->mtx, NULL);
-       return block;
-}
-
-static MapBlock *create_block(MapSector *sector, size_t idx, v3s32 pos)
-{
-       MapBlock *block = allocate_block(pos);
-       array_insert(&sector->blocks, &block, idx);
-       return block;
-}
-
-static bool read_block_data(int fd, MapBlockData data)
-{
-       size_t n_read_total = 0;
-       int n_read;
-
-       while (n_read_total < sizeof(MapBlockData)) {
-               if ((n_read = read(fd, (char *) data + n_read_total, sizeof(MapBlockData) - n_read_total)) == -1) {
-                       perror("read");
-                       return false;
-               }
-
-               n_read_total += n_read;
-       }
-
-       return true;
-}
-
 Map *map_create()
 {
        Map *map = malloc(sizeof(Map));
@@ -63,13 +29,17 @@ Map *map_create()
        return map;
 }
 
-void map_delete(Map *map)
+void map_delete(Map *map, void (*callback)(void *extra))
 {
        pthread_rwlock_destroy(&map->rwlck);
        for (size_t s = 0; s < map->sectors.siz; s++) {
                MapSector *sector = map_get_sector_raw(map, s);
-               for (size_t b = 0; b < sector->blocks.siz; b++)
-                       map_free_block(map_get_block_raw(sector, b));
+               for (size_t b = 0; b < sector->blocks.siz; b++) {
+                       MapBlock *block = map_get_block_raw(sector, b);
+                       if (callback)
+                               callback(block->extra);
+                       map_free_block(block);
+               }
                if (sector->blocks.ptr)
                        free(sector->blocks.ptr);
                pthread_rwlock_destroy(&sector->rwlck);
@@ -141,7 +111,8 @@ MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
                if (block->state < MBS_READY && ! create)
                        block = NULL;
        } else if (create) {
-               block = create_block(sector, res.index, pos);
+               block = map_allocate_block(pos);
+               array_insert(&sector->blocks, &block, res.index);
        }
 
        pthread_rwlock_unlock(&sector->rwlck);
@@ -149,6 +120,16 @@ MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
        return block;
 }
 
+MapBlock *map_allocate_block(v3s32 pos)
+{
+       MapBlock *block = malloc(sizeof(MapBlock));
+       block->pos = pos;
+       block->state = MBS_CREATED;
+       block->extra = NULL;
+       pthread_mutex_init(&block->mtx, NULL);
+       return block;
+}
+
 void map_clear_meta(MapBlock *block)
 {
        pthread_mutex_lock(&block->mtx);
@@ -178,103 +159,49 @@ bool map_deserialize_node(int fd, MapNode *node)
        return true;
 }
 
-bool map_serialize_block(FILE *file, MapBlock *block)
+void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr)
 {
-       s32 pos[3] = {
-               htobe32(block->pos.x),
-               htobe32(block->pos.y),
-               htobe32(block->pos.z),
-       };
-       if (fwrite(pos, 1, sizeof(pos), file) != sizeof(pos))
-               return false;
+       *sizeptr = sizeof(MapBlockHeader) + sizeof(MapBlockData);
+       *dataptr = malloc(*sizeptr);
 
-       MapBlockData data;
+       MapBlockHeader header = htobe64(sizeof(MapBlockData));
+       memcpy(*dataptr, &header, sizeof(header));
 
+       MapBlockData blockdata;
        ITERATE_MAPBLOCK {
                MapNode node = block->data[x][y][z];
                node.type = htobe32(node.type);
-               data[x][y][z] = node;
+               blockdata[x][y][z] = node;
        }
-
-       if (fwrite(data, 1, sizeof(MapBlockData), file) !=  sizeof(MapBlockData))
-               perror("fwrite");
-       else
-               return true;
-
-       return false;
+       memcpy(*dataptr + sizeof(header), blockdata, sizeof(MapBlockData));
 }
 
-bool map_deserialize_block(int fd, Map *map, MapBlock **blockptr, bool dummy)
+bool map_deserialize_block(MapBlock *block, const char *data, size_t size)
 {
-       v3s32 pos;
-
-       if (! read_v3s32(fd, &pos))
+       if (size < sizeof(MapBlockData))
                return false;
 
-       MapBlock *block;
+       MapBlockData blockdata;
 
-       if (dummy)
-               block = allocate_block(pos);
-       else
-               block = map_get_block(map, pos, true);
-
-       if (block->state != MBS_CREATED)
-               map_clear_meta(block);
-
-       pthread_mutex_lock(&block->mtx);
-       block->state = MBS_INITIALIZING;
-
-       MapBlockData data;
-
-       bool success = read_block_data(fd, data);
+       memcpy(blockdata, data, sizeof(MapBlockData));
 
        ITERATE_MAPBLOCK {
-               if (success) {
-                       MapNode node = data[x][y][z];
-                       node.type = be32toh(node.type);
+               MapNode node = blockdata[x][y][z];
+               node.type = be32toh(node.type);
 
-                       if (node.type >= NODE_UNLOADED)
-                               node.type = NODE_INVALID;
+               if (node.type >= NODE_UNLOADED)
+                       node.type = NODE_INVALID;
 
-                       block->data[x][y][z] = node;
-               }
+               block->data[x][y][z] = node;
 
                block->metadata[x][y][z] = list_create(&list_compare_string);
        }
 
-       block->state = MBS_UNSENT;
-
-       if (dummy)
-               map_free_block(block);
-       else if (blockptr && success)
-               *blockptr = block;
+       block->state = MBS_MODIFIED;
 
-       pthread_mutex_unlock(&block->mtx);
-
-       return success;
-}
-
-bool map_serialize(FILE *file, Map *map)
-{
-       pthread_rwlock_rdlock(&map->rwlck);
-       for (size_t s = 0; s < map->sectors.siz; s++) {
-               MapSector *sector = map_get_sector_raw(map, s);
-               pthread_rwlock_rdlock(&sector->rwlck);
-               for (size_t b = 0; b < sector->blocks.siz; b++)
-                       if (! map_serialize_block(file, map_get_block_raw(sector, b)))
-                               return true;
-               pthread_rwlock_unlock(&sector->rwlck);
-       }
-       pthread_rwlock_unlock(&map->rwlck);
        return true;
 }
 
-void map_deserialize(int fd, Map *map)
-{
-       while (map_deserialize_block(fd, map, NULL, false))
-               ;
-}
-
 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
 {
        if (offset)
@@ -297,10 +224,11 @@ void map_set_node(Map *map, v3s32 pos, MapNode node)
        v3u8 offset;
        MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), false);
        if (block) {
+               pthread_mutex_lock(&block->mtx);
+               block->state = MBS_MODIFIED;
                list_clear(&block->metadata[offset.x][offset.y][offset.z]);
                block->data[offset.x][offset.y][offset.z] = node;
-
-               block->state = MBS_UNSENT;
+               pthread_mutex_unlock(&block->mtx);
        }
 }
 
index 64d7fbec2eab3693c29aea69ee09837fad8be3ee..44ccf90a1094bf08d774a406d77302a14e8a7e4d 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -3,7 +3,6 @@
 
 #include <stdbool.h>
 #include <pthread.h>
-#include <stdio.h>
 #include "array.h"
 #include "list.h"
 #include "node.h"
@@ -20,14 +19,15 @@ typedef struct
 typedef enum
 {
        MBS_CREATED,
-       MBS_INITIALIZING,
        MBS_READY,
-       MBS_UNSENT,
-       MBS_SENDING,
+       MBS_MODIFIED,
+       MBS_PROCESSING,
 } MapBlockState;
 
 typedef MapNode MapBlockData[16][16][16];
 
+typedef u64 MapBlockHeader;
+
 typedef struct
 {
        MapBlockData data;
@@ -53,20 +53,20 @@ typedef struct
 } Map;
 
 Map *map_create();
-void map_delete(Map *map);
+void map_delete(Map *map, void (*callback)(void *extra));
 
 MapSector *map_get_sector_raw(Map *map, size_t idx);
 MapBlock *map_get_block_raw(MapSector *sector, size_t idx);
 MapSector *map_get_sector(Map *map, v2s32 pos, bool create);
 MapBlock *map_get_block(Map *map, v3s32 pos, bool create);
 
+MapBlock *map_allocate_block(v3s32 pos);
+void map_clear_meta(MapBlock *block);
 void map_free_block(MapBlock *block);
 
 bool map_deserialize_node(int fd, MapNode *buf);
-bool map_serialize_block(FILE *file, MapBlock *block);
-bool map_deserialize_block(int fd, Map *map, MapBlock **blockptr, bool dummy);
-bool map_serialize(FILE *file, Map *map);
-void map_deserialize(int fd, Map *map);
+void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr);
+bool map_deserialize_block(MapBlock *block, const char *data, size_t size);
 
 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset);
 
diff --git a/src/mapdb.c b/src/mapdb.c
new file mode 100644 (file)
index 0000000..7d9c18c
--- /dev/null
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <endian.h>
+#include <stdlib.h>
+#include "mapdb.h"
+#include "servermap.h"
+
+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 *open_mapdb(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;
+}
+
+static sqlite3_stmt *prepare_statement(sqlite3 *db, MapBlock *block, const char *action, const char *sql)
+{
+       sqlite3_stmt *stmt;
+
+       if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
+               print_error(db, block, action);
+               return NULL;
+       }
+
+       size_t psize = sizeof(s32) * 3;
+       s32 *pos = malloc(psize);
+       pos[0] = htobe32(block->pos.x);
+       pos[1] = htobe32(block->pos.y);
+       pos[2] = htobe32(block->pos.z);
+
+       sqlite3_bind_blob(stmt, 1, pos, psize, &free);
+
+       return stmt;
+}
+
+bool load_block(sqlite3 *db, MapBlock *block)
+{
+       sqlite3_stmt *stmt;
+
+       if (! (stmt = prepare_statement(db, block, "loading", "SELECT 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), be64toh(*(MapBlockHeader *) data));
+       } else if (rc != SQLITE_DONE) {
+               print_error(db, block, "loading");
+       }
+
+       sqlite3_finalize(stmt);
+       return found;
+}
+
+void save_block(sqlite3 *db, MapBlock *block)
+{
+       sqlite3_stmt *stmt;
+
+       if (! (stmt = prepare_statement(db, block, "saving", "REPLACE INTO blocks (pos, data) VALUES(?1, ?2)")))
+               return;
+
+       MapBlockExtraData *extra = block->extra;
+
+       sqlite3_bind_blob(stmt, 2, extra->data, extra->size, SQLITE_TRANSIENT);
+
+       if (sqlite3_step(stmt) != SQLITE_DONE)
+               print_error(db, block, "saving");
+
+       sqlite3_finalize(stmt);
+}
diff --git a/src/mapdb.h b/src/mapdb.h
new file mode 100644 (file)
index 0000000..beb318b
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _MAPDB_H_
+#define _MAPDB_H_
+
+#include <sqlite3.h>
+#include <stdbool.h>
+#include "map.h"
+
+sqlite3 *open_mapdb(const char *path);
+bool load_block(sqlite3 *db, MapBlock *block);
+void save_block(sqlite3 *db, MapBlock *block);
+
+#endif
diff --git a/src/queue.c b/src/queue.c
new file mode 100644 (file)
index 0000000..2179098
--- /dev/null
@@ -0,0 +1,46 @@
+#include <stdlib.h>
+#include "queue.h"
+
+Queue *create_queue()
+{
+       Queue *queue = malloc(sizeof(Queue));
+       queue->list = list_create(NULL);
+       pthread_mutex_init(&queue->mtx, NULL);
+       return queue;
+}
+
+void delete_queue(Queue *queue)
+{
+       pthread_mutex_destroy(&queue->mtx);
+       list_clear(&queue->list);
+}
+
+void enqueue(Queue *queue, void *elem)
+{
+       pthread_mutex_lock(&queue->mtx);
+       list_put(&queue->list, elem, NULL);
+       pthread_mutex_unlock(&queue->mtx);
+}
+
+void *dequeue(Queue *queue)
+{
+       return dequeue_callback(queue, NULL);
+}
+
+void *dequeue_callback(Queue *queue, void (*callback)(void *elem))
+{
+       pthread_mutex_lock(&queue->mtx);
+       void *elem = NULL;
+       ListPair **lptr = &queue->list.first;
+       if (*lptr) {
+               elem = (*lptr)->key;
+               ListPair *next = (*lptr)->next;
+               free(*lptr);
+               *lptr = next;
+
+               if (callback)
+                       callback(elem);
+       }
+       pthread_mutex_unlock(&queue->mtx);
+       return elem;
+}
diff --git a/src/queue.h b/src/queue.h
new file mode 100644 (file)
index 0000000..d77f048
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef _QUEUE_H_
+#define _QUEUE_H_
+
+#include <pthread.h>
+#include "list.h"
+
+typedef struct
+{
+       List list;
+       pthread_mutex_t mtx;
+} Queue;
+
+Queue *create_queue();
+void delete_queue(Queue *queue);
+void enqueue(Queue *queue, void *elem);
+void *dequeue(Queue *queue);
+void *dequeue_callback(Queue *queue, void (*callback)(void *elem));
+
+#endif
index d90a558996b615866ab65cb3ebecbb4100eb4ab1..d6e3b6e4719d9da00f86b06a102300c5ef22edfd 100644 (file)
@@ -12,7 +12,6 @@ Server server;
 
 void server_disconnect_client(Client *client, int flags, const char *detail)
 {
-       ClientState cs = client->state;
        client->state = CS_DISCONNECTED;
 
        if (! (flags & DISCO_NO_REMOVE)) {
@@ -39,9 +38,6 @@ void server_disconnect_client(Client *client, int flags, const char *detail)
        if (! (flags & DISCO_NO_JOIN))
                pthread_join(client->net_thread, NULL);
 
-       if (cs == CS_ACTIVE)
-               pthread_join(client->map_thread, NULL);
-
        if (client->name != client->address)
                free(client->name);
        free(client->address);
@@ -84,6 +80,7 @@ static void server_accept_client()
        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);
 
        pthread_rwlock_wrlock(&server.clients_rwlck);
        list_put(&server.clients, client, NULL);
@@ -101,14 +98,6 @@ void server_start(int fd)
        pthread_rwlock_init(&server.players_rwlck, NULL);
        server.players = list_create(&list_compare_string);
 
-       FILE *mapfile = fopen("map", "r");
-       if (mapfile) {
-               map_deserialize(fileno(mapfile), server.map);
-               fclose(mapfile);
-       } else if (errno != ENOENT) {
-               perror("fopen");
-       }
-
        servermap_init(&server);
 
        while (! interrupted)
@@ -116,6 +105,8 @@ void server_start(int fd)
 
        printf("Shutting down\n");
 
+       servermap_deinit();
+
        pthread_rwlock_wrlock(&server.clients_rwlck);
        ITERATE_LIST(&server.clients, pair) server_disconnect_client(pair->key, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
        list_clear(&server.clients);
@@ -130,18 +121,7 @@ void server_start(int fd)
        shutdown(server.sockfd, SHUT_RDWR);
        close(server.sockfd);
 
-       mapfile = fopen("map", "w");
-       if (mapfile) {
-               if (map_serialize(mapfile, server.map))
-                       printf("Saved map\n");
-               else
-                       perror("map_serialize");
-               fclose(mapfile);
-       } else {
-               perror("fopen");
-       }
-
-       map_delete(server.map);
+       map_delete(server.map, &servermap_delete_extra_data);
 
        exit(EXIT_SUCCESS);
 }
index 0048c05de8b300a15e2e0b801111c1a39e92b82d..5d1f3cc20264478c8796183380350112c07402d0 100644 (file)
@@ -28,7 +28,7 @@ typedef struct Client
        char *name;
        Server *server;
        pthread_t net_thread;
-       pthread_t map_thread;
+       List sent_blocks;
        v3f pos;
 } Client;
 
index 328627774f2901cfe7c5d8662308b2d04a12e25d..c5d7f01b224c1c287176961f0acdb5e93b9c2be4 100644 (file)
@@ -25,14 +25,10 @@ static bool auth_handler(Client *client, bool good)
 
        pthread_rwlock_wrlock(&client->server->players_rwlck);
        u8 success = list_put(&client->server->players, name, client);
-       pthread_rwlock_unlock(&client->server->players_rwlck);
-
-       printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", client->address, name);
 
        if (success) {
                client->name = name;
                client->state = CS_ACTIVE;
-               servermap_add_client(client);
        } else {
                free(name);
        }
@@ -41,6 +37,10 @@ static bool auth_handler(Client *client, bool good)
        bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success);
        pthread_mutex_unlock(&client->mtx);
 
+       pthread_rwlock_unlock(&client->server->players_rwlck);
+
+       printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", client->address, name);
+
        return ret;
 }
 
index b33ef696b7b3586fa84d919547d8595f80834a7d..83a0b5876ec616aac7709c01b4e47eb0b592924b 100644 (file)
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <stdio.h>
 #include "facecache.h"
-#include "mapgen.h"
 #include "map.h"
+#include "mapdb.h"
+#include "mapgen.h"
 #include "servermap.h"
 
-static size_t max_blocks;
+static struct {
+       size_t max_blocks;
+       pthread_t thread;
+       sqlite3 *db;
+       bool cancel;
+} servermap;
 
 static Server *server = NULL;
 
-typedef struct
-{
-       char *ptr;
-       size_t size;
-} MapBlockBuffer;
-
-static void generate_block(MapBlock *block)
+static void initialize_block(MapBlock *block)
 {
-       pthread_mutex_lock(&block->mtx);
-       if (block->state == MBS_CREATED) {
-               block->state = MBS_INITIALIZING;
+       if (! load_block(servermap.db, block))
                mapgen_generate_block(block);
-               block->state = MBS_UNSENT;
-       }
-       pthread_mutex_unlock(&block->mtx);
+       block->state = MBS_MODIFIED;
 }
 
-static void serialize_block(Client *client, MapBlock *block)
+static void reset_block(MapBlock *block)
 {
-       MapBlockBuffer *buffer = block->extra;
+       MapBlockExtraData *extra = block->extra;
+
+       if (extra) {
+               free(extra->data);
+       } else {
+               extra = malloc(sizeof(MapBlockExtraData));
+               extra->clients = list_create(&list_compare_string);
+       }
 
-       if (! buffer) {
-               buffer = malloc(sizeof(MapBlockBuffer));
+       map_serialize_block(block, &extra->data, &extra->size);
 
-               FILE *buffile = open_memstream(&buffer->ptr, &buffer->size);
-               map_serialize_block(buffile, block);
-               fflush(buffile);
-               fclose(buffile);
+       block->extra = extra;
 
-               block->extra = buffer;
+       save_block(servermap.db, block);
+
+       ITERATE_LIST(&extra->clients, pair) {
+               Client *client = list_get(&server->players, pair->key);
+               if (client)
+                       list_delete(&client->sent_blocks, block);
+               free(pair->key);
        }
+       list_clear(&extra->clients);
+
+       block->state = MBS_READY;
+}
+
+static void send_block(Client *client, 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);
-       (void) (write_u32(client->fd, CC_BLOCK) && write(client->fd, buffer->ptr, buffer->size) != -1);
+       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);
 }
 
-static void send_block(MapBlock *block)
+static void reset_modified_blocks(Client *client)
 {
-       pthread_mutex_lock(&block->mtx);
-       if (block->state == MBS_UNSENT) {
-               block->state = MBS_SENDING;
-               if (block->extra) {
-                       free(((MapBlockBuffer *) block->extra)->ptr);
-                       free(block->extra);
-                       block->extra = NULL;
-               }
-               // ToDo: only send to near clients
-               pthread_rwlock_rdlock(&server->players_rwlck);
-               ITERATE_LIST(&server->players, pair) {
-                       if (block->state != MBS_SENDING)
-                               break;
-                       serialize_block(pair->value, block);
+       for (ListPair **pairptr = &client->sent_blocks.first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
+
+               MapBlock *block = (*pairptr)->key;
+
+               pthread_mutex_lock(&block->mtx);
+               if (block->state == MBS_MODIFIED) {
+                       reset_block(block);
+
+                       ListPair *next = (*pairptr)->next;
+                       free(*pairptr);
+                       *pairptr = next;
                }
-               pthread_rwlock_unlock(&server->players_rwlck);
-               if (block->state == MBS_SENDING)
-                       block->state = MBS_READY;
+               pthread_mutex_unlock(&block->mtx);
        }
-       pthread_mutex_unlock(&block->mtx);
 }
 
 static void send_blocks(Client *client)
 {
        v3s32 pos = map_node_to_block_pos((v3s32) {client->pos.x, client->pos.y, client->pos.z}, NULL);
-       for (size_t i = 0; i < max_blocks; i++) {
+
+       for (size_t i = 0; i < servermap.max_blocks; i++) {
                MapBlock *block = map_get_block(client->server->map, get_face(i, &pos), true);
+
+               pthread_mutex_lock(&block->mtx);
                switch (block->state) {
-               case MBS_CREATED:
-                       generate_block(block);
-                       __attribute__ ((fallthrough));
-               case MBS_UNSENT:
-                       send_block(block);
-                       __attribute__ ((fallthrough));
-               case MBS_INITIALIZING:
-               case MBS_SENDING:
-                       return;
-               case MBS_READY:
-                       break;
+                       case MBS_CREATED:
+
+                               initialize_block(block);
+
+                       __attribute__ ((fallthrough)); case MBS_MODIFIED:
+
+                               reset_block(block);
+
+                               send:
+                               send_block(client, block);
+
+                               pthread_mutex_unlock(&block->mtx);
+                               return;
+
+                       case MBS_READY:
+
+                               if (! list_get(&client->sent_blocks, block))
+                                       goto send;
+                               else
+                                       break;
+
+                       default:
+
+                               break;
                }
+               pthread_mutex_unlock(&block->mtx);
        }
 }
 
-static void *map_thread(void *cli)
+static void map_step()
 {
-       Client *client = cli;
+       pthread_rwlock_rdlock(&server->players_rwlck);
+       ITERATE_LIST(&server->players, pair) reset_modified_blocks(pair->value);
+       ITERATE_LIST(&server->players, pair) send_blocks(pair->value);
+       pthread_rwlock_unlock(&server->players_rwlck);
+}
 
-       while (client->state != CS_DISCONNECTED) {
-               send_blocks(client);
-               sched_yield();
-       }
+static void *map_thread(void *unused)
+{
+       (void) unused;
+
+       servermap.db = open_mapdb("map.sqlite");
+
+       while (! servermap.cancel)
+               map_step();
 
        return NULL;
 }
@@ -108,10 +144,26 @@ static void *map_thread(void *cli)
 void servermap_init(Server *srv)
 {
        server = srv;
-       max_blocks = get_face_count(3);
+       servermap.max_blocks = get_face_count(3);
+
+       pthread_create(&servermap.thread, NULL, &map_thread, NULL);
+}
+
+void servermap_delete_extra_data(void *ext)
+{
+       MapBlockExtraData *extra = ext;
+
+       if (extra) {
+               ITERATE_LIST(&extra->clients, pair) free(pair->key);
+               list_clear(&extra->clients);
+               free(extra->data);
+               free(extra);
+       }
 }
 
-void servermap_add_client(Client *client)
+void servermap_deinit()
 {
-       pthread_create(&client->map_thread, NULL, &map_thread, client);
+       servermap.cancel = true;
+       pthread_join(servermap.thread, NULL);
+       sqlite3_close(servermap.db);
 }
index 7fc27f6a56c928e239820e92b413bc657c78b7e3..b005b9125aa2d4d7e949723a9a40bc394c77bdd2 100644 (file)
@@ -3,8 +3,15 @@
 
 #include "server.h"
 
-void servermap_init(Server *srv);
+typedef struct
+{
+       List clients;
+       char *data;
+       size_t size;
+} MapBlockExtraData;
 
-void servermap_add_client(Client *client);
+void servermap_init(Server *srv);
+void servermap_deinit();
+void servermap_delete_extra_data(void *ext);
 
 #endif