]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/database.c
3d9eb92ba689000c7a2c30bbe1a4802dd3d0293d
[dragonblocks_alpha.git] / src / server / database.c
1 #include <stdio.h>
2 #include <endian.h/endian.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sqlite3.h>
6 #include <time.h>
7 #include "day.h"
8 #include "server/database.h"
9 #include "server/server_map.h"
10 #include "perlin.h"
11 #include "util.h"
12
13 static sqlite3 *map_database;
14 static sqlite3 *meta_database;
15 static sqlite3 *players_database;
16
17 // utility functions
18
19 // prepare a SQLite3 statement
20 static inline sqlite3_stmt *prepare_statement(sqlite3 *database, const char *sql)
21 {
22         sqlite3_stmt *stmt;
23         return sqlite3_prepare_v2(database, sql, -1, &stmt, NULL) == SQLITE_OK ? stmt : NULL;
24 }
25
26 // print SQLite3 error message for failed block SQL statement
27 static inline void print_block_error(MapBlock *block, const char *action)
28 {
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));
30 }
31
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)
34 {
35         sqlite3_stmt *stmt;
36
37         if (! (stmt = prepare_statement(map_database, sql))) {
38                 print_block_error(block, action);
39                 return NULL;
40         }
41
42         Blob buffer = {0, NULL};
43         v3s32_write(&buffer, &block->pos);
44
45         sqlite3_bind_blob(stmt, 1, buffer.data, buffer.siz, &free);
46
47         return stmt;
48 }
49
50 // bind v3f64 to sqlite3 statement
51 static inline void bind_v3f64(sqlite3_stmt *stmt, int idx, v3f64 pos)
52 {
53         Blob buffer = {0, NULL};
54         v3f64_write(&buffer, &pos);
55
56         sqlite3_bind_blob(stmt, idx, buffer.data, buffer.siz, &free);
57 }
58
59 // public functions
60
61 // open and initialize SQLite3 databases
62 bool database_init()
63 {
64         struct {
65                 sqlite3 **handle;
66                 const char *path;
67                 const char *init;
68         } databases[3] = {
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                               );"},
72         };
73
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));
77                         return false;
78                 }
79
80                 char *err;
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);
83                         sqlite3_free(err);
84                         return false;
85                 }
86         }
87
88         s64 saved_seed;
89
90         if (database_load_meta("seed", &saved_seed)) {
91                 seed = saved_seed;
92         } else {
93                 srand(time(NULL));
94                 seed = rand();
95                 database_save_meta("seed", seed);
96         }
97
98         s64 time_of_day;
99
100         if (database_load_meta("time_of_day", &time_of_day))
101                 set_time_of_day(time_of_day);
102         else
103                 set_time_of_day(12 * MINUTES_PER_HOUR);
104
105         return true;
106 }
107
108 // close database
109 void database_deinit()
110 {
111         database_save_meta("time_of_day", (s64) get_time_of_day());
112
113         sqlite3_close(map_database);
114         sqlite3_close(meta_database);
115         sqlite3_close(players_database);
116 }
117
118 // load a block from map database (initializes state, mgs buffer and data), returns false on failure
119 bool database_load_block(MapBlock *block)
120 {
121         sqlite3_stmt *stmt;
122
123         if (! (stmt = prepare_block_statement(block, "loading", "SELECT generated, data, mgsb FROM map WHERE pos=?")))
124                 return false;
125
126         int rc = sqlite3_step(stmt);
127         bool found = rc == SQLITE_ROW;
128
129         if (found) {
130                 MapBlockExtraData *extra = block->extra;
131
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);
135
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);
138                         exit(EXIT_FAILURE);
139                 }
140         } else if (rc != SQLITE_DONE) {
141                 print_block_error(block, "loading");
142         }
143
144         sqlite3_finalize(stmt);
145         return found;
146 }
147
148 // save a block to database
149 void database_save_block(MapBlock *block)
150 {
151         sqlite3_stmt *stmt;
152
153         if (! (stmt = prepare_block_statement(block, "saving", "REPLACE INTO map (pos, generated, data, mgsb) VALUES(?1, ?2, ?3, ?4)")))
154                 return;
155
156         MapBlockExtraData *extra = block->extra;
157
158         Blob data = {0, NULL};
159         Blob_write(&data, &extra->data);
160
161         Blob mgsb = {0, NULL};
162         MapgenStageBuffer_write(&mgsb, &extra->mgsb);
163
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);
167
168         if (sqlite3_step(stmt) != SQLITE_DONE)
169                 print_block_error(block, "saving");
170
171         sqlite3_finalize(stmt);
172 }
173
174 // load a meta entry
175 bool database_load_meta(const char *key, s64 *value_ptr)
176 {
177         sqlite3_stmt *stmt;
178
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));
181                 return false;
182         }
183
184         sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
185
186         int rc = sqlite3_step(stmt);
187         bool found = rc == SQLITE_ROW;
188
189         if (found)
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));
193
194         sqlite3_finalize(stmt);
195         return found;
196 }
197
198 // save / update a meta entry
199 void database_save_meta(const char *key, s64 value)
200 {
201         sqlite3_stmt *stmt;
202
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));
205                 return;
206         }
207
208         sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
209         sqlite3_bind_int64(stmt, 2, value);
210
211         if (sqlite3_step(stmt) != SQLITE_DONE)
212                 fprintf(stderr, "Database error with saving meta %s: %s\n", key, sqlite3_errmsg(meta_database));
213
214         sqlite3_finalize(stmt);
215 }
216
217 // load player data from database
218 bool database_load_player(char *name, v3f64 *pos_ptr)
219 {
220         sqlite3_stmt *stmt;
221
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));
224                 return false;
225         }
226
227         sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
228
229         int rc = sqlite3_step(stmt);
230         bool found = rc == SQLITE_ROW;
231
232         if (found)
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));
236
237         sqlite3_finalize(stmt);
238         return found;
239 }
240
241 // insert new player into database
242 void database_create_player(char *name, v3f64 pos)
243 {
244         sqlite3_stmt *stmt;
245
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));
248                 return;
249         }
250
251         sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
252         bind_v3f64(stmt, 2, pos);
253
254         if (sqlite3_step(stmt) != SQLITE_DONE)
255                 fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(players_database));
256
257         sqlite3_finalize(stmt);
258 }
259
260 // update player position
261 void database_update_player_pos(char *name, v3f64 pos)
262 {
263         sqlite3_stmt *stmt;
264
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));
267                 return;
268         }
269
270         bind_v3f64(stmt, 1, pos);
271         sqlite3_bind_text(stmt, 2, name, strlen(name), SQLITE_TRANSIENT);
272
273         if (sqlite3_step(stmt) != SQLITE_DONE)
274                 fprintf(stderr, "Database error with updating player %s position: %s\n", name, sqlite3_errmsg(players_database));
275
276         sqlite3_finalize(stmt);
277 }