diff options
Diffstat (limited to 'src/char')
-rw-r--r-- | src/char/char.c | 1083 | ||||
-rw-r--r-- | src/char/char.h | 34 | ||||
-rw-r--r-- | src/char/int_elemental.c | 4 | ||||
-rw-r--r-- | src/char/int_guild.c | 28 | ||||
-rw-r--r-- | src/char/int_homun.c | 4 | ||||
-rw-r--r-- | src/char/int_mercenary.c | 4 | ||||
-rw-r--r-- | src/char/int_party.c | 6 | ||||
-rw-r--r-- | src/char/int_pet.c | 6 | ||||
-rw-r--r-- | src/char/inter.c | 117 | ||||
-rw-r--r-- | src/char/inter.h | 9 | ||||
-rw-r--r-- | src/char/pincode.c | 68 | ||||
-rw-r--r-- | src/char/pincode.h | 6 |
12 files changed, 932 insertions, 437 deletions
diff --git a/src/char/char.c b/src/char/char.c index 2851d3eba..6f79a55e3 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -42,6 +42,7 @@ #include "common/HPM.h" #include "common/cbasetypes.h" +#include "common/conf.h" #include "common/console.h" #include "common/core.h" #include "common/db.h" @@ -61,6 +62,7 @@ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> +#include <sys/stat.h> // stat() #if MAX_MAP_SERVERS > 1 # ifdef _MSC_VER @@ -110,9 +112,6 @@ char char_reg_num_db[32] = "char_reg_num_db"; struct char_interface char_s; struct char_interface *chr; -// show loading/saving messages -int save_log = 1; - char db_path[1024] = "db"; char wisp_server_name[NAME_LENGTH] = "Server"; @@ -123,7 +122,7 @@ char char_ip_str[128]; char bind_ip_str[128]; uint32 bind_ip = INADDR_ANY; int char_maintenance_min_group_id = 0; -bool char_new = true; +bool enable_char_creation = true; ///< Whether to allow character creation. bool name_ignoring_case = false; // Allow or not identical name for characters but with a different case by [Yor] int char_name_option = 0; // Option to know which letters/symbols are authorized in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] @@ -131,19 +130,24 @@ char unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the request #define TRIM_CHARS "\255\xA0\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex] char char_name_letters[1024] = ""; // list of letters/symbols allowed (or not) in a character name. by [Yor] -int char_del_level = 0; //From which level u can delete character [Lupus] +int char_del_level = 0; ///< From which level you can delete character [Lupus] int char_del_delay = 86400; - -int log_char = 1; // logging char or not [devil] -int log_inter = 1; // logging inter or not [devil] - -int char_aegis_delete = 0; // Verify if char is in guild/party or char and reacts as Aegis does (doesn't allow deletion), see chr->delete2_req for more information +bool char_aegis_delete = false; ///< Verify if char is in guild/party or char and reacts as Aegis does (disallow deletion), @see chr->delete2_req. int max_connect_user = -1; int gm_allow_group = -1; int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; int start_zeny = 0; -int start_items[MAX_START_ITEMS*3]; + +/// Start items for new characters +struct start_item_s { + int id; + int amount; + int loc; + bool stackable; +}; +VECTOR_DECL(struct start_item_s) start_items; + int guild_exp_rate = 100; //Custom limits for the fame lists. [Skotlex] @@ -690,7 +694,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) #endif StrBuf->Destroy(&buf); - if (save_status[0]!='\0' && save_log) + if (chr->show_save_log && save_status[0] != '\0') ShowInfo("Saved char %d - %s:%s.\n", char_id, p->name, save_status); if (!errors) memcpy(cp, p, sizeof(struct mmo_charstatus)); @@ -1043,7 +1047,8 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every memset(p, 0, sizeof(struct mmo_charstatus)); - if (save_log) ShowInfo("Char load request (%d)\n", char_id); + if (chr->show_save_log) + ShowInfo("Char load request (%d)\n", char_id); stmt = SQL->StmtMalloc(inter->sql_handle); if( stmt == NULL ) @@ -1336,7 +1341,8 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every if( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) strcat(t_msg, " accdata"); - if (save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfully! + if (chr->show_save_log) + ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfully! SQL->StmtFree(stmt); StrBuf->Destroy(&buf); @@ -1463,12 +1469,11 @@ int char_rename_char_sql(struct char_session_data *sd, int char_id) memset(sd->new_name,0,sizeof(sd->new_name)); // log change - if( log_char ) - { - if( SQL_ERROR == SQL->Query(inter->sql_handle, - "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '0', '0', '0', '0', '0', '0', '0', '0')", - charlog_db, "change char name", sd->account_id, char_dat.char_id, char_dat.slot, esc_name) ) + if (chr->enable_logs) { + if (SQL_ERROR == SQL->Query(inter->sql_handle, + "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" + "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '0', '0', '0', '0', '0', '0', '0', '0')", + charlog_db, "change char name", sd->account_id, char_dat.char_id, char_dat.slot, esc_name)) Sql_ShowDebug(inter->sql_handle); } @@ -1543,7 +1548,7 @@ int char_make_new_char_sql(struct char_session_data *sd, const char *name_, int { char name[NAME_LENGTH]; char esc_name[NAME_LENGTH*2+1]; - int char_id, flag, k, l; + int char_id, flag, i; nullpo_retr(-2, sd); nullpo_retr(-2, name_); @@ -1606,32 +1611,29 @@ int char_make_new_char_sql(struct char_session_data *sd, const char *name_, int return -2; // Validation success, log result - if (log_char) { - if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db, "make new char", sd->account_id, char_id, slot, esc_name, str, agi, vit, int_, dex, luk, hair_style, hair_color) ) + if (chr->enable_logs) { + if (SQL_ERROR == SQL->Query(inter->sql_handle, + "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" + "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + charlog_db, "make new char", sd->account_id, char_id, slot, esc_name, str, agi, vit, int_, dex, luk, hair_style, hair_color)) Sql_ShowDebug(inter->sql_handle); } //Give the char the default items - for (k = 0; k < ARRAYLENGTH(start_items) && start_items[k] != 0; k += 3) { - // FIXME: How to define if an item is stackable without having to lookup itemdb? [panikon] - if( start_items[k+2] == 1 ) - { - if( SQL_ERROR == SQL->Query(inter->sql_handle, - "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')", - inventory_db, char_id, start_items[k], start_items[k + 1], 1) ) - Sql_ShowDebug(inter->sql_handle); - } - else if( start_items[k+2] == 0 ) - { + for (i = 0; i < VECTOR_LENGTH(start_items); i++) { + struct start_item_s *item = &VECTOR_INDEX(start_items, i); + if (item->stackable) { + if (SQL_ERROR == SQL->Query(inter->sql_handle, + "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')", + inventory_db, char_id, item->id, item->amount, 1)) + Sql_ShowDebug(inter->sql_handle); + } else { // Non-stackable items should have their own entries (issue: 7279) - for( l = 0; l < start_items[k+1]; l++ ) - { - if( SQL_ERROR == SQL->Query(inter->sql_handle, - "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')", - inventory_db, char_id, start_items[k], 1, 1) - ) + int l, loc = item->loc; + for (l = 0; l < item->amount; l++) { + if (SQL_ERROR == SQL->Query(inter->sql_handle, + "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')", + inventory_db, char_id, item->id, 1, loc, 1)) Sql_ShowDebug(inter->sql_handle); } } @@ -1804,13 +1806,13 @@ int char_delete_char_sql(int char_id) #endif /* delete character */ - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_db, char_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_db, char_id)) { Sql_ShowDebug(inter->sql_handle); - else if( log_char ) { - if( SQL_ERROR == SQL->Query(inter->sql_handle, - "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `char_msg`, `name`)" - " VALUES (NOW(), '%d', '%d', '%d', 'Deleted character', '%s')", - charlog_db, account_id, char_id, 0, esc_name) ) + } else if (chr->enable_logs) { + if (SQL_ERROR == SQL->Query(inter->sql_handle, + "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `char_msg`, `name`)" + " VALUES (NOW(), '%d', '%d', '%d', 'Deleted character', '%s')", + charlog_db, account_id, char_id, 0, esc_name)) Sql_ShowDebug(inter->sql_handle); } @@ -2015,7 +2017,7 @@ int char_mmo_char_send_characters(int fd, struct char_session_data* sd) #if PACKETVER >= 20100413 offset += 3; #endif - if (save_log) + if (chr->show_save_log) ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id); j = 24 + offset; // offset @@ -2181,7 +2183,7 @@ int char_parse_fromlogin_connection_state(int fd) ShowError("Can not connect to login-server.\n"); ShowError("The server communication passwords (default s1/p1) are probably invalid.\n"); ShowError("Also, please make sure your login db has the correct communication username/passwords and the gender of the account is S.\n"); - ShowError("The communication passwords are set in /conf/map-server.conf and /conf/char-server.conf\n"); + ShowError("The communication passwords are set in /conf/map-server.conf and /conf/char/char-server.conf\n"); sockt->eof(fd); return 1; } else { @@ -4516,13 +4518,13 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) if( cd->sex == 99 ) cd->sex = sd->sex; - if (log_char) { + if (chr->enable_logs) { char esc_name[NAME_LENGTH*2+1]; // FIXME: Why are we re-escaping the name if it was already escaped in rename/make_new_char? [Panikon] SQL->EscapeStringLen(inter->sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH)); - if( SQL_ERROR == SQL->Query(inter->sql_handle, - "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `name`) VALUES (NOW(), '%d', '%d', '%d', '%s')", - charlog_db, sd->account_id, cd->char_id, slot, esc_name) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, + "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `name`) VALUES (NOW(), '%d', '%d', '%d', '%s')", + charlog_db, sd->account_id, cd->char_id, slot, esc_name)) Sql_ShowDebug(inter->sql_handle); } ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name); @@ -4615,7 +4617,7 @@ void char_parse_char_create_new_char(int fd, struct char_session_data* sd) __att void char_parse_char_create_new_char(int fd, struct char_session_data* sd) { int result; - if( !char_new ) { + if (!enable_char_creation) { //turn character creation on/off [Kevin] result = -2; } else { @@ -5348,304 +5350,673 @@ static int char_online_data_cleanup(int tid, int64 tick, int id, intptr_t data) return 0; } -void char_sql_config_read(const char* cfgName) +/** + * Reads the 'inter_configuration' config file and initializes required variables. + * + * @param filename Path to configuration file + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_sql_config_read(const char *filename, bool imported) { - char line[1024], w1[1024], w2[1024]; - FILE* fp; + struct config_t config; + const struct config_setting_t *setting = NULL; + const char *import = NULL; + bool retval = true; - if ((fp = fopen(cfgName, "r")) == NULL) { - ShowError("File not found: %s\n", cfgName); - return; + nullpo_retr(false, filename); + + if (!libconfig->load_file(&config, filename)) + return false; // Error message is already shown by libconfig->load_file + + if ((setting = libconfig->lookup(&config, "inter_configuration/database_names")) == NULL) { + libconfig->destroy(&config); + if (imported) + return true; + ShowError("sql_config_read: inter_configuration/database_names was not found in %s!\n", filename); + return false; + } + libconfig->setting_lookup_mutable_string(setting, "char_db", char_db, sizeof(char_db)); + libconfig->setting_lookup_mutable_string(setting, "interlog_db", interlog_db, sizeof(interlog_db)); + libconfig->setting_lookup_mutable_string(setting, "ragsrvinfo_db", ragsrvinfo_db, sizeof(ragsrvinfo_db)); + + if (!chr->sql_config_read_registry(filename, &config, imported)) + retval = false; + if (!chr->sql_config_read_pc(filename, &config, imported)) + retval = false; + if (!chr->sql_config_read_guild(filename, &config, imported)) + retval = false; + + ShowInfo("Done reading %s.\n", filename); + // import should overwrite any previous configuration, so it should be called last + if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) { + if (strcmp(import, filename) == 0 || strcmp(import, chr->SQL_CONF_NAME) == 0) { + ShowWarning("sql_config_read: Loop detected in %s! Skipping 'import'...\n", filename); + } else { + if (!chr->sql_config_read(import, true)) + retval = false; + } } - while(fgets(line, sizeof(line), fp)) - { - if(line[0] == '/' && line[1] == '/') - continue; + // TODO HPM->parseConf(w1, w2, HPCT_CHAR_INTER); - if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2) - continue; + libconfig->destroy(&config); + return retval; +} - if(!strcmpi(w1,"char_db")) - safestrncpy(char_db, w2, sizeof(char_db)); - else if(!strcmpi(w1,"scdata_db")) - safestrncpy(scdata_db, w2, sizeof(scdata_db)); - else if(!strcmpi(w1,"cart_db")) - safestrncpy(cart_db, w2, sizeof(cart_db)); - else if(!strcmpi(w1,"inventory_db")) - safestrncpy(inventory_db, w2, sizeof(inventory_db)); - else if(!strcmpi(w1,"charlog_db")) - safestrncpy(charlog_db, w2, sizeof(charlog_db)); - else if(!strcmpi(w1,"storage_db")) - safestrncpy(storage_db, w2, sizeof(storage_db)); - else if(!strcmpi(w1,"skill_db")) - safestrncpy(skill_db, w2, sizeof(skill_db)); - else if(!strcmpi(w1,"interlog_db")) - safestrncpy(interlog_db, w2, sizeof(interlog_db)); - else if(!strcmpi(w1,"memo_db")) - safestrncpy(memo_db, w2, sizeof(memo_db)); - else if(!strcmpi(w1,"guild_db")) - safestrncpy(guild_db, w2, sizeof(guild_db)); - else if(!strcmpi(w1,"guild_alliance_db")) - safestrncpy(guild_alliance_db, w2, sizeof(guild_alliance_db)); - else if(!strcmpi(w1,"guild_castle_db")) - safestrncpy(guild_castle_db, w2, sizeof(guild_castle_db)); - else if(!strcmpi(w1,"guild_expulsion_db")) - safestrncpy(guild_expulsion_db, w2, sizeof(guild_expulsion_db)); - else if(!strcmpi(w1,"guild_member_db")) - safestrncpy(guild_member_db, w2, sizeof(guild_member_db)); - else if(!strcmpi(w1,"guild_skill_db")) - safestrncpy(guild_skill_db, w2, sizeof(guild_skill_db)); - else if(!strcmpi(w1,"guild_position_db")) - safestrncpy(guild_position_db, w2, sizeof(guild_position_db)); - else if(!strcmpi(w1,"guild_storage_db")) - safestrncpy(guild_storage_db, w2, sizeof(guild_storage_db)); - else if(!strcmpi(w1,"party_db")) - safestrncpy(party_db, w2, sizeof(party_db)); - else if(!strcmpi(w1,"pet_db")) - safestrncpy(pet_db, w2, sizeof(pet_db)); - else if(!strcmpi(w1,"mail_db")) - safestrncpy(mail_db, w2, sizeof(mail_db)); - else if(!strcmpi(w1,"auction_db")) - safestrncpy(auction_db, w2, sizeof(auction_db)); - else if(!strcmpi(w1,"friend_db")) - safestrncpy(friend_db, w2, sizeof(friend_db)); - else if(!strcmpi(w1,"hotkey_db")) - safestrncpy(hotkey_db, w2, sizeof(hotkey_db)); - else if(!strcmpi(w1,"quest_db")) - safestrncpy(quest_db,w2,sizeof(quest_db)); - else if(!strcmpi(w1,"homunculus_db")) - safestrncpy(homunculus_db,w2,sizeof(homunculus_db)); - else if(!strcmpi(w1,"skill_homunculus_db")) - safestrncpy(skill_homunculus_db,w2,sizeof(skill_homunculus_db)); - else if(!strcmpi(w1,"mercenary_db")) - safestrncpy(mercenary_db,w2,sizeof(mercenary_db)); - else if(!strcmpi(w1,"mercenary_owner_db")) - safestrncpy(mercenary_owner_db,w2,sizeof(mercenary_owner_db)); - else if(!strcmpi(w1,"ragsrvinfo_db")) - safestrncpy(ragsrvinfo_db,w2,sizeof(ragsrvinfo_db)); - else if(!strcmpi(w1,"elemental_db")) - safestrncpy(elemental_db,w2,sizeof(elemental_db)); - else if(!strcmpi(w1,"account_data_db")) - safestrncpy(account_data_db,w2,sizeof(account_data_db)); - else if(!strcmpi(w1,"char_reg_num_db")) - safestrncpy(char_reg_num_db, w2, sizeof(char_reg_num_db)); - else if(!strcmpi(w1,"char_reg_str_db")) - safestrncpy(char_reg_str_db, w2, sizeof(char_reg_str_db)); - else if(!strcmpi(w1,"acc_reg_str_db")) - safestrncpy(acc_reg_str_db, w2, sizeof(acc_reg_str_db)); - else if(!strcmpi(w1,"acc_reg_num_db")) - safestrncpy(acc_reg_num_db, w2, sizeof(acc_reg_num_db)); - //support the import command, just like any other config - else if(!strcmpi(w1,"import")) - chr->sql_config_read(w2); - else - HPM->parseConf(w1, w2, HPCT_CHAR_INTER); +/** + * Reads the 'inter_configuration/database_names/registry' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_sql_config_read_registry(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "inter_configuration/database_names/registry")) == NULL) { + if (imported) + return true; + ShowError("sql_config_read: inter_configuration/database_names/registry was not found in %s!\n", filename); + return false; } - fclose(fp); - ShowInfo("Done reading %s.\n", cfgName); + // Not all registries are read by char-server + libconfig->setting_lookup_mutable_string(setting, "char_reg_num_db", char_reg_num_db, sizeof(char_reg_num_db)); + libconfig->setting_lookup_mutable_string(setting, "char_reg_str_db", char_reg_str_db, sizeof(char_reg_str_db)); + libconfig->setting_lookup_mutable_string(setting, "acc_reg_str_db", acc_reg_str_db, sizeof(acc_reg_str_db)); + libconfig->setting_lookup_mutable_string(setting, "acc_reg_num_db", acc_reg_num_db, sizeof(acc_reg_num_db)); + + return true; } -void char_config_dispatch(char *w1, char *w2) { - bool (*dispatch_to[]) (char *w1, char *w2) = { - /* as many as it needs */ - pincode->config_read - }; - int i, len = ARRAYLENGTH(dispatch_to); - for(i = 0; i < len; i++) { - if( (*dispatch_to[i])(w1,w2) ) - break;/* we found who this belongs to, can stop */ +/** + * Reads the 'inter_configuration/database_names/pc' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_sql_config_read_pc(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "inter_configuration/database_names/pc")) == NULL) { + if (imported) + return true; + ShowError("sql_config_read: inter_configuration/database_names/pc was not found in %s!\n", filename); + return false; } - if (i == len) - HPM->parseConf(w1, w2, HPCT_CHAR); + libconfig->setting_lookup_mutable_string(setting, "hotkey_db", hotkey_db, sizeof(hotkey_db)); + libconfig->setting_lookup_mutable_string(setting, "scdata_db", scdata_db, sizeof(scdata_db)); + libconfig->setting_lookup_mutable_string(setting, "inventory_db", inventory_db, sizeof(inventory_db)); + libconfig->setting_lookup_mutable_string(setting, "cart_db", cart_db, sizeof(cart_db)); + libconfig->setting_lookup_mutable_string(setting, "charlog_db", charlog_db, sizeof(charlog_db)); + libconfig->setting_lookup_mutable_string(setting, "storage_db", storage_db, sizeof(storage_db)); + libconfig->setting_lookup_mutable_string(setting, "skill_db", skill_db, sizeof(skill_db)); + libconfig->setting_lookup_mutable_string(setting, "memo_db", memo_db, sizeof(memo_db)); + libconfig->setting_lookup_mutable_string(setting, "party_db", party_db, sizeof(party_db)); + libconfig->setting_lookup_mutable_string(setting, "pet_db", pet_db, sizeof(pet_db)); + libconfig->setting_lookup_mutable_string(setting, "friend_db", friend_db, sizeof(friend_db)); + libconfig->setting_lookup_mutable_string(setting, "mail_db", mail_db, sizeof(mail_db)); + libconfig->setting_lookup_mutable_string(setting, "auction_db", auction_db, sizeof(auction_db)); + libconfig->setting_lookup_mutable_string(setting, "quest_db", quest_db, sizeof(quest_db)); + libconfig->setting_lookup_mutable_string(setting, "homunculus_db", homunculus_db, sizeof(homunculus_db)); + libconfig->setting_lookup_mutable_string(setting, "skill_homunculus_db", skill_homunculus_db, sizeof(skill_homunculus_db)); + libconfig->setting_lookup_mutable_string(setting, "mercenary_db", mercenary_db, sizeof(mercenary_db)); + libconfig->setting_lookup_mutable_string(setting, "mercenary_owner_db", mercenary_owner_db, sizeof(mercenary_owner_db)); + libconfig->setting_lookup_mutable_string(setting, "elemental_db", elemental_db, sizeof(elemental_db)); + libconfig->setting_lookup_mutable_string(setting, "account_data_db", account_data_db, sizeof(account_data_db)); + + return true; } -int char_config_read(const char* cfgName) +/** + * Reads the 'inter_configuration/database_names/guild' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_sql_config_read_guild(const char *filename, const struct config_t *config, bool imported) { - char line[1024], w1[1024], w2[1024]; - FILE* fp = fopen(cfgName, "r"); + const struct config_setting_t *setting = NULL; - if (fp == NULL) { - ShowError("Configuration file not found: %s.\n", cfgName); - return 1; + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "inter_configuration/database_names/guild")) == NULL) { + if (imported) + return true; + ShowError("sql_config_read: inter_configuration/database_names/guild was not found in %s!\n", filename); + return false; + } + + libconfig->setting_lookup_mutable_string(setting, "main_db", guild_db, sizeof(guild_db)); + libconfig->setting_lookup_mutable_string(setting, "alliance_db", guild_alliance_db, sizeof(guild_alliance_db)); + libconfig->setting_lookup_mutable_string(setting, "castle_db", guild_castle_db, sizeof(guild_castle_db)); + libconfig->setting_lookup_mutable_string(setting, "expulsion_db", guild_expulsion_db, sizeof(guild_expulsion_db)); + libconfig->setting_lookup_mutable_string(setting, "member_db", guild_member_db, sizeof(guild_member_db)); + libconfig->setting_lookup_mutable_string(setting, "skill_db", guild_skill_db, sizeof(guild_skill_db)); + libconfig->setting_lookup_mutable_string(setting, "position_db", guild_position_db, sizeof(guild_position_db)); + libconfig->setting_lookup_mutable_string(setting, "storage_db", guild_storage_db, sizeof(guild_storage_db)); + + return true; +} + +/** + * Reads the 'char_configuration' config file and initializes required variables. + * + * @param filename Path to configuration file. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read(const char *filename, bool imported) +{ + struct config_t config; + const char *import = NULL; + bool retval = true; + + nullpo_retr(false, filename); + + if (!libconfig->load_file(&config, filename)) + return false; // Error message is already shown by libconfig->load_file + + if (!chr->config_read_top(filename, &config, imported)) + retval = false; + if (!chr->config_read_inter(filename, &config, imported)) + retval = false; + if (!chr->config_read_permission(filename, &config, imported)) + retval = false; + if (!chr->config_read_player(filename, &config, imported)) + retval = false; + if (!chr->config_read_console(filename, &config, imported)) + retval = false; + if (!chr->config_read_database(filename, &config, imported)) + retval = false; + if (!inter->config_read_connection(filename, &config, imported)) + retval = false; + if (!pincode->config_read(filename, &config, imported)) + retval = false; + + // TODO HPM->parseConf(w1, w2, HPCT_CHAR); + + ShowInfo("Done reading %s.\n", filename); + + // import should overwrite any previous configuration, so it should be called last + if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) { + if (strcmp(import, filename) == 0 || strcmp(import, chr->CHAR_CONF_NAME) == 0) { + ShowWarning("char_config_read: Loop detected in %s! Skipping 'import'...\n", filename); + } else { + if (!chr->config_read(import, true)) + retval = false; + } } - while(fgets(line, sizeof(line), fp)) { - if (line[0] == '/' && line[1] == '/') + libconfig->destroy(&config); + return retval; +} + +/** + * Reads the 'char_configuration' top level config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_top(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration was not found in %s!\n", filename); + return false; + } + + // char_configuration/server_name + if (libconfig->setting_lookup_mutable_string(setting, "server_name", chr->server_name, sizeof(chr->server_name)) == CONFIG_TRUE) { + ShowInfo("server name %s\n", chr->server_name); + } else if (!imported) { + ShowWarning("char_config_read: server_name was not set! Defaulting to 'Hercules'.\n"); + safestrncpy(chr->server_name, "Hercules", sizeof(chr->server_name)); + } + // char_configuration/wisp_server_name + if (libconfig->setting_lookup_mutable_string(setting, "wisp_server_name", wisp_server_name, sizeof(wisp_server_name)) == CONFIG_TRUE) { + // wisp_server_name should _always_ be equal or bigger than 4 characters! + if (strlen(wisp_server_name) < 4) { // TODO: This length should be a #define (i.e. MIN_NAME_LENGTH) + ShowWarning("char_config_read: char_configuration/wisp_server_name is too small! Defaulting to: Server.\n"); + safestrncpy(chr->server_name, "Server", sizeof(chr->server_name)); + } + } + // char_configuration/guild_exp_rate + libconfig->setting_lookup_int(setting, "guild_exp_rate", &guild_exp_rate); + + return true; +} + +/** + * Reads the 'char_configuration/inter' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_inter(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + const char *str = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/inter")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/inter was not found in %s!\n", filename); + return false; + } + + // Login information + libconfig->setting_lookup_mutable_string(setting, "userid", chr->userid, sizeof(chr->userid)); + libconfig->setting_lookup_mutable_string(setting, "passwd", chr->passwd, sizeof(chr->passwd)); + + // Login-server and character-server information + if (libconfig->setting_lookup_string(setting, "login_ip", &str) == CONFIG_TRUE) + chr->config_set_ip("Login server", str, &login_ip, login_ip_str); + + if (libconfig->setting_lookup_string(setting, "char_ip", &str) == CONFIG_TRUE) + chr->config_set_ip("Character server", str, &chr->ip, char_ip_str); + + if (libconfig->setting_lookup_string(setting, "bind_ip", &str) == CONFIG_TRUE) + chr->config_set_ip("Character server binding", str, &bind_ip, bind_ip_str); + + libconfig->setting_lookup_uint16(setting, "login_port", &login_port); + libconfig->setting_lookup_uint16(setting, "char_port", &chr->port); + + return true; +} + +/** + * Reads the 'char_configuration/database' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_database(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/database")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/database was not found in %s!\n", filename); + return false; + } + if (libconfig->setting_lookup_int(setting, "autosave_time", &autosave_interval) == CONFIG_TRUE) { + autosave_interval *= 1000; + if (autosave_interval <= 0) + autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; + } + libconfig->setting_lookup_mutable_string(setting, "db_path", db_path, sizeof(db_path)); + libconfig->setting_lookup_bool_real(setting, "log_char", &chr->enable_logs); + return true; +} + +/** + * Reads the 'char_configuration/console' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_console(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/console")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/console was not found in %s!\n", filename); + return false; + } + libconfig->setting_lookup_bool_real(setting, "stdout_with_ansisequence", &showmsg->stdout_with_ansisequence); + libconfig->setting_lookup_bool_real(setting, "save_log", &chr->show_save_log); + if (libconfig->setting_lookup_int(setting, "console_silent", &showmsg->silent) == CONFIG_TRUE) { + if (showmsg->silent) // only bother if its actually enabled + ShowInfo("Console Silent Setting: %d\n", showmsg->silent); + } + libconfig->setting_lookup_mutable_string(setting, "timestamp_format", showmsg->timestamp_format, sizeof(showmsg->timestamp_format)); + + return true; +} + +/** + * Reads the 'char_configuration/player' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_player(const char *filename, const struct config_t *config, bool imported) +{ + bool retval = true; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if (!chr->config_read_player_new(filename, config, imported)) + retval = false; + if (!chr->config_read_player_name(filename, config, imported)) + retval = false; + if (!chr->config_read_player_deletion(filename, config, imported)) + retval = false; + if (!chr->config_read_player_fame(filename, config, imported)) + retval = false; + + return retval; +} + +/** + * Reads the 'char_configuration/player/fame' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_player_fame(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/player/fame")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/player/fame was not found in %s!\n", filename); + return false; + } + + libconfig->setting_lookup_int(setting, "alchemist", &fame_list_size_chemist); + if (fame_list_size_chemist > MAX_FAME_LIST) { + ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST); + fame_list_size_chemist = MAX_FAME_LIST; + } + + libconfig->setting_lookup_int(setting, "blacksmith", &fame_list_size_smith); + if (fame_list_size_smith > MAX_FAME_LIST) { + ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST); + fame_list_size_smith = MAX_FAME_LIST; + } + + libconfig->setting_lookup_int(setting, "taekwon", &fame_list_size_taekwon); + if (fame_list_size_taekwon > MAX_FAME_LIST) { + ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST); + fame_list_size_taekwon = MAX_FAME_LIST; + } + + return true; +} + +/** + * Reads the 'char_configuration/player/deletion' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_player_deletion(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/player/deletion")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/player/deletion was not found in %s!\n", filename); + return false; + } + libconfig->setting_lookup_int(setting, "level", &char_del_level); + libconfig->setting_lookup_int(setting, "delay", &char_del_delay); + libconfig->setting_lookup_bool_real(setting, "use_aegis_delete", &char_aegis_delete); + + return true; +} + +/** + * Reads the 'char_configuration/player/name' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_player_name(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/player/name")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/player/name was not found in %s!\n", filename); + return false; + } + libconfig->setting_lookup_mutable_string(setting, "unknown_char_name", unknown_char_name, sizeof(unknown_char_name)); + libconfig->setting_lookup_mutable_string(setting, "name_letters", char_name_letters, sizeof(char_name_letters)); + libconfig->setting_lookup_int(setting, "name_option", &char_name_option); + libconfig->setting_lookup_bool_real(setting, "name_ignoring_case", &name_ignoring_case); + + return true; +} + +/** + * Defines start_items based on '(...)/player/new/start_item'. + * + * @param setting The already retrieved start_item setting. + */ +void char_config_set_start_item(const struct config_setting_t *setting) +{ + int i, count; + + nullpo_retv(setting); + + VECTOR_CLEAR(start_items); + + count = libconfig->setting_length(setting); + if (!count) + return; + + VECTOR_ENSURE(start_items, count, 1); + + for (i = 0; i < count; i++) { + const struct config_setting_t *t = libconfig->setting_get_elem(setting, i); + struct start_item_s start_item = { 0 }; + + if (t == NULL) continue; - if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2) + if (libconfig->setting_lookup_int(t, "id", &start_item.id) != CONFIG_TRUE) { + ShowWarning("char_config_read: entry (%d) is missing id! Ignoring...\n", i); + continue; + } + if (libconfig->setting_lookup_int(t, "amount", &start_item.amount) != CONFIG_TRUE) { + ShowWarning("char_config_read: entry (%d) is missing amount! Defaulting to 1...\n", i); + start_item.amount = 1; + } + if (libconfig->setting_lookup_bool_real(t, "stackable", &start_item.stackable) != CONFIG_TRUE) { + // Without knowing if the item is stackable or not we can't add it! + ShowWarning("char_config_read: entry (%d) is missing stackable! Ignoring...\n", i); continue; + } + if (libconfig->setting_lookup_int(t, "loc", &start_item.loc) != CONFIG_TRUE) + start_item.loc = 0; + VECTOR_PUSH(start_items, start_item); + } +} - remove_control_chars(w1); - remove_control_chars(w2); - if(strcmpi(w1,"timestamp_format") == 0) { - safestrncpy(showmsg->timestamp_format, w2, sizeof(showmsg->timestamp_format)); - } else if(strcmpi(w1,"console_silent")==0){ - showmsg->silent = atoi(w2); - if (showmsg->silent) /* only bother if its actually enabled */ - ShowInfo("Console Silent Setting: %d\n", atoi(w2)); - } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ - showmsg->stdout_with_ansisequence = config_switch(w2) ? true : false; - } else if (strcmpi(w1, "userid") == 0) { - safestrncpy(chr->userid, w2, sizeof(chr->userid)); - } else if (strcmpi(w1, "passwd") == 0) { - safestrncpy(chr->passwd, w2, sizeof(chr->passwd)); - } else if (strcmpi(w1, "server_name") == 0) { - safestrncpy(chr->server_name, w2, sizeof(chr->server_name)); - } else if (strcmpi(w1, "wisp_server_name") == 0) { - if (strlen(w2) >= 4) { - safestrncpy(wisp_server_name, w2, sizeof(wisp_server_name)); - } - } else if (strcmpi(w1, "login_ip") == 0) { - login_ip = sockt->host2ip(w2); - if (login_ip) { - char ip_str[16]; - safestrncpy(login_ip_str, w2, sizeof(login_ip_str)); - ShowStatus("Login server IP address : %s -> %s\n", w2, sockt->ip2str(login_ip, ip_str)); - } - } else if (strcmpi(w1, "login_port") == 0) { - login_port = atoi(w2); - } else if (strcmpi(w1, "char_ip") == 0) { - chr->ip = sockt->host2ip(w2); - if (chr->ip) { - char ip_str[16]; - safestrncpy(char_ip_str, w2, sizeof(char_ip_str)); - ShowStatus("Character server IP address : %s -> %s\n", w2, sockt->ip2str(chr->ip, ip_str)); - } - } else if (strcmpi(w1, "bind_ip") == 0) { - bind_ip = sockt->host2ip(w2); - if (bind_ip) { - char ip_str[16]; - safestrncpy(bind_ip_str, w2, sizeof(bind_ip_str)); - ShowStatus("Character server binding IP address : %s -> %s\n", w2, sockt->ip2str(bind_ip, ip_str)); - } - } else if (strcmpi(w1, "char_port") == 0) { - chr->port = atoi(w2); - } else if (strcmpi(w1, "char_server_type") == 0) { - chr->server_type = atoi(w2); - } else if (strcmpi(w1, "char_new") == 0) { - char_new = (bool)atoi(w2); - } else if (strcmpi(w1, "char_new_display") == 0) { - chr->new_display = atoi(w2); - } else if (strcmpi(w1, "max_connect_user") == 0) { - max_connect_user = atoi(w2); - if (max_connect_user < -1) - max_connect_user = -1; // unlimited online players - } else if(strcmpi(w1, "gm_allow_group") == 0) { - gm_allow_group = atoi(w2); - } else if (strcmpi(w1, "autosave_time") == 0) { - autosave_interval = atoi(w2) * 1000; - if (autosave_interval <= 0) - autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; - } else if (strcmpi(w1, "save_log") == 0) { - save_log = config_switch(w2); +/** + * Reads the 'char_configuration/player/new' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_player_new(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL, *setting2 = NULL; +#ifdef RENEWAL + const char *start_point_setting = "start_point_re"; +#else + const char *start_point_setting = "start_point_pre"; +#endif + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/player/new")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/player/new was not found in %s!\n", filename); + return false; + } + + if (libconfig->setting_lookup_int(setting, "zeny", &start_zeny) == CONFIG_TRUE) { + if (start_zeny > MAX_ZENY) { + ShowWarning("char_config_read: player/new/zeny is too big! Capping to MAX_ZENY.\n"); + start_zeny = MAX_ZENY; } - #ifdef RENEWAL - else if (strcmpi(w1, "start_point_re") == 0) { - char map[MAP_NAME_LENGTH_EXT]; - int x, y; - if (sscanf(w2, "%15[^,],%d,%d", map, &x, &y) < 3) - continue; - start_point.map = mapindex->name2id(map); - if (!start_point.map) - ShowError("Specified start_point_re '%s' not found in map-index cache.\n", map); - start_point.x = x; - start_point.y = y; - } - #else - else if (strcmpi(w1, "start_point_pre") == 0) { - char map[MAP_NAME_LENGTH_EXT]; - int x, y; - if (sscanf(w2, "%15[^,],%d,%d", map, &x, &y) < 3) - continue; - start_point.map = mapindex->name2id(map); - if (!start_point.map) - ShowError("Specified start_point_pre '%s' not found in map-index cache.\n", map); - start_point.x = x; - start_point.y = y; - } - #endif - else if (strcmpi(w1, "start_items") == 0) { - int i; - char *split; + } - i = 0; - split = strtok(w2, ","); - while (split != NULL && i < MAX_START_ITEMS * 3) { - char *split2 = split; - split = strtok(NULL, ","); - start_items[i] = atoi(split2); + if ((setting2 = libconfig->setting_get_member(setting, "start_items"))) + chr->config_set_start_item(setting2); - if (start_items[i] < 0) - start_items[i] = 0; + // start_point / start_point_pre + if ((setting2 = libconfig->setting_get_member(setting, start_point_setting))) { + const char *str = NULL; + if (libconfig->setting_lookup_string(setting2, "map", &str) == CONFIG_TRUE) { + start_point.map = mapindex->name2id(str); + if (start_point.map == 0) + ShowError("char_config_read_player_new: Specified start_point %s not found in map-index cache.\n", str); + libconfig->setting_lookup_int16(setting2, "x", &start_point.x); + libconfig->setting_lookup_int16(setting2, "y", &start_point.y); + } + } - ++i; - } + return true; +} - // Format is: id1,quantity1,stackable1,idN,quantityN,stackableN - if( i%3 ) - { - ShowWarning("chr->config_read: There are not enough parameters in start_items, ignoring last item...\n"); - if( i%3 == 1 ) - start_items[i-1] = 0; - else - start_items[i-2] = 0; - } - } else if (strcmpi(w1, "start_zeny") == 0) { - start_zeny = atoi(w2); - if (start_zeny < 0) - start_zeny = 0; - } else if(strcmpi(w1,"log_char")==0) { - log_char = atoi(w2); //log char or not [devil] - } else if (strcmpi(w1, "unknown_char_name") == 0) { - safestrncpy(unknown_char_name, w2, sizeof(unknown_char_name)); - unknown_char_name[NAME_LENGTH-1] = '\0'; - } else if (strcmpi(w1, "name_ignoring_case") == 0) { - name_ignoring_case = (bool)config_switch(w2); - } else if (strcmpi(w1, "char_name_option") == 0) { - char_name_option = atoi(w2); - } else if (strcmpi(w1, "char_name_letters") == 0) { - safestrncpy(char_name_letters, w2, sizeof(char_name_letters)); - } else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus] - char_del_level = atoi(w2); - } else if (strcmpi(w1, "char_del_delay") == 0) { - char_del_delay = atoi(w2); - } else if (strcmpi(w1, "char_aegis_delete") == 0) { - char_aegis_delete = atoi(w2); - } else if(strcmpi(w1,"db_path")==0) { - safestrncpy(db_path, w2, sizeof(db_path)); - } else if (strcmpi(w1, "fame_list_alchemist") == 0) { - fame_list_size_chemist = atoi(w2); - if (fame_list_size_chemist > MAX_FAME_LIST) { - ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST); - fame_list_size_chemist = MAX_FAME_LIST; - } - } else if (strcmpi(w1, "fame_list_blacksmith") == 0) { - fame_list_size_smith = atoi(w2); - if (fame_list_size_smith > MAX_FAME_LIST) { - ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST); - fame_list_size_smith = MAX_FAME_LIST; - } - } else if (strcmpi(w1, "fame_list_taekwon") == 0) { - fame_list_size_taekwon = atoi(w2); - if (fame_list_size_taekwon > MAX_FAME_LIST) { - ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST); - fame_list_size_taekwon = MAX_FAME_LIST; - } - } else if (strcmpi(w1, "guild_exp_rate") == 0) { - guild_exp_rate = atoi(w2); - } else if (strcmpi(w1, "char_maintenance_min_group_id") == 0) { - char_maintenance_min_group_id = atoi(w2); - } else if (strcmpi(w1, "import") == 0) { - chr->config_read(w2); - } else - chr->config_dispatch(w1,w2); +/** + * Reads the 'char_configuration/permission' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool char_config_read_permission(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/permission")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/permission was not found in %s!\n", filename); + return false; } - fclose(fp); - ShowInfo("Done reading %s.\n", cfgName); - return 0; + libconfig->setting_lookup_bool_real(setting, "enable_char_creation", &enable_char_creation); + libconfig->setting_lookup_int16(setting, "display_new", &chr->new_display); + libconfig->setting_lookup_int(setting, "max_connect_user", &max_connect_user); + libconfig->setting_lookup_int(setting, "gm_allow_group", &gm_allow_group); + libconfig->setting_lookup_int(setting, "maintenance_min_group_id", &char_maintenance_min_group_id); + if (libconfig->setting_lookup_int(setting, "server_type", &chr->server_type) == CONFIG_TRUE) { + if (chr->server_type < CST_NORMAL || chr->server_type >= CST_MAX) { + ShowWarning("char_config_read: Invalid permission/server_type %d, defaulting to CST_NORMAL.\n", chr->server_type); + chr->server_type = CST_NORMAL; + } + } + + return true; +} + +/** + * Loads an IP into 'out_ip' and shows status. + * + * @param type[in] String containing the type of IP being set (for logging purposes). + * @param value[in] New ip value to parse. + * @param out_ip[in] Pointer to numeric value that will be changed. + * @param out_ip_str[in,out] Pointer to str value that will be changed (expected to be already initialized, to display previous value, if any). + * + * @retval false in case of error. + */ +bool char_config_set_ip(const char *type, const char *value, uint32 *out_ip, char *out_ip_str) +{ + uint32 ip = 0; + + nullpo_retr(false, type); + nullpo_retr(false, value); + nullpo_retr(false, out_ip); + nullpo_retr(false, out_ip_str); + + if ((ip = sockt->host2ip(value)) == 0) + return false; + *out_ip = ip; + + ShowStatus("%s IP address : %s -> %s\n", type, out_ip_str[0] ? out_ip_str : "0.0.0.0", sockt->ip2str(ip, NULL)); + safestrncpy(out_ip_str, value, sizeof(out_ip_str)); + return true; } int do_final(void) { @@ -5685,6 +6056,8 @@ int do_final(void) { for (i = 0; i < MAX_MAP_SERVERS; i++) VECTOR_CLEAR(chr->server[i].maps); + VECTOR_CLEAR(start_items); + aFree(chr->CHAR_CONF_NAME); aFree(chr->NET_CONF_NAME); aFree(chr->SQL_CONF_NAME); @@ -5791,10 +6164,12 @@ int do_init(int argc, char **argv) { char_load_defaults(); - chr->CHAR_CONF_NAME = aStrdup("conf/char-server.conf"); + chr->CHAR_CONF_NAME = aStrdup("conf/char/char-server.conf"); chr->NET_CONF_NAME = aStrdup("conf/network.conf"); - chr->SQL_CONF_NAME = aStrdup("conf/inter-server.conf"); - chr->INTER_CONF_NAME = aStrdup("conf/inter-server.conf"); + chr->SQL_CONF_NAME = aStrdup("conf/common/inter-server.conf"); + chr->INTER_CONF_NAME = aStrdup("conf/common/inter-server.conf"); + + VECTOR_INIT(start_items); for (i = 0; i < MAX_MAP_SERVERS; i++) VECTOR_INIT(chr->server[i].maps); @@ -5813,16 +6188,39 @@ int do_init(int argc, char **argv) { start_point.map = mapindex->name2id("new_1-1"); #endif + safestrncpy(chr->userid, "s1", sizeof(chr->userid)); + safestrncpy(chr->passwd, "p1", sizeof(chr->passwd)); + cmdline->exec(argc, argv, CMDLINE_OPT_NORMAL); - chr->config_read(chr->CHAR_CONF_NAME); + chr->config_read(chr->CHAR_CONF_NAME, false); sockt->net_config_read(chr->NET_CONF_NAME); - chr->sql_config_read(chr->SQL_CONF_NAME); + chr->sql_config_read(chr->SQL_CONF_NAME, false); + + { + // TODO: Remove this when no longer needed. +#define CHECK_OLD_LOCAL_CONF(oldname, newname) do { \ + if (stat((oldname), &fileinfo) == 0 && fileinfo.st_size > 0) { \ + ShowWarning("An old configuration file \"%s\" was found.\n", (oldname)); \ + ShowWarning("If it contains settings you wish to keep, please merge them into \"%s\".\n", (newname)); \ + ShowWarning("Otherwise, just delete it.\n"); \ + ShowInfo("Resuming in 10 seconds...\n"); \ + HSleep(10); \ + } \ +} while (0) + struct stat fileinfo; + + CHECK_OLD_LOCAL_CONF("conf/import/char_conf.txt", "conf/import/char-server.conf"); + CHECK_OLD_LOCAL_CONF("conf/import/inter_conf.txt", "conf/import/inter-server.conf"); + CHECK_OLD_LOCAL_CONF("conf/import/packet_conf.txt", "conf/import/socket.conf"); + +#undef CHECK_OLD_LOCAL_CONF + } #ifndef BUILDBOT if (strcmp(chr->userid, "s1")==0 && strcmp(chr->passwd, "p1")==0) { ShowWarning("Using the default user/password s1/p1 is NOT RECOMMENDED.\n"); ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n"); - ShowNotice("And then change the user/password to use in conf/char-server.conf (or conf/import/char_conf.txt)\n"); + ShowNotice("And then change the user/password to use in conf/char/char-server.conf (or conf/import/char-server.conf)\n"); } #endif @@ -5947,6 +6345,9 @@ void char_defaults(void) chr->server_type = 0; chr->new_display = 0; + chr->show_save_log = true; + chr->enable_logs = true; + chr->waiting_disconnect = char_waiting_disconnect; chr->delete_char_sql = char_delete_char_sql; chr->create_online_char_data = char_create_online_char_data; @@ -6103,6 +6504,20 @@ void char_defaults(void) chr->online_data_cleanup_sub = char_online_data_cleanup_sub; chr->online_data_cleanup = char_online_data_cleanup; chr->sql_config_read = char_sql_config_read; - chr->config_dispatch = char_config_dispatch; + chr->sql_config_read_registry = char_sql_config_read_registry; + chr->sql_config_read_pc = char_sql_config_read_pc; + chr->sql_config_read_guild = char_sql_config_read_guild; chr->config_read = char_config_read; + chr->config_read_database = char_config_read_database; + chr->config_read_console = char_config_read_console; + chr->config_read_player_fame = char_config_read_player_fame; + chr->config_read_player_deletion = char_config_read_player_deletion; + chr->config_read_player_name = char_config_read_player_name; + chr->config_set_start_item = char_config_set_start_item; + chr->config_read_player_new = char_config_read_player_new; + chr->config_read_player = char_config_read_player; + chr->config_read_permission = char_config_read_permission; + chr->config_set_ip = char_config_set_ip; + chr->config_read_inter = char_config_read_inter; + chr->config_read_top = char_config_read_top; } diff --git a/src/char/char.h b/src/char/char.h index a0cfb3bd7..4dbdfd9db 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -26,6 +26,10 @@ #include "common/db.h" #include "common/mmo.h" +/* Forward Declarations */ +struct config_setting_t; // common/conf.h +struct config_t; // common/conf.h + enum E_CHARSERVER_ST { CHARSERVER_ST_RUNNING = CORE_ST_LAST, CHARSERVER_ST_SHUTDOWN, @@ -110,13 +114,16 @@ struct char_interface { uint32 ip; uint16 port; int server_type; - int new_display; + int16 new_display; ///< Display 'New' in the server list. char *CHAR_CONF_NAME; char *NET_CONF_NAME; ///< Network config filename char *SQL_CONF_NAME; char *INTER_CONF_NAME; + bool show_save_log; ///< Show loading/saving messages. + bool enable_logs; ///< Whether to log char server operations. + int (*waiting_disconnect) (int tid, int64 tick, int id, intptr_t data); int (*delete_char_sql) (int char_id); struct DBData (*create_online_char_data) (union DBKey key, va_list args); @@ -272,9 +279,24 @@ struct char_interface { int (*check_connect_login_server) (int tid, int64 tick, int id, intptr_t data); int (*online_data_cleanup_sub) (union DBKey key, struct DBData *data, va_list ap); int (*online_data_cleanup) (int tid, int64 tick, int id, intptr_t data); - void (*sql_config_read) (const char* cfgName); - void (*config_dispatch) (char *w1, char *w2); - int (*config_read) (const char* cfgName); + + bool (*sql_config_read) (const char *filename, bool imported); + bool (*sql_config_read_registry) (const char *filename, const struct config_t *config, bool imported); + bool (*sql_config_read_pc) (const char *filename, const struct config_t *config, bool imported); + bool (*sql_config_read_guild) (const char *filename, const struct config_t *config, bool imported); + bool (*config_read) (const char *filename, bool imported); + bool (*config_read_database) (const char *filename, const struct config_t *config, bool imported); + bool (*config_read_console) (const char *filename, const struct config_t *config, bool imported); + bool (*config_read_player_fame) (const char *filename, const struct config_t *config, bool imported); + bool (*config_read_player_deletion) (const char *filename, const struct config_t *config, bool imported); + bool (*config_read_player_name) (const char *filename, const struct config_t *config, bool imported); + void (*config_set_start_item) (const struct config_setting_t *setting); + bool (*config_read_player_new) (const char *filename, const struct config_t *config, bool imported); + bool (*config_read_player) (const char *filename, const struct config_t *config, bool imported); + bool (*config_read_permission) (const char *filename, const struct config_t *config, bool imported); + bool (*config_set_ip) (const char *type, const char *value, uint32 *out_ip, char *out_ip_str); + bool (*config_read_inter) (const char *filename, const struct config_t *config, bool imported); + bool (*config_read_top) (const char *filename, const struct config_t *config, bool imported); }; #ifdef HERCULES_CORE @@ -282,7 +304,6 @@ extern int char_name_option; extern char char_name_letters[]; extern bool char_gm_read; extern int autosave_interval; -extern int save_log; extern char db_path[]; extern char char_db[256]; extern char scdata_db[256]; @@ -318,7 +339,6 @@ extern char char_reg_str_db[32]; extern char char_reg_num_db[32]; extern int guild_exp_rate; -extern int log_inter; void char_load_defaults(void); void char_defaults(void); diff --git a/src/char/int_elemental.c b/src/char/int_elemental.c index c25cfa5c8..aed25febd 100644 --- a/src/char/int_elemental.c +++ b/src/char/int_elemental.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -128,7 +128,7 @@ bool mapif_elemental_load(int ele_id, int char_id, struct s_elemental *ele) { SQL->GetData(inter->sql_handle, 13, &data, NULL); ele->hit = atoi(data); SQL->GetData(inter->sql_handle, 14, &data, NULL); ele->life_time = atoi(data); SQL->FreeResult(inter->sql_handle); - if( save_log ) + if (chr->show_save_log) ShowInfo("Elemental loaded (%d - %d).\n", ele->elemental_id, ele->char_id); return true; diff --git a/src/char/int_guild.c b/src/char/int_guild.c index d2507a2f9..dcc1ed7cb 100644 --- a/src/char/int_guild.c +++ b/src/char/int_guild.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -83,9 +83,9 @@ int inter_guild_save_timer(int tid, int64 tick, int id, intptr_t data) { state++; } - if( g->save_flag == GS_REMOVE ) - {// Nothing to save, guild is ready for removal. - if (save_log) + if (g->save_flag == GS_REMOVE) { + // Nothing to save, guild is ready for removal. + if (chr->show_save_log) ShowInfo("Guild Unloaded (%d - %s)\n", g->guild_id, g->name); db_remove(inter_guild->guild_db, key); } @@ -337,8 +337,8 @@ int inter_guild_tosql(struct guild *g,int flag) } } - if (save_log) - ShowInfo("Saved guild (%d - %s):%s\n",g->guild_id,g->name,t_info); + if (chr->show_save_log) + ShowInfo("Saved guild (%d - %s):%s\n", g->guild_id, g->name, t_info); return 1; } @@ -525,7 +525,7 @@ struct guild * inter_guild_fromsql(int guild_id) idb_put(inter_guild->guild_db, guild_id, g); //Add to cache g->save_flag |= GS_REMOVE; //But set it to be removed, in case it is not needed for long. - if (save_log) + if (chr->show_save_log) ShowInfo("Guild loaded (%d - %s)\n", guild_id, g->name); return g; @@ -548,7 +548,7 @@ int inter_guild_castle_tosql(struct guild_castle *gc) if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) Sql_ShowDebug(inter->sql_handle); - else if(save_log) + else if (chr->show_save_log) ShowInfo("Saved guild castle (%d)\n", gc->castle_id); StrBuf->Destroy(&buf); @@ -600,7 +600,7 @@ struct guild_castle* inter_guild_castle_fromsql(int castle_id) idb_put(inter_guild->castle_db, castle_id, gc); - if (save_log) + if (chr->show_save_log) ShowInfo("Loaded guild castle (%d - guild %d)\n", castle_id, gc->guild_id); return gc; @@ -1233,9 +1233,9 @@ int mapif_parse_CreateGuild(int fd, int account_id, const char *name, const stru mapif->guild_created(fd,account_id,g); mapif->guild_info(fd,g); - if(log_inter) + if (inter->enable_logs) inter->log("guild %s (id=%d) created by master %s (id=%d)\n", - name, g->guild_id, master->name, master->account_id ); + name, g->guild_id, master->name, master->account_id); return 0; } @@ -1442,8 +1442,8 @@ int mapif_parse_BreakGuild(int fd, int guild_id) mapif->guild_broken(guild_id,0); - if(log_inter) - inter->log("guild %s (id=%d) broken\n",g->name,guild_id); + if (inter->enable_logs) + inter->log("guild %s (id=%d) broken\n", g->name, guild_id); //Remove the guild from memory. [Skotlex] idb_remove(inter_guild->guild_db, guild_id); @@ -1821,7 +1821,7 @@ int mapif_parse_GuildCastleDataSave(int fd, int castle_id, int index, int value) switch (index) { case 1: - if (log_inter && gc->guild_id != value) { + if (inter->enable_logs && gc->guild_id != value) { int gid = (value) ? value : gc->guild_id; struct guild *g = idb_get(inter_guild->guild_db, gid); inter->log("guild %s (id=%d) %s castle id=%d\n", diff --git a/src/char/int_homun.c b/src/char/int_homun.c index 90643699c..04f4c8f21 100644 --- a/src/char/int_homun.c +++ b/src/char/int_homun.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -262,7 +262,7 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd) } SQL->FreeResult(inter->sql_handle); - if( save_log ) + if (chr->show_save_log) ShowInfo("Homunculus loaded (%d - %s).\n", hd->hom_id, hd->name); return true; diff --git a/src/char/int_mercenary.c b/src/char/int_mercenary.c index 0bc3c0909..f96e45a50 100644 --- a/src/char/int_mercenary.c +++ b/src/char/int_mercenary.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -168,7 +168,7 @@ bool mapif_mercenary_load(int merc_id, int char_id, struct s_mercenary *merc) SQL->GetData(inter->sql_handle, 3, &data, NULL); merc->kill_count = atoi(data); SQL->GetData(inter->sql_handle, 4, &data, NULL); merc->life_time = atoi(data); SQL->FreeResult(inter->sql_handle); - if( save_log ) + if (chr->show_save_log) ShowInfo("Mercenary loaded (%d - %d).\n", merc->mercenary_id, merc->char_id); return true; diff --git a/src/char/int_party.c b/src/char/int_party.c index 684d846d4..e2be9f3ca 100644 --- a/src/char/int_party.c +++ b/src/char/int_party.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -190,7 +190,7 @@ int inter_party_tosql(struct party *p, int flag, int index) Sql_ShowDebug(inter->sql_handle); } - if( save_log ) + if (chr->show_save_log) ShowInfo("Party Saved (%d - %s)\n", party_id, p->name); return 1; } @@ -257,7 +257,7 @@ struct party_data *inter_party_fromsql(int party_id) } SQL->FreeResult(inter->sql_handle); - if( save_log ) + if (chr->show_save_log) ShowInfo("Party loaded (%d - %s).\n", party_id, p->party.name); //Add party to memory. CREATE(p, struct party_data, 1); diff --git a/src/char/int_pet.c b/src/char/int_pet.c index eb9bda8d0..489b10e3e 100644 --- a/src/char/int_pet.c +++ b/src/char/int_pet.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -85,7 +85,7 @@ int inter_pet_tosql(const struct s_pet *p) pet_id = p->pet_id; } - if (save_log) + if (chr->show_save_log) ShowInfo("Pet saved %d - %s.\n", pet_id, p->name); return pet_id; @@ -130,7 +130,7 @@ int inter_pet_fromsql(int pet_id, struct s_pet* p) p->hungry = cap_value(p->hungry, 0, 100); p->intimate = cap_value(p->intimate, 0, 1000); - if( save_log ) + if (chr->show_save_log) ShowInfo("Pet loaded (%d - %s).\n", pet_id, p->name); } return 0; diff --git a/src/char/inter.c b/src/char/inter.c index 5fb35e6aa..4c5eb05e5 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -36,6 +36,7 @@ #include "char/int_storage.h" #include "char/mapif.h" #include "common/cbasetypes.h" +#include "common/conf.h" #include "common/db.h" #include "common/memmgr.h" #include "common/mmo.h" @@ -62,7 +63,7 @@ char char_server_pw[100] = "ragnarok"; char char_server_db[32] = "ragnarok"; char default_codepage[32] = ""; //Feature by irmin. -unsigned int party_share_level = 10; +int party_share_level = 10; // recv. packet list int inter_recv_packet_length[] = { @@ -791,50 +792,84 @@ int inter_accreg_fromsql(int account_id,int char_id, int fd, int type) return 1; } -/*========================================== - * read config file - *------------------------------------------*/ -static int inter_config_read(const char* cfgName) +/** + * Reads the 'char_configuration/sql_connection' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool inter_config_read_connection(const char *filename, const struct config_t *config, bool imported) { - char line[1024], w1[1024], w2[1024]; - FILE* fp; + const struct config_setting_t *setting = NULL; - nullpo_retr(1, cfgName); - fp = fopen(cfgName, "r"); - if(fp == NULL) { - ShowError("File not found: %s\n", cfgName); - return 1; + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/sql_connection")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/sql_connection was not found in %s!\n", filename); + ShowWarning("inter_config_read_connection: Defaulting sql_connection...\n"); + return false; } - while (fgets(line, sizeof(line), fp)) { - int i = sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2); - if(i != 2) - continue; + libconfig->setting_lookup_int(setting, "db_port", &char_server_port); + libconfig->setting_lookup_mutable_string(setting, "db_hostname", char_server_ip, sizeof(char_server_ip)); + libconfig->setting_lookup_mutable_string(setting, "db_username", char_server_id, sizeof(char_server_id)); + libconfig->setting_lookup_mutable_string(setting, "db_password", char_server_pw, sizeof(char_server_pw)); + libconfig->setting_lookup_mutable_string(setting, "db_database", char_server_db, sizeof(char_server_db)); + libconfig->setting_lookup_mutable_string(setting, "default_codepage", default_codepage, sizeof(default_codepage)); + + return true; +} - if(!strcmpi(w1,"char_server_ip")) { - safestrncpy(char_server_ip, w2, sizeof(char_server_ip)); - } else if(!strcmpi(w1,"char_server_port")) { - char_server_port = atoi(w2); - } else if(!strcmpi(w1,"char_server_id")) { - safestrncpy(char_server_id, w2, sizeof(char_server_id)); - } else if(!strcmpi(w1,"char_server_pw")) { - safestrncpy(char_server_pw, w2, sizeof(char_server_pw)); - } else if(!strcmpi(w1,"char_server_db")) { - safestrncpy(char_server_db, w2, sizeof(char_server_db)); - } else if(!strcmpi(w1,"default_codepage")) { - safestrncpy(default_codepage, w2, sizeof(default_codepage)); - } else if(!strcmpi(w1,"party_share_level")) - party_share_level = atoi(w2); - else if(!strcmpi(w1,"log_inter")) - log_inter = atoi(w2); - else if(!strcmpi(w1,"import")) - inter->config_read(w2); +/** + * Reads the 'inter_configuration' config file and initializes required variables. + * + * @param filename Path to configuration file + * @param imported Whether the current config is from an imported file. + * + * @retval false in case of error. + */ +bool inter_config_read(const char *filename, bool imported) +{ + struct config_t config; + const struct config_setting_t *setting = NULL; + const char *import = NULL; + bool retval = true; + + nullpo_retr(false, filename); + + if (!libconfig->load_file(&config, filename)) + return false; + + if ((setting = libconfig->lookup(&config, "inter_configuration")) == NULL) { + libconfig->destroy(&config); + if (imported) + return true; + ShowError("inter_config_read: inter_configuration was not found in %s!\n", filename); + return false; } - fclose(fp); + libconfig->setting_lookup_int(setting, "party_share_level", &party_share_level); + libconfig->setting_lookup_bool_real(setting, "log_inter", &inter->enable_logs); - ShowInfo ("Done reading %s.\n", cfgName); + ShowInfo("Done reading %s.\n", filename); - return 0; + // import should overwrite any previous configuration, so it should be called last + if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) { + if (strcmp(import, filename) == 0 || strcmp(import, chr->INTER_CONF_NAME) == 0) { + ShowWarning("inter_config_read: Loop detected in %s! Skipping 'import'...\n", filename); + } else { + if (!inter->config_read(import, true)) + retval = false; + } + } + + libconfig->destroy(&config); + return retval; } /** @@ -879,9 +914,7 @@ int inter_log(char* fmt, ...) // initialize int inter_init_sql(const char *file) { - //int i; - - inter->config_read(file); + inter->config_read(file, false); //DB connection initialized inter->sql_handle = SQL->Malloc(); @@ -1364,6 +1397,7 @@ void inter_defaults(void) { inter = &inter_s; + inter->enable_logs = true; inter->sql_handle = NULL; inter->msg_txt = inter_msg_txt; @@ -1384,4 +1418,5 @@ void inter_defaults(void) inter->check_length = inter_check_length; inter->parse_frommap = inter_parse_frommap; inter->final = inter_final; + inter->config_read_connection = inter_config_read_connection; } diff --git a/src/char/inter.h b/src/char/inter.h index 4e8d113ce..57d1db86c 100644 --- a/src/char/inter.h +++ b/src/char/inter.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -28,11 +28,13 @@ /* Forward Declarations */ struct Sql; // common/sql.h +struct config_t; // common/conf.h /** * inter interface **/ struct inter_interface { + bool enable_logs; ///< Whether to log inter-server operations. struct Sql *sql_handle; const char* (*msg_txt) (int msg_number); bool (*msg_config_read) (const char *cfg_name, bool allow_override); @@ -42,7 +44,6 @@ struct inter_interface { void (*msg_to_fd) (int fd, int u_fd, int aid, char *msg, ...) __attribute__((format(printf, 4, 5))); void (*savereg) (int account_id, int char_id, const char *key, unsigned int index, intptr_t val, bool is_string); int (*accreg_fromsql) (int account_id,int char_id, int fd, int type); - int (*config_read) (const char* cfgName); int (*vlog) (char* fmt, va_list ap); int (*log) (char* fmt, ...); int (*init_sql) (const char *file); @@ -52,10 +53,12 @@ struct inter_interface { int (*check_length) (int fd, int length); int (*parse_frommap) (int fd); void (*final) (void); + bool (*config_read) (const char *filename, bool imported); + bool (*config_read_connection) (const char *filename, const struct config_t *config, bool imported); }; #ifdef HERCULES_CORE -extern unsigned int party_share_level; +extern int party_share_level; ///< Share range for parties. void inter_defaults(void); #endif // HERCULES_CORE diff --git a/src/char/pincode.c b/src/char/pincode.c index 6930a7a57..fc1a4c037 100644 --- a/src/char/pincode.c +++ b/src/char/pincode.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -24,6 +24,7 @@ #include "char/char.h" #include "common/cbasetypes.h" +#include "common/conf.h" #include "common/db.h" #include "common/mmo.h" #include "common/nullpo.h" @@ -178,33 +179,52 @@ void pincode_decrypt(unsigned int userSeed, char* pin) { sprintf(pin, "%d%d%d%d", pin[0], pin[1], pin[2], pin[3]); } -bool pincode_config_read(char *w1, char *w2) { +/** + * Reads the 'char_configuration/pincode' config entry and initializes required variables. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool pincode_config_read(const char *filename, const struct config_t *config, bool imported) +{ + const struct config_setting_t *setting = NULL; + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "char_configuration/pincode")) == NULL) { + if (imported) + return true; + ShowError("char_config_read: char_configuration/pincode was not found in %s!\n", filename); + return false; + } - nullpo_ret(w1); - nullpo_ret(w2); - while ( true ) { - if ( strcmpi(w1, "pincode_enabled") == 0 ) { - pincode->enabled = atoi(w2); + if (libconfig->setting_lookup_bool(setting, "enabled", &pincode->enabled) == CONFIG_TRUE) { #if PACKETVER < 20110309 - if( pincode->enabled ) { - ShowWarning("pincode_enabled requires PACKETVER 20110309 or higher. disabling...\n"); - pincode->enabled = 0; - } + if (pincode->enabled) { + ShowWarning("pincode_enabled requires PACKETVER 20110309 or higher. disabling...\n"); + pincode->enabled = 0; + } #endif - } else if ( strcmpi(w1, "pincode_changetime") == 0 ) { - pincode->changetime = atoi(w2)*60; - } else if ( strcmpi(w1, "pincode_maxtry") == 0 ) { - pincode->maxtry = atoi(w2); - if( pincode->maxtry > 3 ) { - ShowWarning("pincode_maxtry is too high (%d); maximum allowed: 3! capping to 3...\n", pincode->maxtry); - pincode->maxtry = 3; - } - } else if ( strcmpi(w1, "pincode_charselect") == 0 ) { - pincode->charselect = atoi(w2); - } else { - return false; + } + + if (libconfig->setting_lookup_int(setting, "change_time", &pincode->changetime) == CONFIG_TRUE) + pincode->changetime *= 60; + + if (libconfig->setting_lookup_int(setting, "max_tries", &pincode->maxtry) == CONFIG_TRUE) { + if (pincode->maxtry > 3) { + ShowWarning("pincode_maxtry is too high (%d); Maximum allowed: 3! Capping to 3...\n",pincode->maxtry); + pincode->maxtry = 3; + } + } + + if (libconfig->setting_lookup_int(setting, "request", &pincode->charselect) == CONFIG_TRUE) { + if (pincode->charselect != 1 && pincode->charselect != 0) { + ShowWarning("Invalid pincode/request! Defaulting to 0\n"); + pincode->charselect = 0; } - break; } return true; diff --git a/src/char/pincode.h b/src/char/pincode.h index fb0c1a9c4..cffaa3054 100644 --- a/src/char/pincode.h +++ b/src/char/pincode.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -23,7 +23,9 @@ #include "common/hercules.h" +/* Forward Declarations */ struct char_session_data; +struct config_t; // common/conf.h enum PincodeResponseCode { PINCODE_OK = 0, @@ -55,7 +57,7 @@ struct pincode_interface { void (*change) (int fd, struct char_session_data* sd); int (*compare) (int fd, struct char_session_data* sd, char* pin); void (*check) (int fd, struct char_session_data* sd); - bool (*config_read) (char *w1, char *w2); + bool (*config_read) (const char *filename, const struct config_t *config, bool imported); }; #ifdef HERCULES_CORE |