DungeonCrawl
Loading...
Searching...
No Matches
gamestate_database.c File Reference

Implements functionality to work with the gamestate and database. More...

#include "gamestate_database.h"
#include "../../logging/logger.h"
#include "../database.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>

Go to the source code of this file.

Macros

#define TIMESTAMP_FORMAT   "%Y-%m-%d %H:%M:%S"
#define SQL_INSERT_GAME_STATE   "INSERT INTO game_state (GS_SAVEDTIME, GS_NAME) VALUES (?, ?)"
#define SQL_INSERT_MAP_STATE   "INSERT INTO map_state (MS_MAP, MS_REVEALED, MS_HEIGHT,MS_WIDTH, MS_GS_ID) VALUES (?, ?, ?, ?, ?)"
#define SQL_INSERT_PLAYER_STATE   "INSERT INTO player_state (PS_X, PS_Y, PS_GS_ID) VALUES (?, ?, ?)"
#define SQL_SELECT_LAST_GAME_STATE   "SELECT GS_ID FROM game_state ORDER BY GS_SAVEDTIME DESC LIMIT 1"
#define SQL_SELECT_MAP_STATE   "SELECT MS_HEIGHT, MS_WIDTH FROM map_state WHERE MS_GS_ID = ?"
#define SQL_SELECT_MAP   "SELECT value FROM map_state, json_each(map_state.MS_MAP) WHERE MS_GS_ID = ?"
#define SQL_SELECT_REVEALED_MAP   "SELECT value FROM map_state, json_each(map_state.MS_REVEALED) WHERE MS_GS_ID = ?"
#define SQL_SELECT_PLAYER_STATE   "SELECT PS_X, PS_Y FROM player_state WHERE PS_GS_ID = ?"
#define SQL_SELECT_ALL_GAME_STATES   "SELECT GS_ID, GS_SAVEDTIME, GS_NAME FROM game_state ORDER BY GS_SAVEDTIME DESC"

Functions

char * get_iso8601_time ()
 Get the current time in ISO 8601 format.
sqlite_int64 save_game_state (const db_connection_t *db_connection, const int *map, const int *revealed_map, const int width, const int height, const vector2d_t player, const char *save_name)
 Save the game state.
char * arr2D_to_flat_json (const int *arr, const int width, const int height)
 Convert a 2D array to a Json-Array.
int get_game_state (const db_connection_t *db_connection, int *map, int *revealed_map, const int width, const int height, const player_pos_setter_t setter)
 Load the game state from the database.
int get_game_state_by_id (const db_connection_t *db_connection, const int game_state_id, int *map, int *revealed_map, const int width, const int height, const player_pos_setter_t setter)
 Load the game state for a specific id from the database.
save_info_container_tget_save_infos (const db_connection_t *db_connection)
 Get the info of the saves.
void free_save_infos (save_info_container_t *save_infos)
 Free the resources associated with a save_info_container.
void create_tables_game_state (const db_connection_t *db_connection)
 Create the tables for the game state.
int get_latest_save_id (const db_connection_t *db_connection)

Detailed Description

Implements functionality to work with the gamestate and database.

Definition in file gamestate_database.c.

Macro Definition Documentation

◆ SQL_INSERT_GAME_STATE

#define SQL_INSERT_GAME_STATE   "INSERT INTO game_state (GS_SAVEDTIME, GS_NAME) VALUES (?, ?)"

Definition at line 18 of file gamestate_database.c.

◆ SQL_INSERT_MAP_STATE

#define SQL_INSERT_MAP_STATE   "INSERT INTO map_state (MS_MAP, MS_REVEALED, MS_HEIGHT,MS_WIDTH, MS_GS_ID) VALUES (?, ?, ?, ?, ?)"

Definition at line 19 of file gamestate_database.c.

◆ SQL_INSERT_PLAYER_STATE

#define SQL_INSERT_PLAYER_STATE   "INSERT INTO player_state (PS_X, PS_Y, PS_GS_ID) VALUES (?, ?, ?)"

Definition at line 20 of file gamestate_database.c.

◆ SQL_SELECT_ALL_GAME_STATES

#define SQL_SELECT_ALL_GAME_STATES   "SELECT GS_ID, GS_SAVEDTIME, GS_NAME FROM game_state ORDER BY GS_SAVEDTIME DESC"

Definition at line 26 of file gamestate_database.c.

◆ SQL_SELECT_LAST_GAME_STATE

#define SQL_SELECT_LAST_GAME_STATE   "SELECT GS_ID FROM game_state ORDER BY GS_SAVEDTIME DESC LIMIT 1"

Definition at line 21 of file gamestate_database.c.

◆ SQL_SELECT_MAP

#define SQL_SELECT_MAP   "SELECT value FROM map_state, json_each(map_state.MS_MAP) WHERE MS_GS_ID = ?"

Definition at line 23 of file gamestate_database.c.

◆ SQL_SELECT_MAP_STATE

#define SQL_SELECT_MAP_STATE   "SELECT MS_HEIGHT, MS_WIDTH FROM map_state WHERE MS_GS_ID = ?"

Definition at line 22 of file gamestate_database.c.

◆ SQL_SELECT_PLAYER_STATE

#define SQL_SELECT_PLAYER_STATE   "SELECT PS_X, PS_Y FROM player_state WHERE PS_GS_ID = ?"

Definition at line 25 of file gamestate_database.c.

◆ SQL_SELECT_REVEALED_MAP

#define SQL_SELECT_REVEALED_MAP   "SELECT value FROM map_state, json_each(map_state.MS_REVEALED) WHERE MS_GS_ID = ?"

Definition at line 24 of file gamestate_database.c.

◆ TIMESTAMP_FORMAT

#define TIMESTAMP_FORMAT   "%Y-%m-%d %H:%M:%S"

Definition at line 16 of file gamestate_database.c.

Function Documentation

◆ arr2D_to_flat_json()

char * arr2D_to_flat_json ( const int * arr,
int width,
int height )

Convert a 2D array to a Json-Array.

Parameters
arrThe given array to convert to a Json-Array
widthThe width of the array
heightThe height of the array
Returns
The Json-Array as a string (must be freed by the caller), NULL if a malloc error occurred
Note
The returned string is a flattened JSON Array (e.g. [[1,2],[3,4]] -> [1,2,3,4])

Definition at line 203 of file gamestate_database.c.

203 {
204 const size_t buffer_size = width * height * 12 + 2;// 16 is a safe estimate for int size + comma + space
205 char* json = malloc(buffer_size);
206 if (json == NULL) {
207 log_msg(ERROR, "GameState", "Failed to allocate memory for JSON string");
208 return NULL;
209 }
210
211 strcpy(json, "[");
212
213 const int total_elements = width * height;
214 // Loop over the 2D map and append each element in a 1D fashion
215 for (int i = 0; i < total_elements; i++) {
216 char number[10];// Buffer to hold the number as a string
217 // Write the value into the buffer
218 snprintf(number, sizeof(number), "%d", arr[i]);
219 strcat(json, number);// Append the number to the JSON string
220
221 // If it's not the last element, append a comma
222 if (i < total_elements - 1) {
223 strcat(json, ",");
224 }
225 }
226
227 strcat(json, "]");//strcat always ensures that the string is null-terminated
228 return json;
229}
void log_msg(const log_level_t level, const char *module, const char *format,...)
Logs a formatted message with a specified log level and module.
Definition logger.c:246

◆ create_tables_game_state()

void create_tables_game_state ( const db_connection_t * db_connection)

Create the tables for the game state.

Parameters
db_connectionThe database connection
Note
The tables will be created if they do not exist

Definition at line 430 of file gamestate_database.c.

430 {
431 if (!db_is_open(db_connection)) {
432 log_msg(ERROR, "GameState", "Database is not open");
433 return;
434 }
435
436 //Create GS table
437 sqlite3_stmt* stmt;
438 int rc = sqlite3_prepare_v2(db_connection->db, SQL_CREATE_TABLES_GAMESTATE_GS, -1, &stmt, NULL);
439 if (rc != SQLITE_OK) {
440 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
441 return;
442 }
443 rc = sqlite3_step(stmt);
444 if (rc != SQLITE_DONE) {
445 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
446 }
447 sqlite3_finalize(stmt);
448
449 // Create MS table
450 rc = sqlite3_prepare_v2(db_connection->db, SQL_CREATE_TABLES_GAMESTATE_MS, -1, &stmt, NULL);
451 if (rc != SQLITE_OK) {
452 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
453 return;
454 }
455 rc = sqlite3_step(stmt);
456 if (rc != SQLITE_DONE) {
457 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
458 }
459 sqlite3_finalize(stmt);
460
461 // Create PS table
462 rc = sqlite3_prepare_v2(db_connection->db, SQL_CREATE_TABLES_GAMESTATE_PS, -1, &stmt, NULL);
463 if (rc != SQLITE_OK) {
464 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
465 return;
466 }
467 rc = sqlite3_step(stmt);
468 if (rc != SQLITE_DONE) {
469 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
470 }
471 sqlite3_finalize(stmt);
472}
int db_is_open(const db_connection_t *db_connection)
This function is to check if the database is open.
Definition database.c:27

◆ free_save_infos()

void free_save_infos ( save_info_container_t * save_infos)

Free the resources associated with a save_info_container.

Parameters
save_infosSave info to free.

Definition at line 421 of file gamestate_database.c.

421 {
422 if (save_infos == NULL) {
423 return;
424 }
425
426 free(save_infos->infos);
427 free(save_infos);
428}

◆ get_game_state()

int get_game_state ( const db_connection_t * db_connection,
int * map,
int * revealed_map,
int width,
int height,
player_pos_setter_t setter )

Load the game state from the database.

Parameters
db_connectionA database connection.
mapPointer to the map to load into.
revealed_mapPointer to the revealed map to load into.
widthWidth of the map.
heightHieght of the map.
setterA setter for the player position.
Returns
0 on success none 0 on failure.

Definition at line 231 of file gamestate_database.c.

231 {
232 return get_game_state_by_id(db_connection, get_latest_save_id(db_connection), map, revealed_map, width, height, setter);
233}
int get_latest_save_id(const db_connection_t *db_connection)
int get_game_state_by_id(const db_connection_t *db_connection, const int game_state_id, int *map, int *revealed_map, const int width, const int height, const player_pos_setter_t setter)
Load the game state for a specific id from the database.

◆ get_game_state_by_id()

int get_game_state_by_id ( const db_connection_t * db_connection,
int game_state_id,
int * map,
int * revealed_map,
int width,
int height,
player_pos_setter_t setter )

Load the game state for a specific id from the database.

Parameters
db_connectionA database connection.
game_state_idThe id of the game state to be loaded.
mapPointer to the map to load into.
revealed_mapPointer to the revealed map to load into.
widthWidth of the map.
heightHieght of the map.
setterA setter for the player position.
Returns
0 on success none 0 on failure.

Definition at line 235 of file gamestate_database.c.

235 {
236 // Get the height and width from the database
237 sqlite3_stmt* stmt_map;
238 int rc = sqlite3_prepare_v2(db_connection->db, SQL_SELECT_MAP_STATE, -1, &stmt_map, NULL);
239 if (rc != SQLITE_OK) {
240 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
241 return 0;
242 }
243 // Bind the game state ID to the statement
244 rc = sqlite3_bind_int64(stmt_map, 1, game_state_id);
245 if (rc != SQLITE_OK) {
246 log_msg(ERROR, "GameState", "Failed to bind game state ID: %s", sqlite3_errmsg(db_connection->db));
247 sqlite3_finalize(stmt_map);
248 return 0;
249 }
250 // Execute the statement
251 rc = sqlite3_step(stmt_map);
252 if (rc != SQLITE_ROW) {
253 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
254 sqlite3_finalize(stmt_map);
255 return 0;
256 }
257
258 sqlite3_finalize(stmt_map);
259
260 //Get the map from the database
261 sqlite3_stmt* stmt_map_data;
262 rc = sqlite3_prepare_v2(db_connection->db, SQL_SELECT_MAP, -1, &stmt_map_data, NULL);
263 if (rc != SQLITE_OK) {
264 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
265 return 0;
266 }
267 // Bind the game state ID to the statement
268 rc = sqlite3_bind_int64(stmt_map_data, 1, game_state_id);
269 if (rc != SQLITE_OK) {
270 log_msg(ERROR, "GameState", "Failed to bind game state ID: %s", sqlite3_errmsg(db_connection->db));
271 sqlite3_finalize(stmt_map_data);
272 return 0;
273 }
274 // Execute the statement
275 rc = sqlite3_step(stmt_map_data);
276 if (rc != SQLITE_ROW) {
277 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
278 sqlite3_finalize(stmt_map_data);
279 return 0;
280 }
281
282 int index = 0;
283 const int total_cells = width * height;
284 while (index < total_cells && rc == SQLITE_ROW) {
285 map[index] = sqlite3_column_int(stmt_map_data, 0);
286 index++;
287 rc = sqlite3_step(stmt_map_data);
288 }
289
290 if (index != total_cells) {
291 log_msg(ERROR, "GameState", "Didn't get all map data. Expected %d, got %d", total_cells, index);
292 sqlite3_finalize(stmt_map_data);
293 return 0;
294 }
295
296 sqlite3_finalize(stmt_map_data);
297
298 //Get the revealed map from the database
299 sqlite3_stmt* stmt_revealed_map_data;
300 rc = sqlite3_prepare_v2(db_connection->db, SQL_SELECT_REVEALED_MAP, -1, &stmt_revealed_map_data, NULL);
301 if (rc != SQLITE_OK) {
302 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
303 return 0;
304 }
305 // Bind the game state ID to the statement
306 rc = sqlite3_bind_int64(stmt_revealed_map_data, 1, game_state_id);
307 if (rc != SQLITE_OK) {
308 log_msg(ERROR, "GameState", "Failed to bind game state ID: %s", sqlite3_errmsg(db_connection->db));
309 sqlite3_finalize(stmt_revealed_map_data);
310 return 0;
311 }
312 // Execute the statement
313 rc = sqlite3_step(stmt_revealed_map_data);
314 if (rc != SQLITE_ROW) {
315 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
316 sqlite3_finalize(stmt_revealed_map_data);
317 return 0;
318 }
319
320 // Build the revealed map from the result
321 index = 0;
322 while (index < total_cells && rc == SQLITE_ROW) {
323 revealed_map[index] = sqlite3_column_int(stmt_revealed_map_data, 0);
324 index++;
325 rc = sqlite3_step(stmt_revealed_map_data);
326 }
327
328 if (index != total_cells) {
329 log_msg(ERROR, "GameState", "Didn't get all revealed map data. Expected %d, got %d", total_cells, index);
330 sqlite3_finalize(stmt_revealed_map_data);
331 return 0;
332 }
333
334 sqlite3_finalize(stmt_revealed_map_data);
335
336 //Get the player position from the database
337 sqlite3_stmt* stmt_player_data;
338 rc = sqlite3_prepare_v2(db_connection->db, SQL_SELECT_PLAYER_STATE, -1, &stmt_player_data, NULL);
339 if (rc != SQLITE_OK) {
340 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
341 return 0;
342 }
343 // Bind the game state ID to the statement
344 rc = sqlite3_bind_int64(stmt_player_data, 1, game_state_id);
345 if (rc != SQLITE_OK) {
346 log_msg(ERROR, "GameState", "Failed to bind game state ID: %s", sqlite3_errmsg(db_connection->db));
347 sqlite3_finalize(stmt_player_data);
348 return 0;
349 }
350 // Execute the statement
351 rc = sqlite3_step(stmt_player_data);
352 if (rc != SQLITE_ROW) {
353 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
354 sqlite3_finalize(stmt_player_data);
355 return 0;
356 }
357 // Get the player position from the result
358 const int player_x = sqlite3_column_int(stmt_player_data, 0);
359 const int player_y = sqlite3_column_int(stmt_player_data, 1);
360 setter(player_x, player_y);
361 sqlite3_finalize(stmt_player_data);
362 return 1;
363}

◆ get_iso8601_time()

char * get_iso8601_time ( )

Get the current time in ISO 8601 format.

Returns
The current time as a string in ISO 8601 format (must be freed by the caller)

Definition at line 194 of file gamestate_database.c.

194 {
195 const time_t now = time(NULL);
196 const struct tm* tm = localtime(&now);
197 char timestamp[TIMESTAMP_LENGTH];
198 strftime(timestamp, sizeof(timestamp), TIMESTAMP_FORMAT, tm);
199
200 return strdup(timestamp);
201}

◆ get_latest_save_id()

int get_latest_save_id ( const db_connection_t * db_connection)
Parameters
db_connectionConnection to the database
Returns
The ID of the latest save

Definition at line 474 of file gamestate_database.c.

474 {
475 // Get the last game state ID
476 sqlite3_stmt* stmt;
477 int rc = sqlite3_prepare_v2(db_connection->db, SQL_SELECT_LAST_GAME_STATE, -1, &stmt, NULL);
478 if (rc != SQLITE_OK) {
479 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
480 return 0;
481 }
482 // Execute the statement
483 rc = sqlite3_step(stmt);
484 if (rc != SQLITE_ROW) {
485 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
486 sqlite3_finalize(stmt);
487 return 0;
488 }
489
490 const int game_state_id = sqlite3_column_int(stmt, 0);
491 sqlite3_finalize(stmt);
492 return game_state_id;
493}

◆ get_save_infos()

save_info_container_t * get_save_infos ( const db_connection_t * db_connection)

Get the info of the saves.

Parameters
db_connectionA database connedtion.
Returns
A save_info_container containing the save info.

Definition at line 365 of file gamestate_database.c.

365 {
366 sqlite3_stmt* stmt;
367 const int rc = sqlite3_prepare_v2(db_connection->db, SQL_SELECT_ALL_GAME_STATES, -1, &stmt, NULL);
368 if (rc != SQLITE_OK) {
369 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
370 return NULL;
371 }
372
373 // First, count the number of saves
374 int save_count = 0;
375 while (sqlite3_step(stmt) == SQLITE_ROW) {
376 save_count++;
377 }
378
379 // Reset the statement to start from the beginning
380 sqlite3_reset(stmt);
381
382 save_info_container_t* save_infos = malloc(sizeof(save_info_container_t));
383 if (save_infos == NULL) {
384 log_msg(ERROR, "GameState", "Failed to allocate memory for save info container");
385 sqlite3_finalize(stmt);
386 return NULL;
387 }
388
389 if (save_count == 0) {
390 sqlite3_finalize(stmt);
391 save_infos->count = 0;
392 save_infos->infos = NULL;
393 return save_infos;
394 }
395
396 save_infos->count = save_count;
397 save_infos->infos = malloc(sizeof(save_info_t) * save_count);
398 if (save_infos->infos == NULL) {
399 log_msg(ERROR, "GameState", "Failed to allocate memory for save infos");
400 free(save_infos);
401 sqlite3_finalize(stmt);
402 return NULL;
403 }
404
405
406 // Retrieve save information
407 int index = 0;
408 while (sqlite3_step(stmt) == SQLITE_ROW) {
409 save_infos->infos[index].id = sqlite3_column_int(stmt, 0);
410
411 snprintf(save_infos->infos[index].timestamp, TIMESTAMP_LENGTH, "%s", (const char*) sqlite3_column_text(stmt, 1));
412 snprintf(save_infos->infos[index].name, MAX_STRING_LENGTH, "%s", (const char*) sqlite3_column_text(stmt, 2));
413 index++;
414 }
415
416 sqlite3_finalize(stmt);
417
418 return save_infos;
419}

◆ save_game_state()

sqlite_int64 save_game_state ( const db_connection_t * db_connection,
const int * map,
const int * revealed_map,
int width,
int height,
vector2d_t player,
const char * save_name )

Save the game state.

Parameters
db_connectionA database connection.
mapThe current map where nothing is hidden.
revealed_mapThe current state of the revealed map.
widthThe width of the map.
heightThe hight of the map.
playerThe player position.
save_nameA name for the save file.
Returns
0 on success none 0 on failure.

Definition at line 31 of file gamestate_database.c.

31 {
32 // Check if the database connection is open
33 if (!db_is_open(db_connection)) {
34 log_msg(ERROR, "GameState", "Database connection is not open");
35 return 0;
36 }
37
38 // Save the game state to the database into table game_state
39 // Get the current time
40 char* current_time = get_iso8601_time();
41 if (current_time == NULL) {
42 log_msg(ERROR, "GameState", "Failed to get current time");
43 return 0;
44 }
45
46 // Prepare the SQL statement
47 sqlite3_stmt* stmt;
48 int rc = sqlite3_prepare_v2(db_connection->db, SQL_INSERT_GAME_STATE, -1, &stmt, NULL);
49 if (rc != SQLITE_OK) {
50 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
51 free(current_time);
52 return 0;
53 }
54
55 // Bind the current time to the statement
56 rc = sqlite3_bind_text(stmt, 1, current_time, -1, SQLITE_TRANSIENT);
57 if (rc != SQLITE_OK) {
58 log_msg(ERROR, "GameState", "Failed to bind time: %s", sqlite3_errmsg(db_connection->db));
59 sqlite3_finalize(stmt);
60 free(current_time);
61 return 0;
62 }
63
64 // We can free current_time after binding
65 free(current_time);
66
67 // Bind the save name to the statement
68 rc = sqlite3_bind_text(stmt, 2, save_name, -1, SQLITE_TRANSIENT);
69 if (rc != SQLITE_OK) {
70 log_msg(ERROR, "GameState", "Failed to bind save name: %s", sqlite3_errmsg(db_connection->db));
71 sqlite3_finalize(stmt);
72 return 0;
73 }
74
75 // Execute the statement
76 rc = sqlite3_step(stmt);
77 if (rc != SQLITE_DONE) {
78 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
79 }
80 // Get the last inserted row ID
81 const sqlite3_int64 game_state_id = sqlite3_last_insert_rowid(db_connection->db);
82
83 // Finalize the statement
84 sqlite3_finalize(stmt);
85
86 // Save the map and revealed map to the database
87 char* map_json = arr2D_to_flat_json(map, width, height);
88 char* revealed_map_json = arr2D_to_flat_json(revealed_map, width, height);
89 if (map_json == NULL || revealed_map_json == NULL) {
90 free(map_json);// Safe to call on NULL
91 free(revealed_map_json);
92 return 0;
93 }
94
95 // Prepare the SQL statement
96 sqlite3_stmt* stmt_map;
97 rc = sqlite3_prepare_v2(db_connection->db, SQL_INSERT_MAP_STATE, -1, &stmt_map, NULL);
98 if (rc != SQLITE_OK) {
99 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
100 free(map_json);
101 free(revealed_map_json);
102 return 0;
103 }
104
105 // Bind the map and revealed map to the statement
106 rc = sqlite3_bind_text(stmt_map, 1, map_json, -1, SQLITE_TRANSIENT);
107 if (rc != SQLITE_OK) {
108 log_msg(ERROR, "GameState", "Failed to bind map: %s", sqlite3_errmsg(db_connection->db));
109 sqlite3_finalize(stmt_map);
110 free(map_json);
111 free(revealed_map_json);
112 return 0;
113 }
114
115 rc = sqlite3_bind_text(stmt_map, 2, revealed_map_json, -1, SQLITE_TRANSIENT);
116 if (rc != SQLITE_OK) {
117 log_msg(ERROR, "GameState", "Failed to bind revealed map: %s", sqlite3_errmsg(db_connection->db));
118 sqlite3_finalize(stmt_map);
119 free(map_json);
120 free(revealed_map_json);
121 return 0;
122 }
123
124 // We can free JSON strings after binding
125 free(map_json);
126 free(revealed_map_json);
127
128 rc = sqlite3_bind_int(stmt_map, 3, height);
129 if (rc != SQLITE_OK) {
130 log_msg(ERROR, "GameState", "Failed to bind height: %s", sqlite3_errmsg(db_connection->db));
131 sqlite3_finalize(stmt_map);
132 return 0;
133 }
134 rc = sqlite3_bind_int(stmt_map, 4, width);
135 if (rc != SQLITE_OK) {
136 log_msg(ERROR, "GameState", "Failed to bind width: %s", sqlite3_errmsg(db_connection->db));
137 sqlite3_finalize(stmt_map);
138 return 0;
139 }
140 rc = sqlite3_bind_int64(stmt_map, 5, game_state_id);
141 if (rc != SQLITE_OK) {
142 log_msg(ERROR, "GameState", "Failed to bind game state ID: %s", sqlite3_errmsg(db_connection->db));
143 sqlite3_finalize(stmt_map);
144 return 0;
145 }
146 // Execute the statement
147 rc = sqlite3_step(stmt_map);
148 if (rc != SQLITE_DONE) {
149 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
150 }
151 // Finalize the statement
152 sqlite3_finalize(stmt_map);
153
154 // Save the player position to the database
155 sqlite3_stmt* stmt_player;
156 rc = sqlite3_prepare_v2(db_connection->db, SQL_INSERT_PLAYER_STATE, -1, &stmt_player, NULL);
157 if (rc != SQLITE_OK) {
158 log_msg(ERROR, "GameState", "Failed to prepare statement: %s", sqlite3_errmsg(db_connection->db));
159 return 0;
160 }
161 // Bind the player position to the statement
162 rc = sqlite3_bind_int(stmt_player, 1, player.dx);
163 if (rc != SQLITE_OK) {
164 log_msg(ERROR, "GameState", "Failed to bind player x: %s", sqlite3_errmsg(db_connection->db));
165 sqlite3_finalize(stmt_player);
166 return 0;
167 }
168 rc = sqlite3_bind_int(stmt_player, 2, player.dy);
169 if (rc != SQLITE_OK) {
170 log_msg(ERROR, "GameState", "Failed to bind player y: %s", sqlite3_errmsg(db_connection->db));
171 sqlite3_finalize(stmt_player);
172 return 0;
173 }
174 rc = sqlite3_bind_int64(stmt_player, 3, game_state_id);
175 if (rc != SQLITE_OK) {
176 log_msg(ERROR, "GameState", "Failed to bind game state ID: %s", sqlite3_errmsg(db_connection->db));
177 sqlite3_finalize(stmt_player);
178 return 0;
179 }
180 // Execute the statement
181 rc = sqlite3_step(stmt_player);
182 if (rc != SQLITE_DONE) {
183 log_msg(ERROR, "GameState", "Failed to execute statement: %s", sqlite3_errmsg(db_connection->db));
184 }
185 // Finalize the statement
186 sqlite3_finalize(stmt_player);
187 return game_state_id;
188}
char * arr2D_to_flat_json(const int *arr, const int width, const int height)
Convert a 2D array to a Json-Array.
char * get_iso8601_time()
Get the current time in ISO 8601 format.