7 #include "common/day.h"
8 #include "common/perlin.h"
9 #include "server/database.h"
10 #include "server/server_node.h"
11 #include "server/server_terrain.h"
13 static sqlite3 *terrain_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 chunk SQL statement
27 static inline void print_chunk_error(TerrainChunk *chunk, const char *action)
29 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));
32 // prepare a SQLite3 chunk statement and bind the position
33 static sqlite3_stmt *prepare_chunk_statement(TerrainChunk *chunk, const char *action, const char *sql)
35 sqlite3_stmt *stmt = prepare_statement(terrain_database, sql);
38 print_chunk_error(chunk, action);
42 Blob buffer = {0, NULL};
43 v3s32_write(&buffer, &chunk->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);
59 // bind v3f32 to sqlite3 statement
60 static inline void bind_v3f32(sqlite3_stmt *stmt, int idx, v3f32 pos)
62 Blob buffer = {0, NULL};
63 v3f32_write(&buffer, &pos);
65 sqlite3_bind_blob(stmt, idx, buffer.data, buffer.siz, &free);
70 // open and initialize SQLite3 databases
78 {&terrain_database, "terrain.sqlite", "CREATE TABLE IF NOT EXISTS terrain (pos BLOB PRIMARY KEY, generated INTEGER, data BLOB, tgsb BLOB);"},
79 {&meta_database, "meta.sqlite", "CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value INTEGER );"},
80 {&players_database, "players.sqlite", "CREATE TABLE IF NOT EXISTS players (name TEXT PRIMARY KEY, pos BLOB, rot BLOB );"},
83 for (int i = 0; i < 3; i++) {
84 if (sqlite3_open_v2(databases[i].path, databases[i].handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
85 fprintf(stderr, "[error] failed to open %s: %s\n", databases[i].path, sqlite3_errmsg(*databases[i].handle));
90 if (sqlite3_exec(*databases[i].handle, databases[i].init, NULL, NULL, &err) != SQLITE_OK) {
91 fprintf(stderr, "[error] failed initializing %s: %s\n", databases[i].path, err);
99 if (database_load_meta("seed", &saved_seed)) {
104 database_save_meta("seed", seed);
109 if (database_load_meta("time_of_day", &time_of_day))
110 set_time_of_day(time_of_day);
112 set_time_of_day(12 * MINUTES_PER_HOUR);
116 void database_deinit()
118 database_save_meta("time_of_day", (s64) get_time_of_day());
120 sqlite3_close(terrain_database);
121 sqlite3_close(meta_database);
122 sqlite3_close(players_database);
125 // load a chunk from terrain database (initializes state, tgs buffer and data), returns false on failure
126 bool database_load_chunk(TerrainChunk *chunk)
128 sqlite3_stmt *stmt = prepare_chunk_statement(chunk, "loading", "SELECT generated, data, tgsb FROM terrain WHERE pos=?");
133 int rc = sqlite3_step(stmt);
134 bool found = rc == SQLITE_ROW;
137 TerrainChunkMeta *meta = chunk->extra;
139 meta->state = sqlite3_column_int(stmt, 0) ? CHUNK_STATE_READY : CHUNK_STATE_CREATED;
140 Blob data = {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)};
141 Blob tgsb = {sqlite3_column_bytes(stmt, 2), (void *) sqlite3_column_blob(stmt, 2)};
143 TerrainGenStageBuffer_read(&tgsb, &meta->tgsb);
144 if (!terrain_deserialize_chunk(server_terrain, chunk, data, &server_node_deserialize)) {
145 fprintf(stderr, "[error] failed deserializing chunk at (%d, %d, %d)\n", chunk->pos.x, chunk->pos.y, chunk->pos.z);
148 } else if (rc != SQLITE_DONE) {
149 print_chunk_error(chunk, "loading");
152 sqlite3_finalize(stmt);
156 // save a chunk to terrain database
157 void database_save_chunk(TerrainChunk *chunk)
159 sqlite3_stmt *stmt = prepare_chunk_statement(chunk, "saving", "REPLACE INTO terrain (pos, generated, data, tgsb) VALUES(?1, ?2, ?3, ?4)");
164 TerrainChunkMeta *meta = chunk->extra;
166 Blob data = terrain_serialize_chunk(server_terrain, chunk, &server_node_serialize);
168 Blob tgsb = {0, NULL};
169 TerrainGenStageBuffer_write(&tgsb, &meta->tgsb);
171 sqlite3_bind_int(stmt, 2, meta->state > CHUNK_STATE_CREATED);
172 sqlite3_bind_blob(stmt, 3, data.data, data.siz, &free);
173 sqlite3_bind_blob(stmt, 4, tgsb.data, tgsb.siz, &free);
175 if (sqlite3_step(stmt) != SQLITE_DONE)
176 print_chunk_error(chunk, "saving");
178 sqlite3_finalize(stmt);
182 bool database_load_meta(const char *key, s64 *value_ptr)
184 sqlite3_stmt *stmt = prepare_statement(meta_database, "SELECT value FROM meta WHERE key=?");
187 fprintf(stderr, "[warning] failed loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
191 sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
193 int rc = sqlite3_step(stmt);
194 bool found = rc == SQLITE_ROW;
197 *value_ptr = sqlite3_column_int64(stmt, 0);
198 else if (rc != SQLITE_DONE)
199 fprintf(stderr, "[warning] failed loading meta %s: %s\n", key, sqlite3_errmsg(meta_database));
201 sqlite3_finalize(stmt);
205 // save / update a meta entry
206 void database_save_meta(const char *key, s64 value)
208 sqlite3_stmt *stmt = prepare_statement(meta_database, "REPLACE INTO meta (key, value) VALUES(?1, ?2)");
211 fprintf(stderr, "[warning] failed saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
215 sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
216 sqlite3_bind_int64(stmt, 2, value);
218 if (sqlite3_step(stmt) != SQLITE_DONE)
219 fprintf(stderr, "[warning] failed saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
221 sqlite3_finalize(stmt);
224 // load player data from database
225 bool database_load_player(char *name, v3f64 *pos, v3f32 *rot)
227 sqlite3_stmt *stmt = prepare_statement(players_database, "SELECT pos, rot FROM players WHERE name=?");
230 fprintf(stderr, "[warning] failed loading player %s: %s\n", name, sqlite3_errmsg(players_database));
234 sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
236 int rc = sqlite3_step(stmt);
237 bool found = rc == SQLITE_ROW;
240 v3f64_read(&(Blob) {sqlite3_column_bytes(stmt, 0), (void *) sqlite3_column_blob(stmt, 0)}, pos);
241 v3f32_read(&(Blob) {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)}, rot);
242 } else if (rc != SQLITE_DONE) {
243 fprintf(stderr, "[warning] failed loading player %s: %s\n", name, sqlite3_errmsg(players_database));
246 sqlite3_finalize(stmt);
250 // insert new player into database
251 void database_create_player(char *name, v3f64 pos, v3f32 rot)
253 sqlite3_stmt *stmt = prepare_statement(players_database, "INSERT INTO players (name, pos, rot) VALUES(?1, ?2, ?3)");
256 fprintf(stderr, "[warning] failed creating player %s: %s\n", name, sqlite3_errmsg(players_database));
260 sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
261 bind_v3f64(stmt, 2, pos);
262 bind_v3f32(stmt, 3, rot);
264 if (sqlite3_step(stmt) != SQLITE_DONE)
265 fprintf(stderr, "[warning] failed creating player %s: %s\n", name, sqlite3_errmsg(players_database));
267 sqlite3_finalize(stmt);
270 // update player position
271 void database_update_player_pos_rot(char *name, v3f64 pos, v3f32 rot)
273 sqlite3_stmt *stmt = prepare_statement(players_database, "UPDATE players SET pos=?1, rot=?2 WHERE name=?3");
276 fprintf(stderr, "[warning] failed updating player %s: %s\n", name, sqlite3_errmsg(players_database));
280 bind_v3f64(stmt, 1, pos);
281 bind_v3f32(stmt, 2, rot);
282 sqlite3_bind_text(stmt, 3, name, strlen(name), SQLITE_TRANSIENT);
284 if (sqlite3_step(stmt) != SQLITE_DONE)
285 fprintf(stderr, "[warning] failed updating player %s: %s\n", name, sqlite3_errmsg(players_database));
287 sqlite3_finalize(stmt);