- 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
- 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
*.o
DragonblocksServer
Dragonblocks
-map
+map.sqlite
DragonblocksAlpha-*.zip
## 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
```
The debug flag (`-g`) is set by default (RELEASE=TRUE will disable it).
## Release
+
```bash
./snapshot.sh
```
# Dragonblocks alpha
-A multiplayer voxelgame for POSIX.1-2008 systems.
+A multiplayer voxelgame for POSIX systems.
## Usage
+
```bash
./DragonblocksServer <port>
./Dragonblocks <address> <port>
```bash
sudo apt install libgl1-mesa-dri libglfw3 libglew2.1
```
+
+The server depends on SQLite3.
+
+```bash
+sudo apt install libsqlite3-0
+```
-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
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 $@ $<
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef _BLOCKMESH_H_
+#define _BLOCKMESH_H_
+
+#include "map.h"
+#include "scene.h"
+
+void make_block_mesh(MapBlock *block, Scene *scene);
+
+#endif
clientmap_deinit();
- map_delete(client.map);
+ map_delete(client.map, NULL);
scene_delete(client.scene);
pthread_mutex_destroy(&client.mtx);
#include <stdio.h>
+#include <unistd.h>
#include "client.h"
#include "clientmap.h"
#include "types.h"
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] = {
#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;
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);
}
{
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);
}
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(§or->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));
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(§or->rwlck);
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(§or->blocks, &block, res.index);
}
pthread_rwlock_unlock(§or->rwlck);
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);
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(§or->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(§or->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)
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);
}
}
#include <stdbool.h>
#include <pthread.h>
-#include <stdio.h>
#include "array.h"
#include "list.h"
#include "node.h"
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;
} 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);
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
void server_disconnect_client(Client *client, int flags, const char *detail)
{
- ClientState cs = client->state;
client->state = CS_DISCONNECTED;
if (! (flags & DISCO_NO_REMOVE)) {
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);
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);
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)
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);
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);
}
char *name;
Server *server;
pthread_t net_thread;
- pthread_t map_thread;
+ List sent_blocks;
v3f pos;
} Client;
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);
}
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;
}
#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;
}
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);
}
#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