X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver%2Fdatabase.c;h=79d1e59e1e43cf0d19f1423f203c52c7ec8e09cd;hb=9b70226963294e2b2c7e2cc95a246691ca620820;hp=c1ca01b2f8972607c56725f27194de4352b8c0b2;hpb=0062d353371490454adc1de07ceb3bcbaa29c345;p=dragonblocks_alpha.git diff --git a/src/server/database.c b/src/server/database.c index c1ca01b..79d1e59 100644 --- a/src/server/database.c +++ b/src/server/database.c @@ -1,216 +1,235 @@ -#include #include +#include #include #include #include #include +#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); }