diff --git a/docs/reference/wmud/wmud-docs.sgml b/docs/reference/wmud/wmud-docs.sgml index 9506fb8..02f2355 100644 --- a/docs/reference/wmud/wmud-docs.sgml +++ b/docs/reference/wmud/wmud-docs.sgml @@ -18,24 +18,28 @@ wMUD hackers' guide - + + + + + - + API Index - + diff --git a/docs/reference/wmud/wmud-sections.txt b/docs/reference/wmud/wmud-sections.txt index 43939f4..ff42dae 100644 --- a/docs/reference/wmud/wmud-sections.txt +++ b/docs/reference/wmud/wmud-sections.txt @@ -1,21 +1,45 @@
-Data types +wMUD's Data Types types -wmudClientState -wmudClient -wmudPlayer
-Utilities and uncategorized functions + utils random_number wmud_random_string
-Networking -networking + +configuration +WMUD_CONFIG_ERROR +wmudConfigError +ConfigData +active_config +wmud_config_init +
+ +
+ +maintenance-thread +wmud_maintenance_init +
+ +
+ +game-thread +wmudClientState +wmudClient +elapsed_seconds +elapsed_cycle +game_context +wmud_game_init +
+ +
+ +game-networking TELNET_IAC TELNET_WILL TELNET_WONT @@ -26,7 +50,7 @@ wmud_client_send
-Command interpreter + interpreter wmudCommandFunc wmudCommand @@ -35,7 +59,7 @@ wmud_interpret_game_command
-Database handling + db WMUD_DB_ERROR wmudDbError @@ -45,12 +69,17 @@ wmud_db_save_player
-Player handling + player +wmudPlayer players wmud_player_dup wmud_player_free -wmud_player_exists wmud_player_auth +wmud_player_exists
+
+ +world +
diff --git a/src/Makefile.am b/src/Makefile.am index 2b58d81..acbf9da 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ bin_PROGRAMS = wmud AM_CPPFLAGS = -DWMUD_STATEDIR=\""$(localstatedir)"\" -DWMUD_CONFDIR=\""$(sysconfdir)"\" $(MEMCACHED_CFLAGS) $(GLIB_CFLAGS) $(GIO_CFLAGS) $(GTHREAD_CFLAGS) $(SQLITE3_CFLAGS) -wmud_SOURCES = main.c game-networking.c interpreter.c db.c players.c maintenance.c +wmud_SOURCES = main.c game-networking.c interpreter.c db.c players.c maintenance.c game.c configuration.c world.c wmud_LDADD = $(MEMCACHED_LIBS) $(GLIB_LIBS) $(GIO_LIBS) $(GTHREAD_LIBS) $(SQLITE3_LIBS) diff --git a/src/configuration.c b/src/configuration.c new file mode 100644 index 0000000..56ec439 --- /dev/null +++ b/src/configuration.c @@ -0,0 +1,169 @@ +/* wMUD - Yet another MUD codebase by W00d5t0ck + * Copyright (C) 2012 - Gergely POLONKAI + * + * configuration.c: configuration file related functions + * + * 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 . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "wmud-types.h" +#include "configuration.h" + +/** + * SECTION:configuration + * @short_description: Configuration file handling functions + * @title: Configuration file handling functions + * + */ + +/** + * WMUD_CONFIG_ERROR: + * + * the GQuark for the config error GError + */ +GQuark WMUD_CONFIG_ERROR = 0; +/** + * active_config: + * + * the currently active configuration directives + */ +ConfigData *active_config = NULL; + +/** + * wmud_configdata_free: + * @config_data: pointer to the ConfigData struct to free + * + * Correctly free a ConfigData struct with all its members + */ +void +wmud_configdata_free(ConfigData **config_data) +{ + if ((*config_data)->admin_email) + g_free((*config_data)->admin_email); + + if ((*config_data)->database_file) + g_free((*config_data)->database_file); + + g_free(*config_data); + *config_data = NULL; +} + +/** + * wmud_config_init: + * @config_data: a pointer to a ConfigData struct. This will be filled with the + * configuration file's data + * @err: The GError in which the config handling status should be returned + * + * Parses the default configuration file, and sets different variables + * according to it. + * + * Return value: %TRUE if parsing was successful. %FALSE otherwise. + */ +gboolean +wmud_config_init(ConfigData **config_data, GError **err) +{ + GString *config_file = g_string_new(WMUD_CONFDIR); + GKeyFile *config; + GError *in_err = NULL; + + if (!config_data) + return FALSE; + + if (!*config_data) + { + g_clear_error(err); + g_set_error(err, WMUD_CONFIG_ERROR, WMUD_CONFIG_ERROR_REUSE, "Configuration pointer reuse. Please file a bug report!"); + return FALSE; + } + + *config_data = g_new0(ConfigData, 1); + + g_string_append(config_file, "/wmud.conf"); + + config = g_key_file_new(); + /* TODO: Error checking */ + g_key_file_load_from_file(config, config_file->str, 0, &in_err); + + if (!g_key_file_has_group(config, "global")) + { + g_set_error(err, WMUD_CONFIG_ERROR, WMUD_CONFIG_ERROR_NOGLOBAL, "Config file (%s) does not contain a [global] group", config_file->str); + g_key_file_free(config); + g_string_free(config_file, TRUE); + + return FALSE; + } + + g_clear_error(&in_err); + (*config_data)->port = g_key_file_get_integer(config, "global", "port", &in_err); + if (in_err) + { + if (g_error_matches(in_err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) + { + (*config_data)->port = DEFAULT_PORT; + } + else if (g_error_matches(in_err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE)) + { + g_set_error(err, WMUD_CONFIG_ERROR, WMUD_CONFIG_ERROR_BADPORT, "Config file (%s) contains an invalid port number", config_file->str); + g_key_file_free(config); + g_string_free(config_file, TRUE); + (*config_data)->port = 0; + + return FALSE; + } + + return FALSE; + } + + g_clear_error(&in_err); + (*config_data)->database_file = g_key_file_get_string(config, "global", "world file", &in_err); + if (in_err) + { + if (g_error_matches(in_err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) + { + g_set_error(err, WMUD_CONFIG_ERROR, WMUD_CONFIG_ERROR_NOWORLD, "Config file (%s) does not contain a world file path", config_file->str); + g_key_file_free(config); + g_string_free(config_file, TRUE); + wmud_configdata_free(config_data); + + return FALSE; + } + } + + g_clear_error(&in_err); + (*config_data)->admin_email = g_key_file_get_string(config, "global", "admin email", &in_err); + if (in_err) + { + if (g_error_matches(in_err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) + { + g_set_error(err, WMUD_CONFIG_ERROR, WMUD_CONFIG_ERROR_NOEMAIL, "Config file (%s) does not contain an admin e-mail address", config_file->str); + g_key_file_free(config); + g_string_free(config_file, TRUE); + wmud_configdata_free(config_data); + + return FALSE; + } + } + + g_key_file_free(config); + g_string_free(config_file, TRUE); + + return TRUE; +} + diff --git a/src/configuration.h b/src/configuration.h new file mode 100644 index 0000000..e0329f9 --- /dev/null +++ b/src/configuration.h @@ -0,0 +1,48 @@ +#ifndef __WMUD_CONFIGURATION_H__ +#define __WMUD_CONFIGURATION_H__ + +#include + +extern GQuark WMUD_CONFIG_ERROR; + +/** + * wmudConfigError: + * @WMUD_CONFIG_ERROR_NOGLOBAL: Indicates that the config file read doesn't + * contain a [global] section + * @WMUD_CONFIG_ERROR_BADPORT: Indicates that the config file contains and + * invalid port number + * @WMUD_CONFIG_ERROR_NOWORLD: Indicates that the config file doesn't contain a + * world database file + * @WMUD_CONFIG_ERROR_NOEMAIL: Indicates that the config file doesn't contain + * an administrator e-mail address + * @WMUD_CONFIG_ERROR_REUSE: configuration data is reused (non-NULL) + * + * Error codes returned by configuration file parsing functions. + */ +typedef enum { + WMUD_CONFIG_ERROR_NOGLOBAL, + WMUD_CONFIG_ERROR_BADPORT, + WMUD_CONFIG_ERROR_NOWORLD, + WMUD_CONFIG_ERROR_NOEMAIL, + WMUD_CONFIG_ERROR_REUSE +} wmudConfigError; + +/** + * ConfigData: + * @port: the port number of the game interface to listen on + * @database_file: the database file of the world associated with this + * configuration + * @admin_email: the world administrator's e-mail address + */ +typedef struct _ConfigData { + guint port; + gchar *database_file; + gchar *admin_email; +} ConfigData; + +extern ConfigData *active_config; + +gboolean wmud_config_init(ConfigData **config_data, GError **err); + +#endif /* __WMUD_CONFIGURATION_H__ */ + diff --git a/src/db.c b/src/db.c index 0fd3614..bc21163 100644 --- a/src/db.c +++ b/src/db.c @@ -16,12 +16,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #include #include #include "main.h" #include "db.h" #include "players.h" +#include "configuration.h" /** * SECTION:db @@ -30,7 +32,13 @@ * */ -sqlite3 *dbh = NULL; +/** + * WMUD_DB_ERROR: + * + * the GQuark for the database error GError + */ +GQuark WMUD_DB_ERROR = 0; +static sqlite3 *dbh = NULL; /** * wmud_db_init: @@ -44,7 +52,7 @@ wmud_db_init(GError **err) GString *db_file = g_string_new(WMUD_STATEDIR); int sqlite_code; - g_string_append_printf(db_file, "/%s", database_file); + g_string_append_printf(db_file, "/%s", active_config->database_file); if ((sqlite_code = sqlite3_open(db_file->str, &dbh)) != SQLITE_OK) { @@ -170,3 +178,39 @@ wmud_db_save_player(wmudPlayer *player, GError **err) return TRUE; } +gboolean +wmud_db_load_planes(GSList **planes, GError **err) +{ + return FALSE; +} + +gboolean +wmud_db_load_planets(GSList **planets, GError **err) +{ + return FALSE; +} + +gboolean +wmud_db_load_directions(GSList **directions, GError **err) +{ + return FALSE; +} + +gboolean +wmud_db_load_areas(GSList **areas, GError **err) +{ + return FALSE; +} + +gboolean +wmud_db_load_rooms(GSList **rooms, GError **err) +{ + return FALSE; +} + +gboolean +wmud_db_load_exits(GSList **exits, GError **err) +{ + return FALSE; +} + diff --git a/src/db.h b/src/db.h index d6c5ad4..38da5ca 100644 --- a/src/db.h +++ b/src/db.h @@ -23,9 +23,17 @@ #include "wmud-types.h" +extern GQuark WMUD_DB_ERROR; + gboolean wmud_db_init(GError **err); gboolean wmud_db_players_load(GError **err); gboolean wmud_db_save_player(wmudPlayer *player, GError **err); +gboolean wmud_db_load_planes(GSList **planes, GError **err); +gboolean wmud_db_load_planets(GSList **planets, GError **err); +gboolean wmud_db_load_directions(GSList **directions, GError **err); +gboolean wmud_db_load_areas(GSList **areas, GError **err); +gboolean wmud_db_load_rooms(GSList **rooms, GError **err); +gboolean wmud_db_load_exits(GSList **exits, GError **err); #endif /* __WMUD__DB_H__ */ diff --git a/src/game-networking.c b/src/game-networking.c index f2b244d..51544a7 100644 --- a/src/game-networking.c +++ b/src/game-networking.c @@ -30,9 +30,10 @@ #include "interpreter.h" #include "players.h" #include "db.h" +#include "configuration.h" /** - * SECTION:networking + * SECTION:game-networking * @short_description: Game related networking code * * Functions to handle game connections @@ -85,6 +86,8 @@ static gboolean wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient *client) { GError *err = NULL; + /* TODO: Error checking */ + GRegex *email_regex = g_regex_new("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$", G_REGEX_CASELESS, 0, NULL); if (condition & G_IO_HUP) { @@ -135,7 +138,32 @@ wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient { case WMUD_CLIENT_STATE_FRESH: if (*(client->buffer->str)) - wmud_client_start_login(client); + { + wmudPlayer *player; + + if ((player = wmud_player_exists(client->buffer->str)) != NULL) + { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Trying to login with playername '%s'\n", client->buffer->str); + if (player->cpassword == NULL) + { + wmud_client_send(client, "Your registration is not finished yet.\r\n"); + wmud_client_close(client, TRUE); + } + else + { + client->state = WMUD_CLIENT_STATE_PASSWAIT; + client->player = player; + wmud_client_send(client, "Please provide us your password: %c%c%c", TELNET_IAC, TELNET_WILL, TELNET_ECHO); + } + } + else + { + client->player = g_new0(wmudPlayer, 1); + client->player->player_name = g_strdup(client->buffer->str); + client->state = WMUD_CLIENT_STATE_NEWCHAR; + wmud_client_send(client, "Is %s new to this game? [Y/N] ", client->buffer->str); + } + } break; case WMUD_CLIENT_STATE_PASSWAIT: if (*(client->buffer->str)) @@ -144,7 +172,7 @@ wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient { wmud_client_send(client, "%c%c%cLogin" " successful.\r\n", TELNET_IAC, - TELNET_WILL, TELNET_ECHO); + TELNET_WONT, TELNET_ECHO); client->authenticated = TRUE; /* TODO: Send fail count if non-zero */ client->state = WMUD_CLIENT_STATE_MENU; @@ -158,7 +186,7 @@ wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient " password doesn't seem to be valid." " Let's try it again...\r\nBy what" " name would you like to be called? ", - TELNET_IAC, TELNET_WILL, TELNET_ECHO); + TELNET_IAC, TELNET_WONT, TELNET_ECHO); client->state = WMUD_CLIENT_STATE_FRESH; client->login_try_count++; if (client->login_try_count == 3) @@ -170,7 +198,7 @@ wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient wmud_client_close(client, TRUE); /* TODO: Increase IP fail count, and ban IP if it's too high */ } - /* TODO: Increase and save player fail count */ + /* TODO: Increase and save login fail count */ client->player = NULL; } } @@ -190,13 +218,68 @@ wmud_client_callback(GSocket *client_socket, GIOCondition condition, wmudClient //wmud_interpret_quit_answer(client); break; case WMUD_CLIENT_STATE_NEWCHAR: - wmud_client_interpret_newplayer_answer(client); + if (g_ascii_strcasecmp(client->buffer->str, "n") == 0) + { + wmud_client_send(client, "What is your player-name, then? "); + client->state = WMUD_CLIENT_STATE_FRESH; + } + else if (g_ascii_strcasecmp(client->buffer->str, "y") == 0) + { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Creating new player\n"); + wmud_client_send(client, "Welcome to this MUD!\r\nPlease enter your e-mail address: "); + client->state = WMUD_CLIENT_STATE_REGISTERING; + } + else + { + wmud_client_send(client, "Sorry, but for this question I only understand 'Y' or 'N'.\r\nIs %s a new player here? [Y/N] ", client->player->player_name); + } break; case WMUD_CLIENT_STATE_REGISTERING: - wmud_client_interpret_newplayer_email(client); + if (!*(client->buffer->str)) + { + if (client->bademail) + { + wmud_client_close(client, TRUE); + } + } + + if (g_regex_match(email_regex, client->buffer->str, 0, NULL)) + { + client->player->email = g_strdup(client->buffer->str); + client->state = WMUD_CLIENT_STATE_REGEMAIL_CONFIRM; + wmud_client_send(client, "It seems to be a valid address to me, but could you write it again? "); + } + else + { + wmud_client_send(client, "\r\nSorry, but this e-mail address doesn't seem to be valid to me.\r\n\r\nIf you think this is a valid address, simply press enter to quit, and send an e-mail to %s from that address, so we can fix our e-mail validation code.\r\n\r\nIf you just mistyped your address, type it now: ", active_config->admin_email); + if (*(client->buffer->str)) + client->bademail = TRUE; + } break; case WMUD_CLIENT_STATE_REGEMAIL_CONFIRM: - wmud_client_interpret_newplayer_mailconfirm(client); + if (g_ascii_strcasecmp(client->player->email, client->buffer->str) == 0) + { + g_clear_error(&err); + if (wmud_db_save_player(client->player, &err)) + { + wmud_client_send(client, "Good. We will generate the password for this player name, and send it to you\r\nvia e-mail. Please come back to us, if you get that code, so you can log\r\nin.\r\n"); + players = g_slist_prepend(players, wmud_player_dup(client->player)); + } + else + { + g_critical("wmud_db_save_player() error: %s", err->message); + wmud_client_send(client, "There was an error during the database update. Please try again later!\r\n"); + } + wmud_client_close(client, TRUE); + } + else + { + g_free(client->player->email); + client->player->email = NULL; + + wmud_client_send(client, "This is not the same as you entered before.\r\nLet's just try it again: "); + client->state = WMUD_CLIENT_STATE_REGISTERING; + } break; } g_string_erase(client->buffer, 0, -1); @@ -263,6 +346,7 @@ game_source_callback(GSocket *socket, GIOCondition condition, struct AcceptData /** * wmud_networking_init: * @port_number: the port number on which the game listener should listen + * @game_context: the #GMainContext of the game thread * @err: the GError in which possible errors will be reported * * Initializes the game network listener @@ -271,7 +355,7 @@ game_source_callback(GSocket *socket, GIOCondition condition, struct AcceptData * err is set accordingly (if not NULL) */ gboolean -wmud_networking_init(guint port_number, GError **err) +wmud_networking_init(guint port_number, GMainContext *game_context, GError **err) { struct AcceptData *accept_data; GSocketListener *game_listener; @@ -405,136 +489,3 @@ wmud_client_send(wmudClient *client, const gchar *fmt, ...) g_string_free(buf, TRUE); } -/** - * wmud_client_start_login: - * @client: the client from which the login name came from - * - * This function is currently called when the freshly connected client sends a - * non-empty string (a player name). - */ -void -wmud_client_start_login(wmudClient *client) -{ - wmudPlayer *player; - - if ((player = wmud_player_exists(client->buffer->str)) != NULL) - { - g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Trying to login with playername '%s'\n", client->buffer->str); - if (player->cpassword == NULL) - { - wmud_client_send(client, "Your registration is not finished yet.\r\n"); - wmud_client_close(client, TRUE); - } - else - { - client->state = WMUD_CLIENT_STATE_PASSWAIT; - client->player = player; - wmud_client_send(client, "Please provide us your password: %c%c%c", TELNET_IAC, TELNET_WONT, TELNET_ECHO); - } - } - else - { - client->player = g_new0(wmudPlayer, 1); - client->player->player_name = g_strdup(client->buffer->str); - client->state = WMUD_CLIENT_STATE_NEWCHAR; - wmud_client_send(client, "Is %s new to this game? [Y/N] ", client->buffer->str); - } -} - -/** - * wmud_client_interpret_newplayer_answer: - * @client: the client from which the answer came from - * - * Interprets a yes/no answer from the client to the question if they are new - * to the game. - */ -void -wmud_client_interpret_newplayer_answer(wmudClient *client) -{ - if (g_ascii_strcasecmp(client->buffer->str, "n") == 0) - { - wmud_client_send(client, "What is your player-name, then? "); - client->state = WMUD_CLIENT_STATE_FRESH; - } - else if (g_ascii_strcasecmp(client->buffer->str, "y") == 0) - { - g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Creating new player\n"); - wmud_client_send(client, "Welcome to this MUD!\r\nPlease enter your e-mail address: "); - client->state = WMUD_CLIENT_STATE_REGISTERING; - } - else - { - wmud_client_send(client, "Sorry, but for this question I only understand 'Y' or 'N'.\r\nIs %s a new player here? [Y/N] ", client->player->player_name); - } -} - -/** - * wmud_client_interpret_newplayer_email: - * @client: the client from which the e-mail address arrived from - * - * Checks for the validity of the new player's e-mail address - */ -void -wmud_client_interpret_newplayer_email(wmudClient *client) -{ - /* TODO: Error checking */ - GRegex *email_regex = g_regex_new("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$", G_REGEX_CASELESS, 0, NULL); - - if (!*(client->buffer->str)) - { - if (client->bademail) - { - wmud_client_close(client, TRUE); - } - } - - if (g_regex_match(email_regex, client->buffer->str, 0, NULL)) - { - client->player->email = g_strdup(client->buffer->str); - client->state = WMUD_CLIENT_STATE_REGEMAIL_CONFIRM; - wmud_client_send(client, "It seems to be a valid address to me, but could you write it again? "); - } - else - { - wmud_client_send(client, "\r\nSorry, but this e-mail address doesn't seem to be valid to me.\r\n\r\nIf you think this is a valid address, simply press enter to quit, and send an e-mail to %s from that address, so we can fix our e-mail validation code.\r\n\r\nIf you just mistyped your address, type it now: ", admin_email); - if (*(client->buffer->str)) - client->bademail = TRUE; - } -} - -/** - * wmud_client_interpret_newplayer_mailconfirm: - * @client: the client from which the confirmation e-mail arrived - * - * Check if the confirmed e-mail address is the same as the previously entered - * one. - */ -void -wmud_client_interpret_newplayer_mailconfirm(wmudClient *client) -{ - GError *err = NULL; - - if (g_ascii_strcasecmp(client->player->email, client->buffer->str) == 0) - { - if (wmud_db_save_player(client->player, &err)) - { - wmud_client_send(client, "Good. We will generate the password for this player name, and send it to you\r\nvia e-mail. Please come back to us, if you get that code, so you can log\r\nin.\r\n"); - players = g_slist_prepend(players, wmud_player_dup(client->player)); - } - else - { - g_critical("wmud_db_save_player() error: %s", err->message); - wmud_client_send(client, "There was an error during the database update. Please try again later!\r\n"); - } - wmud_client_close(client, TRUE); - } - else - { - g_free(client->player->email); - client->player->email = NULL; - - wmud_client_send(client, "This is not the same as you entered before.\r\nLet's just try it again: "); - client->state = WMUD_CLIENT_STATE_REGISTERING; - } -} - diff --git a/src/game-networking.h b/src/game-networking.h index dfdda0b..4a63bd3 100644 --- a/src/game-networking.h +++ b/src/game-networking.h @@ -50,10 +50,7 @@ extern GSList *clients; -gboolean wmud_networking_init(guint port_number, GError **err); +gboolean wmud_networking_init(guint port_number, GMainContext *game_context, GError **err); void wmud_client_send(wmudClient *client, const gchar *fmt, ...); -void wmud_client_start_login(wmudClient *client); -void wmud_client_interpret_newplayer_answer(wmudClient *client); -void wmud_client_interpret_newplayer_email(wmudClient *client); #endif diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..f1908ae --- /dev/null +++ b/src/game.c @@ -0,0 +1,123 @@ +/* wMUD - Yet another MUD codebase by W00d5t0ck + * Copyright (C) 2012 - Gergely POLONKAI + * + * game.c: Game Thread related functions + * + * 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 "main.h" +#include "game.h" + +/** + * SECTION:game-thread + * @short_description: Game related functions + * @title: The Game Thread + * + * The game thread is supposed to serve game client connections. Also, + * maintaining the loaded game world is the objective ot this thread. + * + * This thread has to serve all the game clients after a connection is + * estabilished. Player login, menu interaction and play are both the tasks of + * this thread. + * + * The other main objective is to maintain the loaded world. Moving and + * resetting the mobs, cleaning up areas and many other things belong here. + */ + +/** + * elapsed_seconds: + * + * the number of seconds elapsed since game boot. May be inaccurate, as it + * simply gets updated by a timeout function which should run every second + */ +guint32 elapsed_seconds = 0; + +/** + * elapsed_cycle: + * + * yes, I'm optimistic. This counter is increased if, for some reason, + * #elapsed_seconds reaches the maximum value + */ +guint32 elapsed_cycle = 0; + +/** + * + * rl_sec_elapsed: + * @user_data: non-used pointer to callback's user data + * + * Keeps track of elapsed real-world time. It is inaccurate by design, but it + * doesn't actually matter. + */ +gboolean +rl_sec_elapsed(gpointer user_data) +{ + elapsed_seconds++; + if (elapsed_seconds == G_MAXUINT32) + { + elapsed_seconds = 0; + elapsed_cycle++; + } + + if (elapsed_seconds % 30 == 0) + { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, "Heartbeat"); + } + + return TRUE; +} + +/** + * game_thread_func: + * @game_loop: the main loop to be associated with the game thread + * + * The game thread's main function. + * + * Return value: This function always returns %NULL. + */ +gpointer +game_thread_func(GMainLoop *game_loop) +{ + /* Run the game loop */ + g_main_loop_run(game_loop); + + return NULL; +} + +gboolean +wmud_game_init(GThread **game_thread, GMainContext **game_context) +{ + GMainLoop *game_loop; + GSource *timeout_source; + GError *err = NULL; + + /* Create the game context and main loop */ + *game_context = g_main_context_new(); + game_loop = g_main_loop_new(*game_context, FALSE); + + /* Create the timeout source which keeps track of elapsed real-world + * time */ + timeout_source = g_timeout_source_new(1000); + g_source_set_callback(timeout_source, rl_sec_elapsed, NULL, NULL); + g_source_attach(timeout_source, *game_context); + g_source_unref(timeout_source); + + g_clear_error(&err); + *game_thread = g_thread_create((GThreadFunc)game_thread_func, game_loop, TRUE, &err); + + return TRUE; +} + diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..8ad8f92 --- /dev/null +++ b/src/game.h @@ -0,0 +1,25 @@ +/* wMUD - Yet another MUD codebase by W00d5t0ck + * Copyright (C) 2012 - Gergely POLONKAI + * + * game.h: Game Thread related functions + * + * 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 . + */ +#ifndef __WMUD_GAME_H__ +#define __WMUD_GAME_H__ + +gboolean wmud_game_init(GThread **game_thread, GMainContext **game_context); + +#endif /* __WMUD_GAME_H__ */ + diff --git a/src/main.c b/src/main.c index 9f40b78..9e97241 100644 --- a/src/main.c +++ b/src/main.c @@ -33,6 +33,8 @@ #include "db.h" #include "players.h" #include "maintenance.h" +#include "game.h" +#include "configuration.h" /** * SECTION:utils @@ -51,88 +53,6 @@ struct { int line; } debug_context_loc = {NULL, 0}; -/** - * game_context: - * - * the game thread's main context - */ -GMainContext *game_context; -/** - * elapsed_seconds: - * - * the number of seconds elapsed since game boot. May be inaccurate, as it - * simply gets updated by a timeout function which should run every second - */ -guint32 elapsed_seconds = 0; -/** - * elapsed_cycle: - * - * yes, I'm optimistic. This counter is increased if, for some reason, - * #elapsed_seconds reaches the maximum value - */ -guint32 elapsed_cycle = 0; -/** - * main_rand: - * - * the main random generator - */ -GRand *main_rand = NULL; -/** - * WMUD_CONFIG_ERROR: - * - * the GQuark for the config error GError - */ -GQuark WMUD_CONFIG_ERROR = 0; -/** - * WMUD_DB_ERROR: - * - * the GQuark for the database error GError - */ -GQuark WMUD_DB_ERROR = 0; -/** - * port: - * - * the port number to listen on - */ -guint port = 0; -/** - * database_file: - * - * the filename of the world database - */ -gchar *database_file = NULL; -/** - * admin_email: - * - * e-mail address of the MUD's administrator - */ -gchar *admin_email = NULL; - -/** - * rl_sec_elapsed: - * @user_data: non-used pointer to callback's user data - * - * Keeps track of elapsed real-world time. It is inaccurate by design, but it - * doesn't actually matter. - */ -gboolean -rl_sec_elapsed(gpointer user_data) -{ - elapsed_seconds++; - if (elapsed_seconds == G_MAXUINT32) - { - elapsed_seconds = 0; - elapsed_cycle++; - } - - if (elapsed_seconds % 30 == 0) - { - g_log(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, "Heartbeat"); - } - - return TRUE; -} - /** * wmud_random_string: * @len: the desired length of the generated random string @@ -203,111 +123,6 @@ wmud_type_init(void) WMUD_DB_ERROR = g_quark_from_string("wmud_db_error"); } -/** - * wmud_config_init: - * @err: The GError in which the config handling status should be returned - * - * Parses the default configuration file, and sets different variables - * according to it. - * - * Return value: %TRUE if parsing was successful. %FALSE otherwise. - */ -gboolean -wmud_config_init(GError **err) -{ - GString *config_file = g_string_new(WMUD_CONFDIR); - GKeyFile *config; - GError *in_err = NULL; - - g_string_append(config_file, "/wmud.conf"); - - config = g_key_file_new(); - /* TODO: Error checking */ - g_key_file_load_from_file(config, config_file->str, 0, &in_err); - - if (!g_key_file_has_group(config, "global")) - { - g_set_error(err, WMUD_CONFIG_ERROR, WMUD_CONFIG_ERROR_NOGLOBAL, "Config file (%s) does not contain a [global] group", config_file->str); - g_key_file_free(config); - g_string_free(config_file, TRUE); - - return FALSE; - } - - g_clear_error(&in_err); - port = g_key_file_get_integer(config, "global", "port", &in_err); - if (in_err) - { - if (g_error_matches(in_err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) - { - port = DEFAULT_PORT; - } - else if (g_error_matches(in_err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE)) - { - g_set_error(err, WMUD_CONFIG_ERROR, WMUD_CONFIG_ERROR_BADPORT, "Config file (%s) contains an invalid port number", config_file->str); - g_key_file_free(config); - g_string_free(config_file, TRUE); - port = 0; - - return FALSE; - } - - return FALSE; - } - - g_clear_error(&in_err); - database_file = g_key_file_get_string(config, "global", "world file", &in_err); - if (in_err) - { - if (g_error_matches(in_err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) - { - g_set_error(err, WMUD_CONFIG_ERROR, WMUD_CONFIG_ERROR_NOWORLD, "Config file (%s) does not contain a world file path", config_file->str); - g_key_file_free(config); - g_string_free(config_file, TRUE); - database_file = NULL; - - return FALSE; - } - } - - g_clear_error(&in_err); - admin_email = g_key_file_get_string(config, "global", "admin email", &in_err); - if (in_err) - { - if (g_error_matches(in_err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) - { - g_set_error(err, WMUD_CONFIG_ERROR, WMUD_CONFIG_ERROR_NOEMAIL, "Config file (%s) does not contain an admin e-mail address", config_file->str); - g_key_file_free(config); - g_string_free(config_file, TRUE); - admin_email = NULL; - - return FALSE; - } - } - - g_key_file_free(config); - g_string_free(config_file, TRUE); - - return TRUE; -} - -/** - * game_thread_func: - * @game_loop: the main loop to be associated with the game thread - * - * The game thread's main function. - * - * Return value: This function always returns %NULL. - */ -gpointer -game_thread_func(GMainLoop *game_loop) -{ - /* Run the game loop */ - g_main_loop_run(game_loop); - - return NULL; -} - /** * main: * @argc: The number of arguments on the command line @@ -318,10 +133,9 @@ game_thread_func(GMainLoop *game_loop) int main(int argc, char **argv) { - GMainLoop *game_loop; - GSource *timeout_source; GError *err = NULL; GThread *game_thread; + GMainContext *game_context; /* Initialize the thread and type system */ g_thread_init(NULL); @@ -329,24 +143,9 @@ main(int argc, char **argv) wmud_type_init(); /* TODO: Command line parsing */ - - /* Initialize random number generator */ - main_rand = g_rand_new(); - - /* Create the game context and main loop */ - game_context = g_main_context_new(); - game_loop = g_main_loop_new(game_context, FALSE); - - /* Create the timeout source which keeps track of elapsed real-world - * time */ - timeout_source = g_timeout_source_new(1000); - g_source_set_callback(timeout_source, rl_sec_elapsed, NULL, NULL); - g_source_attach(timeout_source, game_context); - g_source_unref(timeout_source); - /* TODO: Create signal handlers! */ - if (!wmud_config_init(&err)) + if (!wmud_config_init(&active_config, &err)) { if (err) { @@ -360,9 +159,6 @@ main(int argc, char **argv) return 1; } - g_assert(port != 0); - g_assert(database_file != NULL); - g_clear_error(&err); if (!wmud_db_init(&err)) { @@ -377,28 +173,28 @@ main(int argc, char **argv) return 1; } - g_clear_error(&err); - if (!wmud_networking_init(port, &err)) - { - if (err) - { - g_critical("Database initialization error: %s", err->message); - } - else - { - g_critical("Database initialization error!"); - } - - return 1; - } g_clear_error(&err); wmud_db_players_load(&err); /* Initialization ends here */ + wmud_game_init(&game_thread, &game_context); + g_clear_error(&err); - game_thread = g_thread_create((GThreadFunc)game_thread_func, game_loop, TRUE, &err); + if (!wmud_networking_init(active_config->port, game_context, &err)) + { + if (err) + { + g_critical("Database initialization error: %s", err->message); + } + else + { + g_critical("Database initialization error!"); + } + + return 1; + } wmud_maintenance_init(); diff --git a/src/main.h b/src/main.h index 2a25ed5..a9a7560 100644 --- a/src/main.h +++ b/src/main.h @@ -5,10 +5,6 @@ extern GMainContext *game_context; extern guint32 elapsed_seconds; -extern GRand *main_rand; -extern gchar *database_file; -extern GQuark WMUD_DB_ERROR; -extern gchar *admin_email; /** * random_number: @@ -17,7 +13,7 @@ extern gchar *admin_email; * * Generates a random number between min and max */ -#define random_number(min, max) g_rand_int_range(main_rand, (min), (max) + 1) +#define random_number(min, max) g_random_int_range((min), (max) + 1) gchar *wmud_random_string(gint len); diff --git a/src/maintenance.c b/src/maintenance.c index ba7940e..25bd038 100644 --- a/src/maintenance.c +++ b/src/maintenance.c @@ -30,6 +30,13 @@ #include "main.h" #include "players.h" +/** + * SECTION:maintenance-thread + * @short_description: Runtime maintenance functions + * @title: Runtime maintenance functions + * + */ + /** * wmud_maintenance_check_new_players: * @player: #wmudPLayer structure of the player record to check diff --git a/src/wmud-types.h b/src/wmud-types.h index 52e2232..ed6a93b 100644 --- a/src/wmud-types.h +++ b/src/wmud-types.h @@ -40,7 +40,7 @@ * @WMUD_CLIENT_STATE_INGAME: Character login was successful, player is now * in-game * @WMUD_CLIENT_STATE_QUITWAIT: Player entered the in-game QUIT command, and we - * are now waiting for an answer if they reallz want to quit + * are now waiting for an answer if they really want to quit * Will be removed soon, this should work totally different (TODO) * @WMUD_CLIENT_STATE_NEWCHAR: Player name entered on the login screen was * invalid. Waiting for answer if this is a new player @@ -66,7 +66,7 @@ typedef enum { * wmudPlayer: * @id: Player's database ID * @player_name: Player's login name - * @cpassword: crzpt()ed password of the player. This is NULL for newly + * @cpassword: crypt()ed password of the player. This is NULL for newly * registered players, who have no password generated for them by the * maintenance loop * @email: E-mail address of the player @@ -109,26 +109,6 @@ typedef struct _wmudClient { gint login_try_count; } wmudClient; -/** - * wmudConfigError: - * @WMUD_CONFIG_ERROR_NOGLOBAL: Indicates that the config file read doesn't - * contain a [global] section - * @WMUD_CONFIG_ERROR_BADPORT: Indicates that the config file contains and - * invalid port number - * @WMUD_CONFIG_ERROR_NOWORLD: Indicates that the config file doesn't contain a - * world database file - * @WMUD_CONFIG_ERROR_NOEMAIL: Indicates that the config file doesn't contain - * an administrator e-mail address - * - * Error codes returned by configuration file parsing functions. - */ -typedef enum { - WMUD_CONFIG_ERROR_NOGLOBAL, - WMUD_CONFIG_ERROR_BADPORT, - WMUD_CONFIG_ERROR_NOWORLD, - WMUD_CONFIG_ERROR_NOEMAIL -} wmudConfigError; - /** * wmudDbError: * @WMUD_DB_ERROR_CANTOPEN: Database file cannot be opened diff --git a/src/world.c b/src/world.c new file mode 100644 index 0000000..5e71402 --- /dev/null +++ b/src/world.c @@ -0,0 +1,155 @@ +/* wMUD - Yet another MUD codebase by W00d5t0ck + * Copyright (C) 2012 - Gergely POLONKAI + * + * world.c: world loading and building functions + * + * 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 "world.h" +#include "db.h" + +/** + * SECTION:world + * @short_description: World loading and building functions + * @title: Game world manipulation + * + */ + +gboolean +wmud_world_check_planes(GSList *planes, GError **err) +{ + return FALSE; +} + +gboolean +wmud_world_check_planets(GSList *planets, GError **err) +{ + return FALSE; +} + +gboolean +wmud_world_check_areas(GSList *areas, GError **err) +{ + return FALSE; +} + +gboolean +wmud_world_check_exits(GSList *exits, GError **err) +{ + return FALSE; +} + +gboolean +wmud_world_assoc_planets_planes(GSList *planets, GSList *planes, GError **err) +{ + return FALSE; +} + +gboolean +wmud_world_assoc_rooms_areas(GSList *rooms, GSList *areas, GError **err) +{ + return FALSE; +} + +gboolean +wmud_world_assoc_rooms_planets(GSList *rooms, GSList *planets, GError **err) +{ + return FALSE; +} + +gboolean +wmud_world_assoc_exits_rooms(GSList *exits, GSList *rooms, GError **err) +{ + return FALSE; +} + + +/** + * wmud_world_load: + * @err: a #GError to put error messages into + * + * Load the world from the database backend without activating it. + */ +gboolean +wmud_world_load(GError **err) +{ + GSList *planes = NULL, + *planets = NULL, + *directions = NULL, + *areas = NULL, + *rooms = NULL, + *exits = NULL; + GError *in_err = NULL; + + /* Load directions from the database */ + wmud_db_load_directions(&directions, &in_err); + + /* Load planes from the database */ + wmud_db_load_planes(&planes, &in_err); + + /* Check if the loaded planes conform to the rules */ + g_clear_error(&in_err); + wmud_world_check_planes(planes, &in_err); + + /* Load planets from the database */ + g_clear_error(&in_err); + wmud_db_load_planets(&planets, &in_err); + + /* Check if the planets conform to the rules */ + g_clear_error(&in_err); + wmud_world_check_planets(planets, &in_err); + + /* Put the planets on the planes */ + g_clear_error(&in_err); + wmud_world_assoc_planets_planes(planets, planes, &in_err); + + /* Load areas from the database */ + g_clear_error(&in_err); + wmud_db_load_areas(&areas, &in_err); + + /* Check if the areas conform to the rules */ + g_clear_error(&in_err); + wmud_world_check_areas(areas, &in_err); + + /* Load rooms from the database */ + g_clear_error(&in_err); + wmud_db_load_rooms(&rooms, &in_err); + + /* Associate rooms with the areas */ + g_clear_error(&in_err); + wmud_world_assoc_rooms_areas(rooms, areas, &in_err); + + /* Associate rooms with planets */ + g_clear_error(&in_err); + wmud_world_assoc_rooms_planets(rooms, planets, &in_err); + + /* Load room exits from the database */ + g_clear_error(&in_err); + wmud_db_load_exits(&exits, &in_err); + + /* Check if the exits conform to the rules */ + g_clear_error(&in_err); + wmud_world_check_exits(exits, &in_err); + + /* Associate exits with rooms */ + g_clear_error(&in_err); + wmud_world_assoc_exits_rooms(exits, rooms, &in_err); + + g_clear_error(&in_err); + return TRUE; +} + diff --git a/src/world.h b/src/world.h new file mode 100644 index 0000000..4063902 --- /dev/null +++ b/src/world.h @@ -0,0 +1,33 @@ +/* wMUD - Yet another MUD codebase by W00d5t0ck + * Copyright (C) 2012 - Gergely POLONKAI + * + * world.h: world loading and building functions + * + * 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 . + */ +#ifndef __WMUD_WORLD_H__ +#define __WMUD_WORLD_H__ + +gboolean wmud_world_check_planes(GSList *planes, GError **err); +gboolean wmud_world_check_planets(GSList *planets, GError **err); +gboolean wmud_world_check_areas(GSList *areas, GError **err); +gboolean wmud_world_check_exits(GSList *exits, GError **err); + +gboolean wmud_world_assoc_planets_planes(GSList *planets, GSList *planes, GError **err); +gboolean wmud_world_assoc_rooms_areas(GSList *rooms, GSList *areas, GError **err); +gboolean wmud_world_assoc_rooms_planets(GSList *rooms, GSList *planets, GError **err); +gboolean wmud_world_assoc_exits_rooms(GSList *exits, GSList *rooms, GError **err); + +#endif /* __WMUD_WORLD_H__ */ +