/* wMUD - Yet another MUD codebase by W00d5t0ck * Copyright (C) 2012 - Gergely POLONKAI * * db.c: database handling routines * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "world.h" #include "main.h" #include "db.h" #include "players.h" #include "configuration.h" #include "menu.h" #include "wmudplayer.h" /** * SECTION:db * @short_description: Database handling * @title: Database handling routines * * This module handles all the database requests. It currently uses an SQLite3 * backend, but due to its nature, it could be easily rewritten to use a * different (e.g MySQL or PostgreSQL) database. */ static GdaConnection *dbh = NULL; static GdaSqlParser *parser = NULL; GQuark wmud_db_error_quark() { return g_quark_from_static_string("wmud-db-error"); } /** * wmud_db_init: * @err: a GError to put error messages in it * * Initializes the wMUD database system. Checks and opens database files. */ gboolean wmud_db_init(GError **err) { GError *local_err = NULL; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Initializing database"); gda_init(); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Database DSN is \"%s\"", active_config->database_dsn); dbh = gda_connection_open_from_string(NULL, active_config->database_dsn, NULL, GDA_CONNECTION_OPTIONS_THREAD_SAFE, &local_err); if (dbh == NULL) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_CANTOPEN, "Can not open databsae (%s): %s", active_config->database_dsn, local_err->message); return FALSE; } parser = gda_sql_parser_new(); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Database initialization finished."); return TRUE; } /** * wmud_db_load_players: * @err: a GError to put error messages in it * * Loads all player records from the database */ gboolean wmud_db_load_players(GError **err) { GdaStatement *sth = NULL; GdaDataModel *res; GdaDataModelIter *iter; GError *local_err = NULL; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loading players"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } sth = gda_sql_parser_parse_string( parser, "SELECT id, login, password, email FROM players", NULL, NULL); /* TODO: error checking! */ if ((res = gda_connection_statement_execute_select( dbh, sth, NULL, &local_err)) == NULL) { g_log(G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "Unable to load players: %s", local_err->message); g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_SELECT_ERROR, "SELECT error: %s", local_err->message); return FALSE; } iter = gda_data_model_create_iter(res); gda_data_model_iter_move_next(iter); while (gda_data_model_iter_is_valid(iter)) { const GValue *val; WmudPlayer *player; player = wmud_player_new(); val = gda_data_model_iter_get_value_at(iter, 0); wmud_player_set_id(player, g_value_get_int(val)); val = gda_data_model_iter_get_value_at(iter, 1); wmud_player_set_player_name(player, g_value_get_string(val)); val = gda_data_model_iter_get_value_at(iter, 2); wmud_player_set_cpassword(player, g_value_get_string(val)); val = gda_data_model_iter_get_value_at(iter, 3); wmud_player_set_email(player, g_value_get_string(val)); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loaded player _%s_", wmud_player_get_player_name(player)); players = g_slist_prepend(players, player); gda_data_model_iter_move_next(iter); } g_object_unref(iter); g_object_unref(sth); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Finished loading players"); return TRUE; } /** * wmud_db_save_player: * @player: the player record to save * @err: a GError to put error messages in it * * Saves a player record to the database backend. * * Return value: %TRUE on success. Upon failure, %FALSE is returned, and err is * set accordingly. */ gboolean wmud_db_save_player(WmudPlayer *player, GError **err) { GError *local_err = NULL; GValue *login_value, *email_value; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Saving player"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } login_value = gda_value_new_from_string( wmud_player_get_player_name(player), G_TYPE_STRING); email_value = gda_value_new_from_string( wmud_player_get_email(player), G_TYPE_STRING); if (!gda_connection_insert_row_into_table(dbh, "players", &local_err, "id", NULL, "login", login_value, "password", NULL, "email", email_value, NULL)) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_BADQUERY, "Error saving player: %s", local_err->message); return FALSE; } return TRUE; } gboolean wmud_db_update_player_password(WmudPlayer *player, gchar *crypted_password, GError **err) { GValue *cpw, *player_id; GError *local_err = NULL; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Saving player password for %s", wmud_player_get_player_name(player)); if (dbh == NULL) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); return FALSE; } player_id = gda_value_new(G_TYPE_UINT); g_value_set_uint(player_id, wmud_player_get_id(player)); cpw = gda_value_new_from_string(crypted_password, G_TYPE_STRING); if (!gda_connection_update_row_in_table(dbh, "players", "id", player_id, &local_err, "password", cpw, NULL)) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_BADQUERY, "Error saving player password: %s", local_err->message); return FALSE; } return TRUE; } gboolean wmud_db_load_planes(GSList **planes, GError **err) { GdaStatement *sth = NULL; GdaDataModel *res = NULL; GdaDataModelIter *iter; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loading planes"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } sth = gda_sql_parser_parse_string(parser, "SELECT id, name FROM planes", NULL, NULL); res = gda_connection_statement_execute_select(dbh, sth, NULL, NULL); iter = gda_data_model_create_iter(res); gda_data_model_iter_move_next(iter); while (gda_data_model_iter_is_valid(iter)) { const GValue *val; wmudPlane *plane; plane = g_new0(wmudPlane, 1); val = gda_data_model_iter_get_value_at(iter, 0); plane->id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 1); plane->name = g_strdup(g_value_get_string(val)); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loaded plane _%s_", plane->name); *planes = g_slist_prepend(*planes, plane); gda_data_model_iter_move_next(iter); } g_object_unref(iter); g_object_unref(sth); return TRUE; } gboolean wmud_db_load_planets(GSList **planets, GError **err) { GdaStatement *sth = NULL; GdaDataModel *res = NULL; GdaDataModelIter *iter; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loading planets"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } sth = gda_sql_parser_parse_string(parser, "SELECT id, name FROM planets", NULL, NULL); res = gda_connection_statement_execute_select(dbh, sth, NULL, NULL); iter = gda_data_model_create_iter(res); gda_data_model_iter_move_next(iter); while (gda_data_model_iter_is_valid(iter)) { const GValue *val; wmudPlanet *planet; planet = g_new0(wmudPlanet, 1); val = gda_data_model_iter_get_value_at(iter, 0); planet->id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 1); planet->name = g_strdup(g_value_get_string(val)); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loaded planet _%s_", planet->name); *planets = g_slist_prepend(*planets, planet); gda_data_model_iter_move_next(iter); } g_object_unref(iter); g_object_unref(sth); return TRUE; } gboolean wmud_db_load_directions(GSList **directions, GError **err) { GdaStatement *sth = NULL; GdaDataModel *res = NULL; GdaDataModelIter *iter; GError *local_err = NULL; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loading directions"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } sth = gda_sql_parser_parse_string( parser, "SELECT id, short_name, name FROM directions", NULL, NULL); if ((res = gda_connection_statement_execute_select( dbh, sth, NULL, &local_err)) == NULL) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_SELECT_ERROR, "Unable to load directions: %s", local_err->message); return FALSE; } iter = gda_data_model_create_iter(res); gda_data_model_iter_move_next(iter); while (gda_data_model_iter_is_valid(iter)) { const GValue *val; wmudDirection *dir; dir = g_new0(wmudDirection, 1); val = gda_data_model_iter_get_value_at(iter, 0); dir->id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 1); dir->short_name = g_strdup(g_value_get_string(val)); val = gda_data_model_iter_get_value_at(iter, 2); dir->name = g_strdup(g_value_get_string(val)); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loaded direction _%s_", dir->name); *directions = g_slist_prepend(*directions, dir); gda_data_model_iter_move_next(iter); } g_object_unref(iter); g_object_unref(sth); return TRUE; } gboolean wmud_db_load_areas(GSList **areas, GError **err) { GdaStatement *sth = NULL; GdaDataModel *res = NULL; GdaDataModelIter *iter; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loading areas"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } sth = gda_sql_parser_parse_string( parser, "SELECT id, name FROM areas", NULL, NULL); res = gda_connection_statement_execute_select(dbh, sth, NULL, NULL); iter = gda_data_model_create_iter(res); gda_data_model_iter_move_next(iter); while (gda_data_model_iter_is_valid(iter)) { const GValue *val; wmudArea *area; area = g_new0(wmudArea, 1); val = gda_data_model_iter_get_value_at(iter, 0); area->id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 1); area->name = g_strdup(g_value_get_string(val)); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loaded area _%s_", area->name); *areas = g_slist_prepend(*areas, area); gda_data_model_iter_move_next(iter); } g_object_unref(iter); g_object_unref(sth); return TRUE; } gboolean wmud_db_load_rooms(GSList **rooms, GError **err) { GdaStatement *sth = NULL; GdaDataModel *res = NULL; GdaDataModelIter *iter; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loading rooms"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } sth = gda_sql_parser_parse_string( parser, "SELECT id, area, name, distant_description, close_description FROM rooms", NULL, NULL); res = gda_connection_statement_execute_select(dbh, sth, NULL, NULL); iter = gda_data_model_create_iter(res); gda_data_model_iter_move_next(iter); while (gda_data_model_iter_is_valid(iter)) { const GValue *val; wmudRoom *room; room = g_new0(wmudRoom, 1); val = gda_data_model_iter_get_value_at(iter, 0); room->id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 1); room->area_id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 2); room->name = g_strdup(g_value_get_string(val)); val = gda_data_model_iter_get_value_at(iter, 3); room->distant_description = g_strdup(g_value_get_string(val)); val = gda_data_model_iter_get_value_at(iter, 4); room->close_description = g_strdup(g_value_get_string(val)); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loaded room %d/_%s_", room->area_id, room->name); *rooms = g_slist_prepend(*rooms, room); gda_data_model_iter_move_next(iter); } g_object_unref(iter); g_object_unref(sth); return TRUE; } gboolean wmud_db_load_exits(GSList **exits, GError **err) { GdaStatement *sth = NULL; GdaDataModel *res = NULL; GdaDataModelIter *iter; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loading exits"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } sth = gda_sql_parser_parse_string( parser, "SELECT room_id, direction, other_side FROM room_exits", NULL, NULL); res = gda_connection_statement_execute_select(dbh, sth, NULL, NULL); iter = gda_data_model_create_iter(res); gda_data_model_iter_move_next(iter); while (gda_data_model_iter_is_valid(iter)) { const GValue *val; wmudExit *room_exit; room_exit = g_new0(wmudExit, 1); val = gda_data_model_iter_get_value_at(iter, 0); room_exit->source_room_id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 1); room_exit->direction_id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 2); room_exit->destination_room_id = g_value_get_int(val); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loaded exit %d =%d=> %d", room_exit->source_room_id, room_exit->direction_id, room_exit->destination_room_id); *exits = g_slist_prepend(*exits, room_exit); gda_data_model_iter_move_next(iter); } g_object_unref(iter); g_object_unref(sth); return TRUE; } gboolean wmud_db_load_planet_planes(GSList **planet_planes, GError **err) { GdaStatement *sth = NULL; GdaDataModel *res = NULL; GdaDataModelIter *iter; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loading planet<->plane associations"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } sth = gda_sql_parser_parse_string( parser, "SELECT planet_id, plane_id FROM planet_planes", NULL, NULL); res = gda_connection_statement_execute_select(dbh, sth, NULL, NULL); iter = gda_data_model_create_iter(res); gda_data_model_iter_move_next(iter); while (gda_data_model_iter_is_valid(iter)) { const GValue *val; wmudPlanetPlaneAssoc *planet_plane; planet_plane = g_new0(wmudPlanetPlaneAssoc, 1); val = gda_data_model_iter_get_value_at(iter, 0); planet_plane->planet_id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 1); planet_plane->plane_id = g_value_get_int(val); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loaded planet-plane association %d <> %d", planet_plane->planet_id, planet_plane->plane_id); *planet_planes = g_slist_prepend(*planet_planes, planet_plane); gda_data_model_iter_move_next(iter); } g_object_unref(iter); g_object_unref(sth); return TRUE; } gboolean wmud_db_load_menu(GSList **menu_items, GError **err) { GdaStatement *sth = NULL; GdaDataModel *res = NULL; GdaDataModelIter *iter; g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loading menu items"); if (dbh == NULL) { if (err) { g_set_error(err, WMUD_DB_ERROR, WMUD_DB_ERROR_NOINIT, "Database backend not initialized"); } return FALSE; } sth = gda_sql_parser_parse_string( parser, "SELECT id, menuchar, need_active_char, placement, display_text, fnctn FROM menu ORDER BY placement", NULL, NULL); res = gda_connection_statement_execute_select(dbh, sth, NULL, NULL); iter = gda_data_model_create_iter(res); gda_data_model_iter_move_next(iter); while (gda_data_model_iter_is_valid(iter)) { const GValue *val; wmudMenu *menu_item; menu_item = g_new0(wmudMenu, 1); val = gda_data_model_iter_get_value_at(iter, 0); menu_item->id = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 1); menu_item->menuchar = *(g_value_get_string(val)); val = gda_data_model_iter_get_value_at(iter, 2); menu_item->need_active_char = g_value_get_boolean(val); val = gda_data_model_iter_get_value_at(iter, 3); menu_item->placement = g_value_get_int(val); val = gda_data_model_iter_get_value_at(iter, 4); menu_item->text = g_strdup(g_value_get_string(val)); val = gda_data_model_iter_get_value_at(iter, 5); menu_item->func = g_strdup(g_value_get_string(val)); g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Loaded menu item %d: %s(%c)", menu_item->id, menu_item->text, menu_item->menuchar); *menu_items = g_slist_prepend(*menu_items, menu_item); gda_data_model_iter_move_next(iter); } g_object_unref(iter); g_object_unref(sth); return TRUE; }