]> git.lizzy.rs Git - dragonblocks_alpha.git/blobdiff - src/server/database.c
Use correct u64 printf fmt
[dragonblocks_alpha.git] / src / server / database.c
index c1ca01b2f8972607c56725f27194de4352b8c0b2..79d1e59e1e43cf0d19f1423f203c52c7ec8e09cd 100644 (file)
-#include <stdio.h>
 #include <endian.h/endian.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sqlite3.h>
 #include <time.h>
+#include "day.h"
 #include "server/database.h"
-#include "server/server_map.h"
+#include "server/server_node.h"
+#include "server/server_terrain.h"
 #include "perlin.h"
-#include "util.h"
 
-static sqlite3 *database;
+static sqlite3 *terrain_database;
+static sqlite3 *meta_database;
+static sqlite3 *players_database;
 
 // utility functions
 
 // prepare a SQLite3 statement
-static inline sqlite3_stmt *prepare_statement(const char *sql)
+static inline sqlite3_stmt *prepare_statement(sqlite3 *database, const char *sql)
 {
        sqlite3_stmt *stmt;
        return sqlite3_prepare_v2(database, sql, -1, &stmt, NULL) == SQLITE_OK ? stmt : NULL;
 }
 
-// print SQLite3 error message for failed block SQL statement
-static inline void print_block_error(MapBlock *block, const char *action)
+// print SQLite3 error message for failed chunk SQL statement
+static inline void print_chunk_error(TerrainChunk *chunk, const char *action)
 {
-       fprintf(stderr, "Database error with %s block at (%d, %d, %d): %s\n", action, block->pos.x, block->pos.y, block->pos.z, sqlite3_errmsg(database));
+       fprintf(stderr, "[warning] failed %s chunk at (%d, %d, %d): %s\n", action, chunk->pos.x, chunk->pos.y, chunk->pos.z, sqlite3_errmsg(terrain_database));
 }
 
-// prepare a SQLite3 block statement and bind the position
-static sqlite3_stmt *prepare_block_statement(MapBlock *block, const char *action, const char *sql)
+// prepare a SQLite3 chunk statement and bind the position
+static sqlite3_stmt *prepare_chunk_statement(TerrainChunk *chunk, const char *action, const char *sql)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(terrain_database, sql);
 
-       if (! (stmt = prepare_statement(sql))) {
-               print_block_error(block, action);
+       if (!stmt) {
+               print_chunk_error(chunk, action);
                return NULL;
        }
 
-       size_t psize = sizeof(u32) * 3;
-       u32 *pos = malloc(psize);
-       pos[0] = htobe32(block->pos.x);
-       pos[1] = htobe32(block->pos.y);
-       pos[2] = htobe32(block->pos.z);
+       Blob buffer = {0, NULL};
+       v3s32_write(&buffer, &chunk->pos);
 
-       sqlite3_bind_blob(stmt, 1, pos, psize, &free);
+       sqlite3_bind_blob(stmt, 1, buffer.data, buffer.siz, &free);
 
        return stmt;
 }
 
-// load something from meta
-static bool load_meta(const char *key, int *value_ptr)
+// bind v3f64 to sqlite3 statement
+static inline void bind_v3f64(sqlite3_stmt *stmt, int idx, v3f64 pos)
 {
-       sqlite3_stmt *stmt;
-
-       if (! (stmt = prepare_statement("SELECT value FROM meta WHERE key=?"))) {
-               fprintf(stderr, "Database error with loading %s: %s\n", key, sqlite3_errmsg(database));
-               return false;
-       }
-
-       sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
-
-       int rc = sqlite3_step(stmt);
-       bool found = rc == SQLITE_ROW;
-
-       if (found)
-               *value_ptr = sqlite3_column_int(stmt, 0);
-       else if (rc != SQLITE_DONE)
-               fprintf(stderr, "Database error with loading %s: %s\n", key, sqlite3_errmsg(database));
+       Blob buffer = {0, NULL};
+       v3f64_write(&buffer, &pos);
 
-       sqlite3_finalize(stmt);
-       return found;
+       sqlite3_bind_blob(stmt, idx, buffer.data, buffer.siz, &free);
 }
 
-// save something to meta
-static void save_meta(const char *key, int value)
+// bind v3f32 to sqlite3 statement
+static inline void bind_v3f32(sqlite3_stmt *stmt, int idx, v3f32 pos)
 {
-       sqlite3_stmt *stmt;
+       Blob buffer = {0, NULL};
+       v3f32_write(&buffer, &pos);
 
-       if (! (stmt = prepare_statement("REPLACE INTO meta (key, value) VALUES(?1, ?2)"))) {
-               fprintf(stderr, "Database error with saving %s: %s\n", key, sqlite3_errmsg(database));
-               return;
-       }
-
-       sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
-       sqlite3_bind_int(stmt, 2, value);
-
-       if (sqlite3_step(stmt) != SQLITE_DONE)
-               fprintf(stderr, "Database error with saving %s: %s\n", key, sqlite3_errmsg(database));
-
-       sqlite3_finalize(stmt);
-}
-
-// bind v3f64 to sqlite3 statement
-static inline void bind_v3f64(sqlite3_stmt *stmt, int idx, v3f64 pos)
-{
-       size_t psize = sizeof(f64) * 3;
-       f64 *blob = malloc(psize);
-       blob[0] = pos.x;
-       blob[1] = pos.y;
-       blob[2] = pos.z;
-       sqlite3_bind_blob(stmt, idx, blob, psize, &free);
+       sqlite3_bind_blob(stmt, idx, buffer.data, buffer.siz, &free);
 }
 
 // public functions
 
-// open and initialize world SQLite3 database
-void database_init()
+// open and initialize SQLite3 databases
+bool database_init()
 {
-       char *err;
-
-       if (sqlite3_open_v2("world.sqlite", &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
-               fprintf(stderr, "Failed to open database: %s\n", sqlite3_errmsg(database));
-               return;
-       }
-
-       const char *init_stmts[3]= {
-               "CREATE TABLE IF NOT EXISTS map (pos BLOB PRIMARY KEY, generated INTEGER, size INTEGER, data BLOB, mgsb_size INTEGER, mgsb_data BLOB);",
-               "CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value INTEGER);",
-               "CREATE TABLE IF NOT EXISTS players (name TEXT PRIMARY KEY, pos BLOB);"
+       struct {
+               sqlite3 **handle;
+               const char *path;
+               const char *init;
+       } databases[3] = {
+               {&terrain_database, "terrain.sqlite", "CREATE TABLE IF NOT EXISTS terrain (pos  BLOB PRIMARY KEY, generated INTEGER, data BLOB, tgsb BLOB);"},
+               {&meta_database,    "meta.sqlite",    "CREATE TABLE IF NOT EXISTS meta    (key  TEXT PRIMARY KEY, value INTEGER                          );"},
+               {&players_database, "players.sqlite", "CREATE TABLE IF NOT EXISTS players (name TEXT PRIMARY KEY, pos BLOB, rot BLOB                     );"},
        };
 
        for (int i = 0; i < 3; i++) {
-               if (sqlite3_exec(database, init_stmts[i], NULL, NULL, &err) != SQLITE_OK) {
-                       fprintf(stderr, "Failed to initialize database: %s\n", err);
+               if (sqlite3_open_v2(databases[i].path, databases[i].handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
+                       fprintf(stderr, "[error] failed to open %s: %s\n", databases[i].path, sqlite3_errmsg(*databases[i].handle));
+                       return false;
+               }
+
+               char *err;
+               if (sqlite3_exec(*databases[i].handle, databases[i].init, NULL, NULL, &err) != SQLITE_OK) {
+                       fprintf(stderr, "[error] failed initializing %s: %s\n", databases[i].path, err);
                        sqlite3_free(err);
-                       return;
+                       return false;
                }
        }
 
-       if (! load_meta("seed", &seed)) {
-               srand(time(0));
+       s64 saved_seed;
+
+       if (database_load_meta("seed", &saved_seed)) {
+               seed = saved_seed;
+       } else {
+               srand(time(NULL));
                seed = rand();
-               save_meta("seed", seed);
+               database_save_meta("seed", seed);
        }
+
+       s64 time_of_day;
+
+       if (database_load_meta("time_of_day", &time_of_day))
+               set_time_of_day(time_of_day);
+       else
+               set_time_of_day(12 * MINUTES_PER_HOUR);
+
+       return true;
 }
 
-// close database
+// close databases
 void database_deinit()
 {
-       sqlite3_close(database);
+       database_save_meta("time_of_day", (s64) get_time_of_day());
+
+       sqlite3_close(terrain_database);
+       sqlite3_close(meta_database);
+       sqlite3_close(players_database);
 }
 
-// load a block from map database (initializes state, mgs buffer and data), returns false on failure
-bool database_load_block(MapBlock *block)
+// load a chunk from terrain database (initializes state, tgs buffer and data), returns false on failure
+bool database_load_chunk(TerrainChunk *chunk)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_chunk_statement(chunk, "loading", "SELECT generated, data, tgsb FROM terrain WHERE pos=?");
 
-       if (! (stmt = prepare_block_statement(block, "loading", "SELECT generated, size, data, mgsb_size, mgsb_data FROM map WHERE pos=?")))
+       if (!stmt)
                return false;
 
        int rc = sqlite3_step(stmt);
        bool found = rc == SQLITE_ROW;
 
        if (found) {
-               MapBlockExtraData *extra = block->extra;
+               TerrainChunkMeta *meta = chunk->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);
+               meta->state = sqlite3_column_int(stmt, 0) ? CHUNK_STATE_READY : CHUNK_STATE_CREATED;
+               Blob data = {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)};
+               Blob tgsb = {sqlite3_column_bytes(stmt, 2), (void *) sqlite3_column_blob(stmt, 2)};
 
-               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);
+               TerrainGenStageBuffer_read(&tgsb, &meta->tgsb);
+               if (!terrain_deserialize_chunk(server_terrain, chunk, data, &server_node_deserialize)) {
+                       fprintf(stderr, "[error] failed deserializing chunk at (%d, %d, %d)\n", chunk->pos.x, chunk->pos.y, chunk->pos.z);
+                       abort();
+               }
        } else if (rc != SQLITE_DONE) {
-               print_block_error(block, "loading");
+               print_chunk_error(chunk, "loading");
        }
 
        sqlite3_finalize(stmt);
        return found;
 }
 
-// save a block to database
-void database_save_block(MapBlock *block)
+// save a chunk to terrain database
+void database_save_chunk(TerrainChunk *chunk)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_chunk_statement(chunk, "saving", "REPLACE INTO terrain (pos, generated, data, tgsb) VALUES(?1, ?2, ?3, ?4)");
 
-       if (! (stmt = prepare_block_statement(block, "saving", "REPLACE INTO map (pos, generated, size, data, mgsb_size, mgsb_data) VALUES(?1, ?2, ?3, ?4, ?5, ?6)")))
+       if (!stmt)
                return;
 
-       MapBlockExtraData *extra = block->extra;
+       TerrainChunkMeta *meta = chunk->extra;
+
+       Blob data = terrain_serialize_chunk(server_terrain, chunk, &server_node_serialize);
+
+       Blob tgsb = {0, NULL};
+       TerrainGenStageBuffer_write(&tgsb, &meta->tgsb);
+
+       sqlite3_bind_int(stmt, 2, meta->state > CHUNK_STATE_CREATED);
+       sqlite3_bind_blob(stmt, 3, data.data, data.siz, &free);
+       sqlite3_bind_blob(stmt, 4, tgsb.data, tgsb.siz, &free);
+
+       if (sqlite3_step(stmt) != SQLITE_DONE)
+               print_chunk_error(chunk, "saving");
+
+       sqlite3_finalize(stmt);
+}
+
+// load a meta entry
+bool database_load_meta(const char *key, s64 *value_ptr)
+{
+       sqlite3_stmt *stmt = prepare_statement(meta_database, "SELECT value FROM meta WHERE key=?");
+
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
+               return false;
+       }
+
+       sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
+
+       int rc = sqlite3_step(stmt);
+       bool found = rc == SQLITE_ROW;
+
+       if (found)
+               *value_ptr = sqlite3_column_int64(stmt, 0);
+       else if (rc != SQLITE_DONE)
+               fprintf(stderr, "[warning] failed loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
 
-       MapgenStageBuffer uncompressed_mgsb;
-       ITERATE_MAPBLOCK uncompressed_mgsb[x][y][z] = htobe32(extra->mgs_buffer[x][y][z]);
+       sqlite3_finalize(stmt);
+       return found;
+}
 
-       char *mgsb_data;
-       size_t mgsb_size;
+// save / update a meta entry
+void database_save_meta(const char *key, s64 value)
+{
+       sqlite3_stmt *stmt = prepare_statement(meta_database, "REPLACE INTO meta (key, value) VALUES(?1, ?2)");
 
-       my_compress(&uncompressed_mgsb, sizeof(MapgenStageBuffer), &mgsb_data, &mgsb_size);
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
+               return;
+       }
 
-       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);
+       sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
+       sqlite3_bind_int64(stmt, 2, value);
 
        if (sqlite3_step(stmt) != SQLITE_DONE)
-               print_block_error(block, "saving");
+               fprintf(stderr, "[warning] failed saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
 
        sqlite3_finalize(stmt);
 }
 
 // load player data from database
-bool database_load_player(char *name, v3f64 *pos_ptr)
+bool database_load_player(char *name, v3f64 *pos, v3f32 *rot)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(players_database, "SELECT pos, rot FROM players WHERE name=?");
 
-       if (! (stmt = prepare_statement("SELECT pos FROM players WHERE name=?"))) {
-               fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(database));
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed loading player %s: %s\n", name, sqlite3_errmsg(players_database));
                return false;
        }
 
@@ -220,10 +239,10 @@ bool database_load_player(char *name, v3f64 *pos_ptr)
        bool found = rc == SQLITE_ROW;
 
        if (found) {
-               const f64 *pos = sqlite3_column_blob(stmt, 0);
-               *pos_ptr = (v3f64) {pos[0], pos[1], pos[2]};
+               v3f64_read(&(Blob) {sqlite3_column_bytes(stmt, 0), (void *) sqlite3_column_blob(stmt, 0)}, pos);
+               v3f32_read(&(Blob) {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)}, rot);
        } else if (rc != SQLITE_DONE) {
-               fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(database));
+               fprintf(stderr, "[warning] failed loading player %s: %s\n", name, sqlite3_errmsg(players_database));
        }
 
        sqlite3_finalize(stmt);
@@ -231,39 +250,41 @@ bool database_load_player(char *name, v3f64 *pos_ptr)
 }
 
 // insert new player into database
-void database_create_player(char *name, v3f64 pos)
+void database_create_player(char *name, v3f64 pos, v3f32 rot)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(players_database, "INSERT INTO players (name, pos, rot) VALUES(?1, ?2, ?3)");
 
-       if (! (stmt = prepare_statement("INSERT INTO players (name, pos) VALUES(?1, ?2)"))) {
-               fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(database));
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed creating player %s: %s\n", name, sqlite3_errmsg(players_database));
                return;
        }
 
        sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
        bind_v3f64(stmt, 2, pos);
+       bind_v3f32(stmt, 3, rot);
 
        if (sqlite3_step(stmt) != SQLITE_DONE)
-               fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(database));
+               fprintf(stderr, "[warning] failed creating player %s: %s\n", name, sqlite3_errmsg(players_database));
 
        sqlite3_finalize(stmt);
 }
 
 // update player position
-void database_update_player_pos(char *name, v3f64 pos)
+void database_update_player_pos_rot(char *name, v3f64 pos, v3f32 rot)
 {
-       sqlite3_stmt *stmt;
+       sqlite3_stmt *stmt = prepare_statement(players_database, "UPDATE players SET pos=?1, rot=?2 WHERE name=?3");
 
-       if (! (stmt = prepare_statement("UPDATE players SET pos=?1 WHERE name=?2"))) {
-               fprintf(stderr, "Database error with updating player %s position: %s\n", name, sqlite3_errmsg(database));
+       if (!stmt) {
+               fprintf(stderr, "[warning] failed updating player %s: %s\n", name, sqlite3_errmsg(players_database));
                return;
        }
 
        bind_v3f64(stmt, 1, pos);
-       sqlite3_bind_text(stmt, 2, name, strlen(name), SQLITE_TRANSIENT);
+       bind_v3f32(stmt, 2, rot);
+       sqlite3_bind_text(stmt, 3, name, strlen(name), SQLITE_TRANSIENT);
 
        if (sqlite3_step(stmt) != SQLITE_DONE)
-               fprintf(stderr, "Database error with updating player %s position: %s\n", name, sqlite3_errmsg(database));
+               fprintf(stderr, "[warning] failed updating player %s: %s\n", name, sqlite3_errmsg(players_database));
 
        sqlite3_finalize(stmt);
 }