2 #include <endian.h/endian.h>
8 #include "server/database.h"
9 #include "server/server_map.h"
13 static sqlite3 *map_database;
14 static sqlite3 *meta_database;
15 static sqlite3 *players_database;
19 // prepare a SQLite3 statement
20 static inline sqlite3_stmt *prepare_statement(sqlite3 *database, const char *sql)
23 return sqlite3_prepare_v2(database, sql, -1, &stmt, NULL) == SQLITE_OK ? stmt : NULL;
26 // print SQLite3 error message for failed block SQL statement
27 static inline void print_block_error(MapBlock *block, const char *action)
29 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(map_database));
32 // prepare a SQLite3 block statement and bind the position
33 static sqlite3_stmt *prepare_block_statement(MapBlock *block, const char *action, const char *sql)
37 if (! (stmt = prepare_statement(map_database, sql))) {
38 print_block_error(block, action);
42 Blob buffer = {0, NULL};
43 v3s32_write(&buffer, &block->pos);
45 sqlite3_bind_blob(stmt, 1, buffer.data, buffer.siz, &free);
50 // bind v3f64 to sqlite3 statement
51 static inline void bind_v3f64(sqlite3_stmt *stmt, int idx, v3f64 pos)
53 Blob buffer = {0, NULL};
54 v3f64_write(&buffer, &pos);
56 sqlite3_bind_blob(stmt, idx, buffer.data, buffer.siz, &free);
61 // open and initialize SQLite3 databases
69 {&map_database, "map.sqlite", "CREATE TABLE IF NOT EXISTS map (pos BLOB PRIMARY KEY, generated INTEGER, data BLOB, mgsb BLOB);"},
70 {&meta_database, "meta.sqlite", "CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value INTEGER );"},
71 {&players_database, "players.sqlite", "CREATE TABLE IF NOT EXISTS players (name TEXT PRIMARY KEY, pos BLOB );"},
74 for (int i = 0; i < 3; i++) {
75 if (sqlite3_open_v2(databases[i].path, databases[i].handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
76 fprintf(stderr, "Failed to open %s: %s\n", databases[i].path, sqlite3_errmsg(*databases[i].handle));
81 if (sqlite3_exec(*databases[i].handle, databases[i].init, NULL, NULL, &err) != SQLITE_OK) {
82 fprintf(stderr, "Failed to initialize %s: %s\n", databases[i].path, err);
90 if (database_load_meta("seed", &saved_seed)) {
95 database_save_meta("seed", seed);
100 if (database_load_meta("time_of_day", &time_of_day))
101 set_time_of_day(time_of_day);
103 set_time_of_day(12 * MINUTES_PER_HOUR);
109 void database_deinit()
111 database_save_meta("time_of_day", (s64) get_time_of_day());
113 sqlite3_close(map_database);
114 sqlite3_close(meta_database);
115 sqlite3_close(players_database);
118 // load a block from map database (initializes state, mgs buffer and data), returns false on failure
119 bool database_load_block(MapBlock *block)
123 if (! (stmt = prepare_block_statement(block, "loading", "SELECT generated, data, mgsb FROM map WHERE pos=?")))
126 int rc = sqlite3_step(stmt);
127 bool found = rc == SQLITE_ROW;
130 MapBlockExtraData *extra = block->extra;
132 extra->state = sqlite3_column_int(stmt, 0) ? MBS_READY : MBS_CREATED;
133 Blob_read( &(Blob) {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)}, &extra->data);
134 MapgenStageBuffer_read(&(Blob) {sqlite3_column_bytes(stmt, 2), (void *) sqlite3_column_blob(stmt, 2)}, &extra->mgsb);
136 if (! map_deserialize_block(block, extra->data)) {
137 fprintf(stderr, "Failed to load block at (%d, %d, %d)\n", block->pos.x, block->pos.y, block->pos.z);
140 } else if (rc != SQLITE_DONE) {
141 print_block_error(block, "loading");
144 sqlite3_finalize(stmt);
148 // save a block to database
149 void database_save_block(MapBlock *block)
153 if (! (stmt = prepare_block_statement(block, "saving", "REPLACE INTO map (pos, generated, data, mgsb) VALUES(?1, ?2, ?3, ?4)")))
156 MapBlockExtraData *extra = block->extra;
158 Blob data = {0, NULL};
159 Blob_write(&data, &extra->data);
161 Blob mgsb = {0, NULL};
162 MapgenStageBuffer_write(&mgsb, &extra->mgsb);
164 sqlite3_bind_int(stmt, 2, extra->state > MBS_CREATED);
165 sqlite3_bind_blob(stmt, 3, data.data, data.siz, &free);
166 sqlite3_bind_blob(stmt, 4, mgsb.data, mgsb.siz, &free);
168 if (sqlite3_step(stmt) != SQLITE_DONE)
169 print_block_error(block, "saving");
171 sqlite3_finalize(stmt);
175 bool database_load_meta(const char *key, s64 *value_ptr)
179 if (! (stmt = prepare_statement(meta_database, "SELECT value FROM meta WHERE key=?"))) {
180 fprintf(stderr, "Database error with loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
184 sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
186 int rc = sqlite3_step(stmt);
187 bool found = rc == SQLITE_ROW;
190 *value_ptr = sqlite3_column_int64(stmt, 0);
191 else if (rc != SQLITE_DONE)
192 fprintf(stderr, "Database error with loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
194 sqlite3_finalize(stmt);
198 // save / update a meta entry
199 void database_save_meta(const char *key, s64 value)
203 if (! (stmt = prepare_statement(meta_database, "REPLACE INTO meta (key, value) VALUES(?1, ?2)"))) {
204 fprintf(stderr, "Database error with saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
208 sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
209 sqlite3_bind_int64(stmt, 2, value);
211 if (sqlite3_step(stmt) != SQLITE_DONE)
212 fprintf(stderr, "Database error with saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
214 sqlite3_finalize(stmt);
217 // load player data from database
218 bool database_load_player(char *name, v3f64 *pos_ptr)
222 if (! (stmt = prepare_statement(players_database, "SELECT pos FROM players WHERE name=?"))) {
223 fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(players_database));
227 sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
229 int rc = sqlite3_step(stmt);
230 bool found = rc == SQLITE_ROW;
233 v3f64_read(&(Blob) {sqlite3_column_bytes(stmt, 0), (void *) sqlite3_column_blob(stmt, 0)}, pos_ptr);
234 else if (rc != SQLITE_DONE)
235 fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(players_database));
237 sqlite3_finalize(stmt);
241 // insert new player into database
242 void database_create_player(char *name, v3f64 pos)
246 if (! (stmt = prepare_statement(players_database, "INSERT INTO players (name, pos) VALUES(?1, ?2)"))) {
247 fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(players_database));
251 sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
252 bind_v3f64(stmt, 2, pos);
254 if (sqlite3_step(stmt) != SQLITE_DONE)
255 fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(players_database));
257 sqlite3_finalize(stmt);
260 // update player position
261 void database_update_player_pos(char *name, v3f64 pos)
265 if (! (stmt = prepare_statement(players_database, "UPDATE players SET pos=?1 WHERE name=?2"))) {
266 fprintf(stderr, "Database error with updating position of player %s: %s\n", name, sqlite3_errmsg(players_database));
270 bind_v3f64(stmt, 1, pos);
271 sqlite3_bind_text(stmt, 2, name, strlen(name), SQLITE_TRANSIENT);
273 if (sqlite3_step(stmt) != SQLITE_DONE)
274 fprintf(stderr, "Database error with updating player %s position: %s\n", name, sqlite3_errmsg(players_database));
276 sqlite3_finalize(stmt);