]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/database.c
Add trees
[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         size_t psize = sizeof(u32) * 3;
41         u32 *pos = malloc(psize);
42         pos[0] = htobe32(block->pos.x);
43         pos[1] = htobe32(block->pos.y);
44         pos[2] = htobe32(block->pos.z);
45
46         sqlite3_bind_blob(stmt, 1, pos, psize, &free);
47
48         return stmt;
49 }
50
51 // bind v3f64 to sqlite3 statement
52 static inline void bind_v3f64(sqlite3_stmt *stmt, int idx, v3f64 pos)
53 {
54         size_t psize = sizeof(f64) * 3;
55         f64 *blob = malloc(psize);
56         blob[0] = pos.x;
57         blob[1] = pos.y;
58         blob[2] = pos.z;
59         sqlite3_bind_blob(stmt, idx, blob, psize, &free);
60 }
61
62 // public functions
63
64 // open and initialize world SQLite3 database
65 void database_init()
66 {
67         char *err;
68
69         if (sqlite3_open_v2("world.sqlite", &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
70                 fprintf(stderr, "Failed to open database: %s\n", sqlite3_errmsg(database));
71                 return;
72         }
73
74         const char *init_stmts[3]= {
75                 "CREATE TABLE IF NOT EXISTS map (pos BLOB PRIMARY KEY, generated INTEGER, size INTEGER, rawsize INTEGER, data BLOB, mgsb_size INTEGER, mgsb_data BLOB);",
76                 "CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value INTEGER);",
77                 "CREATE TABLE IF NOT EXISTS players (name TEXT PRIMARY KEY, pos BLOB);"
78         };
79
80         for (int i = 0; i < 3; i++) {
81                 if (sqlite3_exec(database, init_stmts[i], NULL, NULL, &err) != SQLITE_OK) {
82                         fprintf(stderr, "Failed to initialize database: %s\n", err);
83                         sqlite3_free(err);
84                         return;
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
106 // close database
107 void database_deinit()
108 {
109         database_save_meta("time_of_day", (s64) get_time_of_day());
110         sqlite3_close(database);
111 }
112
113 // load a block from map database (initializes state, mgs buffer and data), returns false on failure
114 bool database_load_block(MapBlock *block)
115 {
116         sqlite3_stmt *stmt;
117
118         if (! (stmt = prepare_block_statement(block, "loading", "SELECT generated, size, rawsize, data, mgsb_size, mgsb_data FROM map WHERE pos=?")))
119                 return false;
120
121         int rc = sqlite3_step(stmt);
122         bool found = rc == SQLITE_ROW;
123
124         if (found) {
125                 MapBlockExtraData *extra = block->extra;
126
127                 extra->state = sqlite3_column_int(stmt, 0) ? MBS_READY : MBS_CREATED;
128                 extra->size = sqlite3_column_int64(stmt, 1);
129                 extra->rawsize = sqlite3_column_int64(stmt, 2);
130                 extra->data = malloc(extra->size);
131                 memcpy(extra->data, sqlite3_column_blob(stmt, 3), extra->size);
132
133                 MapgenStageBuffer decompressed_mgsb;
134                 my_decompress(sqlite3_column_blob(stmt, 5), sqlite3_column_int64(stmt, 4), &decompressed_mgsb, sizeof(MapgenStageBuffer));
135
136                 ITERATE_MAPBLOCK extra->mgs_buffer[x][y][z] = be32toh(decompressed_mgsb[x][y][z]);
137
138                 if (! map_deserialize_block(block, extra->data, extra->size, extra->rawsize))
139                         printf("Error with deserializing 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");
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, size, rawsize, data, mgsb_size, mgsb_data) VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)")))
154                 return;
155
156         MapBlockExtraData *extra = block->extra;
157
158         MapgenStageBuffer uncompressed_mgsb;
159         ITERATE_MAPBLOCK uncompressed_mgsb[x][y][z] = htobe32(extra->mgs_buffer[x][y][z]);
160
161         char *mgsb_data;
162         size_t mgsb_size;
163
164         my_compress(&uncompressed_mgsb, sizeof(MapgenStageBuffer), &mgsb_data, &mgsb_size);
165
166         sqlite3_bind_int(stmt, 2, extra->state > MBS_CREATED);
167         sqlite3_bind_int64(stmt, 3, extra->size);
168         sqlite3_bind_int64(stmt, 4, extra->rawsize);
169         sqlite3_bind_blob(stmt, 5, extra->data, extra->size, SQLITE_TRANSIENT);
170         sqlite3_bind_int64(stmt, 6, mgsb_size);
171         sqlite3_bind_blob(stmt, 7, mgsb_data, mgsb_size, &free);
172
173         if (sqlite3_step(stmt) != SQLITE_DONE)
174                 print_block_error(block, "saving");
175
176         sqlite3_finalize(stmt);
177 }
178
179 // load a meta entry
180 bool database_load_meta(const char *key, s64 *value_ptr)
181 {
182         sqlite3_stmt *stmt;
183
184         if (! (stmt = prepare_statement("SELECT value FROM meta WHERE key=?"))) {
185                 fprintf(stderr, "Database error with loading %s: %s\n", key, sqlite3_errmsg(database));
186                 return false;
187         }
188
189         sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
190
191         int rc = sqlite3_step(stmt);
192         bool found = rc == SQLITE_ROW;
193
194         if (found)
195                 *value_ptr = sqlite3_column_int64(stmt, 0);
196         else if (rc != SQLITE_DONE)
197                 fprintf(stderr, "Database error with loading %s: %s\n", key, sqlite3_errmsg(database));
198
199         sqlite3_finalize(stmt);
200         return found;
201 }
202
203 // save / update a meta entry
204 void database_save_meta(const char *key, s64 value)
205 {
206         sqlite3_stmt *stmt;
207
208         if (! (stmt = prepare_statement("REPLACE INTO meta (key, value) VALUES(?1, ?2)"))) {
209                 fprintf(stderr, "Database error with saving %s: %s\n", key, sqlite3_errmsg(database));
210                 return;
211         }
212
213         sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_TRANSIENT);
214         sqlite3_bind_int64(stmt, 2, value);
215
216         if (sqlite3_step(stmt) != SQLITE_DONE)
217                 fprintf(stderr, "Database error with saving %s: %s\n", key, sqlite3_errmsg(database));
218
219         sqlite3_finalize(stmt);
220 }
221
222 // load player data from database
223 bool database_load_player(char *name, v3f64 *pos_ptr)
224 {
225         sqlite3_stmt *stmt;
226
227         if (! (stmt = prepare_statement("SELECT pos FROM players WHERE name=?"))) {
228                 fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(database));
229                 return false;
230         }
231
232         sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
233
234         int rc = sqlite3_step(stmt);
235         bool found = rc == SQLITE_ROW;
236
237         if (found) {
238                 const f64 *pos = sqlite3_column_blob(stmt, 0);
239                 *pos_ptr = (v3f64) {pos[0], pos[1], pos[2]};
240         } else if (rc != SQLITE_DONE) {
241                 fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(database));
242         }
243
244         sqlite3_finalize(stmt);
245         return found;
246 }
247
248 // insert new player into database
249 void database_create_player(char *name, v3f64 pos)
250 {
251         sqlite3_stmt *stmt;
252
253         if (! (stmt = prepare_statement("INSERT INTO players (name, pos) VALUES(?1, ?2)"))) {
254                 fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(database));
255                 return;
256         }
257
258         sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
259         bind_v3f64(stmt, 2, pos);
260
261         if (sqlite3_step(stmt) != SQLITE_DONE)
262                 fprintf(stderr, "Database error with creating player %s: %s\n", name, sqlite3_errmsg(database));
263
264         sqlite3_finalize(stmt);
265 }
266
267 // update player position
268 void database_update_player_pos(char *name, v3f64 pos)
269 {
270         sqlite3_stmt *stmt;
271
272         if (! (stmt = prepare_statement("UPDATE players SET pos=?1 WHERE name=?2"))) {
273                 fprintf(stderr, "Database error with updating player %s position: %s\n", name, sqlite3_errmsg(database));
274                 return;
275         }
276
277         bind_v3f64(stmt, 1, pos);
278         sqlite3_bind_text(stmt, 2, name, strlen(name), SQLITE_TRANSIENT);
279
280         if (sqlite3_step(stmt) != SQLITE_DONE)
281                 fprintf(stderr, "Database error with updating player %s position: %s\n", name, sqlite3_errmsg(database));
282
283         sqlite3_finalize(stmt);
284 }