]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/database.c
afac7c8d24ff8f4f68f04008c03c1211c724aa6e
[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 *database;
14
15 // utility functions
16
17 // prepare a SQLite3 statement
18 static inline sqlite3_stmt *prepare_statement(const char *sql)
19 {
20         sqlite3_stmt *stmt;
21         return sqlite3_prepare_v2(database, sql, -1, &stmt, NULL) == SQLITE_OK ? stmt : NULL;
22 }
23
24 // print SQLite3 error message for failed block SQL statement
25 static inline void print_block_error(MapBlock *block, const char *action)
26 {
27         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));
28 }
29
30 // prepare a SQLite3 block statement and bind the position
31 static sqlite3_stmt *prepare_block_statement(MapBlock *block, const char *action, const char *sql)
32 {
33         sqlite3_stmt *stmt;
34
35         if (! (stmt = prepare_statement(sql))) {
36                 print_block_error(block, action);
37                 return NULL;
38         }
39
40         Blob buffer = {0, NULL};
41         v3s32_write(&buffer, &block->pos);
42
43         sqlite3_bind_blob(stmt, 1, buffer.data, buffer.siz, &free);
44
45         return stmt;
46 }
47
48 // bind v3f64 to sqlite3 statement
49 static inline void bind_v3f64(sqlite3_stmt *stmt, int idx, v3f64 pos)
50 {
51         Blob buffer = {0, NULL};
52         v3f64_write(&buffer, &pos);
53
54         sqlite3_bind_blob(stmt, idx, buffer.data, buffer.siz, &free);
55 }
56
57 // public functions
58
59 // open and initialize world SQLite3 database
60 void database_init()
61 {
62         char *err;
63
64         if (sqlite3_open_v2("world.sqlite", &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
65                 fprintf(stderr, "Failed to open database: %s\n", sqlite3_errmsg(database));
66                 return;
67         }
68
69         const char *init_stmts[3]= {
70                 "CREATE TABLE IF NOT EXISTS map (pos BLOB PRIMARY KEY, generated INTEGER, data BLOB, mgsb BLOB);",
71                 "CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value INTEGER);",
72                 "CREATE TABLE IF NOT EXISTS players (name TEXT PRIMARY KEY, pos BLOB);"
73         };
74
75         for (int i = 0; i < 3; i++) {
76                 if (sqlite3_exec(database, init_stmts[i], NULL, NULL, &err) != SQLITE_OK) {
77                         fprintf(stderr, "Failed to initialize database: %s\n", err);
78                         sqlite3_free(err);
79                         return;
80                 }
81         }
82
83         s64 saved_seed;
84
85         if (database_load_meta("seed", &saved_seed)) {
86                 seed = saved_seed;
87         } else {
88                 srand(time(NULL));
89                 seed = rand();
90                 database_save_meta("seed", seed);
91         }
92
93         s64 time_of_day;
94
95         if (database_load_meta("time_of_day", &time_of_day))
96                 set_time_of_day(time_of_day);
97         else
98                 set_time_of_day(12 * MINUTES_PER_HOUR);
99 }
100
101 // close database
102 void database_deinit()
103 {
104         database_save_meta("time_of_day", (s64) get_time_of_day());
105         sqlite3_close(database);
106 }
107
108 // load a block from map database (initializes state, mgs buffer and data), returns false on failure
109 bool database_load_block(MapBlock *block)
110 {
111         sqlite3_stmt *stmt;
112
113         if (! (stmt = prepare_block_statement(block, "loading", "SELECT generated, data, mgsb FROM map WHERE pos=?")))
114                 return false;
115
116         int rc = sqlite3_step(stmt);
117         bool found = rc == SQLITE_ROW;
118
119         if (found) {
120                 MapBlockExtraData *extra = block->extra;
121
122                 extra->state = sqlite3_column_int(stmt, 0) ? MBS_READY : MBS_CREATED;
123                 Blob_read(             &(Blob) {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)}, &extra->data);
124                 MapgenStageBuffer_read(&(Blob) {sqlite3_column_bytes(stmt, 2), (void *) sqlite3_column_blob(stmt, 2)}, &extra->mgsb);
125
126                 if (! map_deserialize_block(block, extra->data)) {
127                         fprintf(stderr, "Failed to load block at (%d, %d, %d)\n", block->pos.x, block->pos.y, block->pos.z);
128                         exit(EXIT_FAILURE);
129                 }
130         } else if (rc != SQLITE_DONE) {
131                 print_block_error(block, "loading");
132         }
133
134         sqlite3_finalize(stmt);
135         return found;
136 }
137
138 // save a block to database
139 void database_save_block(MapBlock *block)
140 {
141         sqlite3_stmt *stmt;
142
143         if (! (stmt = prepare_block_statement(block, "saving", "REPLACE INTO map (pos, generated, data, mgsb) VALUES(?1, ?2, ?3, ?4)")))
144                 return;
145
146         MapBlockExtraData *extra = block->extra;
147
148         Blob data = {0, NULL};
149         Blob_write(&data, &extra->data);
150
151         Blob mgsb = {0, NULL};
152         MapgenStageBuffer_write(&mgsb, &extra->mgsb);
153
154         sqlite3_bind_int(stmt, 2, extra->state > MBS_CREATED);
155         sqlite3_bind_blob(stmt, 3, data.data, data.siz, &free);
156         sqlite3_bind_blob(stmt, 4, mgsb.data, mgsb.siz, &free);
157
158         if (sqlite3_step(stmt) != SQLITE_DONE)
159                 print_block_error(block, "saving");
160
161         sqlite3_finalize(stmt);
162 }
163
164 // load a meta entry
165 bool database_load_meta(const char *key, s64 *value_ptr)
166 {
167         sqlite3_stmt *stmt;
168
169         if (! (stmt = prepare_statement("SELECT value FROM meta WHERE key=?"))) {
170                 fprintf(stderr, "Database error with loading %s: %s\n", key, sqlite3_errmsg(database));
171                 return false;
172         }
173
174         sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
175
176         int rc = sqlite3_step(stmt);
177         bool found = rc == SQLITE_ROW;
178
179         if (found)
180                 *value_ptr = sqlite3_column_int64(stmt, 0);
181         else if (rc != SQLITE_DONE)
182                 fprintf(stderr, "Database error with loading %s: %s\n", key, sqlite3_errmsg(database));
183
184         sqlite3_finalize(stmt);
185         return found;
186 }
187
188 // save / update a meta entry
189 void database_save_meta(const char *key, s64 value)
190 {
191         sqlite3_stmt *stmt;
192
193         if (! (stmt = prepare_statement("REPLACE INTO meta (key, value) VALUES(?1, ?2)"))) {
194                 fprintf(stderr, "Database error with saving %s: %s\n", key, sqlite3_errmsg(database));
195                 return;
196         }
197
198         sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
199         sqlite3_bind_int64(stmt, 2, value);
200
201         if (sqlite3_step(stmt) != SQLITE_DONE)
202                 fprintf(stderr, "Database error with saving %s: %s\n", key, sqlite3_errmsg(database));
203
204         sqlite3_finalize(stmt);
205 }
206
207 // load player data from database
208 bool database_load_player(char *name, v3f64 *pos_ptr)
209 {
210         sqlite3_stmt *stmt;
211
212         if (! (stmt = prepare_statement("SELECT pos FROM players WHERE name=?"))) {
213                 fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(database));
214                 return false;
215         }
216
217         sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
218
219         int rc = sqlite3_step(stmt);
220         bool found = rc == SQLITE_ROW;
221
222         if (found)
223                 v3f64_read(&(Blob) {sqlite3_column_bytes(stmt, 0), (void *) sqlite3_column_blob(stmt, 0)}, pos_ptr);
224         else if (rc != SQLITE_DONE)
225                 fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(database));
226
227         sqlite3_finalize(stmt);
228         return found;
229 }
230
231 // insert new player into database
232 void database_create_player(char *name, v3f64 pos)
233 {
234         sqlite3_stmt *stmt;
235
236         if (! (stmt = prepare_statement("INSERT INTO players (name, pos) VALUES(?1, ?2)"))) {
237                 fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(database));
238                 return;
239         }
240
241         sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
242         bind_v3f64(stmt, 2, pos);
243
244         if (sqlite3_step(stmt) != SQLITE_DONE)
245                 fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(database));
246
247         sqlite3_finalize(stmt);
248 }
249
250 // update player position
251 void database_update_player_pos(char *name, v3f64 pos)
252 {
253         sqlite3_stmt *stmt;
254
255         if (! (stmt = prepare_statement("UPDATE players SET pos=?1 WHERE name=?2"))) {
256                 fprintf(stderr, "Database error with updating player %s position: %s\n", name, sqlite3_errmsg(database));
257                 return;
258         }
259
260         bind_v3f64(stmt, 1, pos);
261         sqlite3_bind_text(stmt, 2, name, strlen(name), SQLITE_TRANSIENT);
262
263         if (sqlite3_step(stmt) != SQLITE_DONE)
264                 fprintf(stderr, "Database error with updating player %s position: %s\n", name, sqlite3_errmsg(database));
265
266         sqlite3_finalize(stmt);
267 }