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