diff options
-rw-r--r-- | .gitignore | 10 | ||||
-rw-r--r-- | conf/char-server.conf | 188 | ||||
-rw-r--r-- | conf/char/char-server.conf | 233 | ||||
-rw-r--r-- | conf/global/console.conf | 59 | ||||
-rw-r--r-- | conf/import-tmpl/char-server.conf | 32 | ||||
-rw-r--r-- | conf/import-tmpl/char_conf.txt | 0 | ||||
-rw-r--r-- | doc/global_configuration.txt | 69 | ||||
-rw-r--r-- | src/char/char.c | 747 | ||||
-rw-r--r-- | src/char/char.h | 20 | ||||
-rw-r--r-- | src/char/pincode.c | 68 | ||||
-rw-r--r-- | src/char/pincode.h | 6 | ||||
-rw-r--r-- | src/common/mmo.h | 6 | ||||
-rwxr-xr-x | tools/configconverter.pl | 298 |
13 files changed, 1309 insertions, 427 deletions
diff --git a/.gitignore b/.gitignore index d3c4fe51a..5eccd9969 100644 --- a/.gitignore +++ b/.gitignore @@ -59,7 +59,15 @@ Thumbs.db /cache/ # /conf/ -/conf/import +/conf/import/*.conf +/conf/import/battle_conf.txt +/conf/import/inter_conf.txt +/conf/import/log_conf.txt +/conf/import/login_conf.txt +/conf/import/map_conf.txt +/conf/import/msg_conf.txt +/conf/import/packet_conf.txt +/conf/import/script_conf.txt # /log/ /log/*.log diff --git a/conf/char-server.conf b/conf/char-server.conf deleted file mode 100644 index eb2b9a63d..000000000 --- a/conf/char-server.conf +++ /dev/null @@ -1,188 +0,0 @@ -// Character Server configuration file. - -// Note: "Comments" are all text on the right side of a double slash "//" -// Whatever text is commented will not be parsed by the servers, and serves -// only as information/reference. - -// Server Communication username and password. -userid: s1 -passwd: p1 - -// Server name, use alternative character such as ASCII 160 for spaces. -// NOTE: Do not use spaces or any of these characters which are not allowed in -// Windows filenames \/:*?"<>| -// ... or else guild emblems won't work client-side! -server_name: Hercules - -// Wisp name for server: used to send wisp from server to players (between 4 to 23 characters) -wisp_server_name: Server - -// Login Server IP -// The character server connects to the login server using this IP address. -// NOTE: This is useful when you are running behind a firewall or are on -// a machine with multiple interfaces. -//login_ip: 127.0.0.1 - -// The character server listens on the interface with this IP address. -// NOTE: This allows you to run multiple servers on multiple interfaces -// while using the same ports for each server. -//bind_ip: 127.0.0.1 - -// Login Server Port -login_port: 6900 - -// Character Server IP -// The IP address which clients will use to connect. -// Set this to what your server's public IP address is. -//char_ip: 127.0.0.1 - -// Character Server Port -char_port: 6121 - -//Time-stamp format which will be printed before all messages. -//Can at most be 20 characters long. -//Common formats: -// %I:%M:%S %p (hour:minute:second 12 hour, AM/PM format) -// %H:%M:%S (hour:minute:second, 24 hour format) -// %d/%b/%Y (day/Month/year) -//For full format information, consult the strftime() manual. -//timestamp_format: [%d/%b %H:%M] - -//If redirected output contains escape sequences (color codes) -stdout_with_ansisequence: no - -//Makes server output more silent by ommitting certain types of messages: -//1: Hide Information messages -//2: Hide Status messages -//4: Hide Notice Messages -//8: Hide Warning Messages -//16: Hide Error and SQL Error messages. -//32: Hide Debug Messages -//Example: "console_silent: 7" Hides information, status and notice messages (1+2+4) -console_silent: 0 - -// Type of server. -// No functional side effects at the moment. -// Displayed next to the server name in the client. -// 0=normal, 1=maintenance, 2=over 18, 3=paying, 4=F2P -char_server_type: 0 - -// Minimum Group ID to join char server when it is on char_server_type 1 (maintenance) -char_maintenance_min_group_id: 99 - -// Enable or disable creation of new characters. -// Now it is actually supported [Kevin] -char_new: 1 - -// Display (New) in the server list. -char_new_display: 0 - -// Maximum users able to connect to the server. -// Set to 0 to disable users to log-in. (-1 means unlimited) -max_connect_user: -1 - -// Group ID that is allowed to bypass the server limit of users. -// Default: -1 = nobody (there are no groups with ID < 0) -// See: conf/groups.conf -gm_allow_group: -1 - -// How often should the server save all files? (In seconds) -// Note: Applies to all data files on TXT servers. -// On SQL servers, it applies to guilds (character save interval is defined on the map config) -autosave_time: 60 - -// Display information on the console whenever characters/guilds/parties/pets are loaded/saved? -save_log: yes - -// Start point, Map name followed by coordinates (x,y) -start_point_re: iz_int,97,90 -start_point_pre: new_1-1,53,111 - -// Starting items for new characters -// Format is: id1,quantity1,stackable1,idN,quantityN,stackableN -// stackable: -// 0 - Not stackable (weapon, armor, egg, pet armor) -// 1 - Stackable -start_items: 1201,1,0,2301,1,0 - -// Starting zeny for new characters -start_zeny: 0 - -// Size for the fame-lists -fame_list_alchemist: 10 -fame_list_blacksmith: 10 -fame_list_taekwon: 10 - -// Guild earned exp modifier. -// Adjusts taxed exp before adding it to the guild's exp. For example, if set -// to 200, the guild receives double the player's taxed exp. -guild_exp_rate: 100 - -// Name used for unknown characters -unknown_char_name: Unknown - -// To log the character server? -log_char: 1 - -// Allow or not identical name for characters but with a different case (upper/lower): -// example: Test-test-TEST-TesT; Value: 0 not allowed (default), 1 allowed -name_ignoring_case: no - -// Manage possible letters/symbol in the name of charater. Control character (0x00-0x1f) are never accepted. Possible values are: -// NOTE: Applies to character, party and guild names. -// 0: no restriction (default) -// 1: only letters/symbols in 'char_name_letters' option. -// 2: Letters/symbols in 'char_name_letters' option are forbidden. All others are possibles. -char_name_option: 1 - -// Set the letters/symbols that you want use with the 'char_name_option' option. -// Note: Don't add spaces unless you mean to add 'space' to the list. -char_name_letters: abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 - -// Restrict character deletion by BaseLevel -// 0: no restriction (players can delete characters of any level) -// -X: you can't delete chars with BaseLevel <= X -// Y: you can't delete chars with BaseLevel >= Y -// e.g. char_del_level: 80 (players can't delete characters with 80+ BaseLevel) -char_del_level: 0 - -// Amount of time in seconds by which the character deletion is delayed. -// Default: 86400 (24 hours) -// NOTE: Requires client 2010-08-03aragexeRE or newer. -char_del_delay: 86400 - -// Block deletion if character is inside a guild or a party? (BOOL) -// default: 0 official: 1 -// !!This check is imposed by Aegis to avoid dead entries in databases and _is_not_needed_ as we clear data properly!! -char_aegis_delete: 0 - -// What folder the DB files are in (item_db.conf, etc.) -db_path: db - -//================================================================== -// Pincode system -//================================================================== - -// A window is opened before you can select your character and you will have to enter a pincode by using only your mouse -// NOTE: Requires client 2011-03-09aragexeRE or newer. -// 0: disabled -// 1: enabled -pincode_enabled: 1 - -// Request Pincode only on login or on everytime char select is accessed? -// 0: only on login (default) -// 1: everytime the char select window is accessed -pincode_charselect: 0 - -// How often does a user have to change his pincode? -// Default: 0 -// 0: never -// X: every X minutes -pincode_changetime: 0 - -// How often can a user enter the wrong password? -// Default: 3 -// NOTE: The maximum on clientside is 3 -pincode_maxtry: 3 - -import: conf/import/char_conf.txt diff --git a/conf/char/char-server.conf b/conf/char/char-server.conf new file mode 100644 index 000000000..576925872 --- /dev/null +++ b/conf/char/char-server.conf @@ -0,0 +1,233 @@ +//================= Hercules Configuration ================================ +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2014-2016 Hercules Dev Team +//= +//= Hercules 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 3 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 <http://www.gnu.org/licenses/>. +//========================================================================= +//= Character Server configuration file. +//========================================================================= + +char_configuration: { + @include "conf/global/console.conf" + + // Server name, use alternative character such as ASCII 160 for spaces. + // NOTE: Do not use spaces or any of these characters which are not allowed in + // Windows filenames \/:*?"<>| + // ... or else guild emblems won't work client-side! + server_name: "Hercules" + + // Wisp name for server: used to send wisp from server to players (between 4 to 23 characters) + wisp_server_name: "Server" + + // Guild earned exp modifier. + // Adjusts taxed exp before adding it to the guild's exp. For example, + // if set to 200, the guild receives double the player's taxed exp. + guild_exp_rate: 100 + + // Information related to inter-server behavior + inter: { + // Server Communication username and password. + userid: "s1" + passwd: "p1" + // Login Server IP + // The character server connects to the login server using this IP address. + // NOTE: This is useful when you are running behind a firewall or are on + // a machine with multiple interfaces. + //login_ip: "127.0.0.1" + + // The character server listens on the interface with this IP address. + // NOTE: This allows you to run multiple servers on multiple interfaces + // while using the same ports for each server. + //bind_ip: "127.0.0.1" + + // Login Server Port + login_port: 6900 + + // Character Server IP + // The IP address which clients will use to connect. + // Set this to what your server's public IP address is. + //char_ip: "127.0.0.1" + + // Character Server Port + char_port: 6121 + } + + // Connection permission + permission: { + // Enable or disable creation of new characters. + enable_char_creation: true + + // Display (New) in the server list. + display_new: false + + // Maximum users able to connect to the server. + // Set to 0 to disable users to log-in. (-1 means unlimited) + max_connect_user: -1 + + // Group ID that is allowed to bypass the server limit of users. + // Default: -1 = nobody (there are no groups with ID < 0) + // See: conf/groups.conf + gm_allow_group: -1 + + // Type of server. + // No functional side effects at the moment. + // Displayed next to the server name in the client. + // 0=normal, 1=maintenance, 2=over 18, 3=paying, 4=F2P + server_type: 0 + + // Minimum Group ID to join char server when it is on char_server_type 1 (maintenance) + maintenance_min_group_id: 99 + } + + // Player-related configuration + player: { + new: { + // Start point (Renewal) + start_point_re: { + map: "iz_int" + x: 97 + y: 90 + } + // Start point (Pre-Renewal) + start_point_pre: { + map: "new_1-1" + x: 53 + y: 111 + } + + // Starting items for new characters + //{ + // id: Item id + // amount: Item amount + // loc: Item position, same as in item_db if you want the item to be equipped, otherwise 0 (optional) + // stackable: Is stackable? (not stackable item types: weapon, armor, egg, pet armor) + //}, + start_items: ( + { + id: 1201 // Knife + amount: 1 + loc: 2 + stackable: false + }, + { + id: 2301 // Cotton_Shirt + amount: 1 + loc: 16 + stackable: false + }, + ) + + // Starting zeny + zeny: 0 + } + + // Character name configuration + name: { + // Name used for unknown characters + unknown_char_name: "Unknown" + + // Allow or not identical name for characters but with a different case (upper/lower): + // example: Test-test-TEST-TesT; Value: 0 not allowed (default), 1 allowed + name_ignoring_case: false + + // Manage possible letters/symbol in the name of charater. Control character (0x00-0x1f) are never accepted. Possible values are: + // NOTE: Applies to character, party and guild names. + // 0: no restriction (default) + // 1: only letters/symbols in 'name_letters' option. + // 2: Letters/symbols in 'name_letters' option are forbidden. All others are possibles. + name_option: 1 + + // Set the letters/symbols that you want use with the 'char_name_option' option. + // Note: Don't add spaces unless you mean to add 'space' to the list. + name_letters: "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" + } + + deletion: { + // Restrict character deletion by BaseLevel + // 0: no restriction (players can delete characters of any level) + // -X: you can't delete chars with BaseLevel <= X + // Y: you can't delete chars with BaseLevel >= Y + // e.g. char_del_level: 80 (players can't delete characters with 80+ BaseLevel) + level: 0 + + // Amount of time in seconds by which the character deletion is delayed. + // Default: 86400 (24 hours) + // NOTE: Requires client 2010-08-03aragexeRE or newer. + delay: 86400 + + // Block deletion if character is inside a guild or a party? (BOOL) + // default: false official: true + // !!This check is imposed by Aegis to avoid dead entries in databases and _is_not_needed_ as we clear data properly!! + use_aegis_delete: false + } + + // Size for the fame-lists + fame: { + alchemist: 10 + blacksmith: 10 + taekwon: 10 + } + } + + database: { + // How often should server save all guild related information? (character save interval is defined on the map config) + // (in seconds) + autosave_time: 60 + + // What folder the DB files are in (abra_db.txt, etc.) + db_path: "db" + + // To log the character server? + log_char: true + } + + //================================================================== + // Pincode system + //================================================================== + pincode: { + // A window is opened before you can select your character and you will have to enter a pincode by using only your mouse + // NOTE: Requires client 2011-03-09aragexeRE or newer. + // 0: disabled + // 1: enabled + enabled: true + + // Request Pincode only on login or on everytime char select is accessed? + // 0: only on login (default) + // 1: everytime the char select window is accessed + request: 0 + + // How often does a user have to change his pincode? + // Default: 0 + // 0: never + // X: every X minutes + change_time: 0 + + // How often can a user enter the wrong password? + // Default: 3 + // Maximum allowed by clientside: 3 + max_tries: 3 + } + +} + +import: "conf/import/char-server.conf" diff --git a/conf/global/console.conf b/conf/global/console.conf new file mode 100644 index 000000000..d26c48352 --- /dev/null +++ b/conf/global/console.conf @@ -0,0 +1,59 @@ +//================= Hercules Configuration ================================ +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2014-2016 Hercules Dev Team +//= +//= Hercules 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 3 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 <http://www.gnu.org/licenses/>. +//========================================================================= +//= Server Console configuration file. +//========================================================================= +// This file affects how ALL server consoles work, unless explictly defined +// so in the server configuration file (See doc/global_configuration.txt +// for more information). +//========================================================================= + +console: { + //Time-stamp format which will be printed before all messages. + //Can at most be 20 characters long. + //Common formats: + // %I:%M:%S %p (hour:minute:second 12 hour, AM/PM format) + // %H:%M:%S (hour:minute:second, 24 hour format) + // %d/%b/%Y (day/Month/year) + //For full format information, consult the strftime() manual. + //timestamp_format: "[%d/%b %H:%M]" + + //If redirected output contains escape sequences (color codes) + stdout_with_ansisequence: false + + //Makes server output more silent by omitting certain types of messages: + //1: Hide Information messages + //2: Hide Status messages + //4: Hide Notice Messages + //8: Hide Warning Messages + //16: Hide Error and SQL Error messages. + //32: Hide Debug Messages + //Example: "console_silent: 7" Hides information, status and notice messages (1+2+4) + console_silent: 0 + + // [CHAR] Display information on the console whenever characters/guilds/parties/pets are loaded/saved? + save_log: true +} diff --git a/conf/import-tmpl/char-server.conf b/conf/import-tmpl/char-server.conf new file mode 100644 index 000000000..6bfb308b5 --- /dev/null +++ b/conf/import-tmpl/char-server.conf @@ -0,0 +1,32 @@ +//================= Hercules Configuration ================================ +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2014-2016 Hercules Dev Team +//= +//= Hercules 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 3 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 <http://www.gnu.org/licenses/>. +//========================================================================= +//= Character Server local configuration file. +//========================================================================= + +char_configuration: { + // See conf/char/char-server.conf for details +} diff --git a/conf/import-tmpl/char_conf.txt b/conf/import-tmpl/char_conf.txt deleted file mode 100644 index e69de29bb..000000000 --- a/conf/import-tmpl/char_conf.txt +++ /dev/null diff --git a/doc/global_configuration.txt b/doc/global_configuration.txt new file mode 100644 index 000000000..23be19031 --- /dev/null +++ b/doc/global_configuration.txt @@ -0,0 +1,69 @@ +//===== Hercules Documentation =============================== +//= Global configuration reference +//===== By: ================================================== +//= Panikon (Hercules Dev. Team) +//===== Current Version: ===================================== +//= 20140616 +//===== Description: ========================================= +//= Global configurations found in /conf/global/ +//============================================================ + +- What are global configurations? + +Global configurations are configurations that can be shared between servers, +but can also be set independently in each server. + +- How do they work? + +They work by using an include system that is available with libconfig: + + "A configuration file may "include" the contents of another file using an + include directive. This directive has the effect of inlining the contents of + the named file at the point of inclusion. + + An include directive must appear on its own line in the input. It has the + form: + + @include "filename" + + Any backslashes or double quotes in the filename must be escaped as '\\' and + '\"', respectively." + From libconfig's documentation + +So each file that is included is actually inside each one of the main +configuration files and thus a change in the first will be a change in the +latter. +Note: the @include directive is read by the server executable, so any path +should be from were it is and NOT from where the main configuration file is! + +- How do I stop using global configurations? + +To stop using global configurations is very simple, all you have to do is copy +the contents that are inside the global configuration file and put them +_exactly_ where the include directive were in the main configuration file. + +E.g. + Find in any file: + @include "conf/global/sql_connection.conf" + Replace it with: + sql_connection: { + // [INTER] You can specify the codepage to use in your mySQL tables here. + // (Note that this feature requires MySQL 4.1+) + //default_codepage: "" + + // [LOGIN] Is `userid` in account_db case sensitive? + //case_sensitive: false + + // For IPs, ideally under linux, you want to use localhost instead of 127.0.0.1 + // Under windows, you want to use 127.0.0.1. If you see a message like + // "Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)" + // and you have localhost, switch it to 127.0.0.1 + db_hostname: "127.0.0.1" + db_port: 3306 + db_username: "ragnarok" + db_password: "ragnarok" + db_database: "ragnarok" + //codepage:"" + } + If the main configuration file belongs to the map server, for instance, you + don't need to include default_codepage and case_sensitive. diff --git a/src/char/char.c b/src/char/char.c index 571aad566..2b5c837c5 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -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 @@ -136,7 +138,16 @@ 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] @@ -1537,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_); @@ -1609,24 +1620,20 @@ int char_make_new_char_sql(struct char_session_data *sd, const char *name_, int } //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); } } @@ -2176,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 { @@ -5441,206 +5448,504 @@ void char_sql_config_read(const char* cfgName) ShowInfo("Done reading %s.\n", cfgName); } -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 '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 (!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; + } } - if (i == len) - HPM->parseConf(w1, w2, HPCT_CHAR); + + libconfig->destroy(&config); + return retval; } -int char_config_read(const char* cfgName) +/** + * 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) { - 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, "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; - while(fgets(line, sizeof(line), fp)) { - if (line[0] == '/' && line[1] == '/') + 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) { - enable_char_creation = atoi(w2) ? true : false; - } 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) { - chr->show_save_log = config_switch(w2) ? true : false; +/** + * 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) { - chr->enable_logs = atoi(w2) ? true : false; - } 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) ? true : false; - } 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) { @@ -5680,6 +5985,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); @@ -5786,11 +6093,13 @@ 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"); + VECTOR_INIT(start_items); + for (i = 0; i < MAX_MAP_SERVERS; i++) VECTOR_INIT(chr->server[i].maps); @@ -5808,16 +6117,31 @@ 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); + { + // TODO: Remove this when no longer needed. + struct stat fileinfo; + if (stat("conf/import/char_conf.txt", &fileinfo) == 0 && fileinfo.st_size > 0) { + ShowWarning("An old configuration file \"conf/import/char_conf.txt\" was found.\n"); + ShowWarning("If it contains settings you wish to keep, please merge them into \"conf/import/char-server_local.conf\".\n"); + ShowWarning("Otherwise, just delete it.\n"); + ShowInfo("Resuming in 10 seconds...\n"); + HSleep(10); + } + } + #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 @@ -6101,6 +6425,17 @@ 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->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 b94226859..74478b747 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -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, @@ -276,8 +280,20 @@ struct char_interface { 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 (*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 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 diff --git a/src/common/mmo.h b/src/common/mmo.h index 0a5d9d053..77f706f0d 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -185,9 +185,6 @@ #ifndef MAX_QUEST_OBJECTIVES #define MAX_QUEST_OBJECTIVES 3 // Max quest objectives for a quest #endif -#ifndef MAX_START_ITEMS -#define MAX_START_ITEMS 32 // Max number of items allowed to be given to a char whenever it's created. [mkbu95] -#endif // for produce #define MIN_ATTRIBUTE 0 @@ -360,7 +357,7 @@ enum equip_pos { struct point { unsigned short map; - short x,y; + int16 x, y; }; enum e_skill_flag @@ -1068,6 +1065,7 @@ enum e_char_server_type { CST_OVER18 = 2, CST_PAYING = 3, CST_F2P = 4, + CST_MAX, }; enum e_pc_reg_loading { diff --git a/tools/configconverter.pl b/tools/configconverter.pl new file mode 100755 index 000000000..2849270de --- /dev/null +++ b/tools/configconverter.pl @@ -0,0 +1,298 @@ +#!/usr/bin/perl +# +# This file is part of Hercules. +# http://herc.ws - http://github.com/HerculesWS/Hercules +# +# Copyright (C) 2016 Hercules Dev Team +# Copyright (C) 2016 Haru +# +# Hercules 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 3 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 <http://www.gnu.org/licenses/>. + +use strict; +use warnings; + +my $silent = 0; +my $confpath = 'conf'; + +sub parse_config($$) { + my ($input, $defaults) = @_; + + my %output = (); + + for my $line (<$input>) { + chomp $line; + $line =~ s/^\s*//; $line =~ s/\s*$//; + if ($line =~ /^([a-z0-9A-Z_.]+)\s*:\s*(.*)$/) { + my ($variable, $value) = ($1, $2); + if ($defaults->{$variable}) { + next if $defaults->{$variable}->{parse}->($variable, $value, $defaults->{$variable}, \%output); + print "Error: Invalid value for setting '$variable: $value'\n"; + next; + } else { + print "Found unhandled configuration setting: '$variable: $value'\n"; + next; + } + } elsif ($line =~ m{^\s*(?://.*)?$}) { + next; + } else { + print "Error: Unable to parse line '$line'\n"; + } + } + return \%output; +} + +sub cfg_add($$$$) { + my ($variable, $value, $default, $output) = @_; + $output->{$variable} = {value => $value, print => $default->{print}, path => $default->{path}} unless $value eq $default->{default}; +} + +sub parsecfg_string($$$$) { + my ($variable, $value, $default, $output) = @_; + if ($value =~ m{\s*"((?:\\"|.)*)"\s*(?://.*)?$}i) { + cfg_add($variable, $1, $default, $output); + return 1; + } elsif ($value =~ m{\s*((?:\\"|.)*)\s*(?://.*)?$}i) { + cfg_add($variable, $1, $default, $output); + return 1; + } + return 0; +} + +sub parsecfg_int($$$$) { + my ($variable, $value, $default, $output) = @_; + if ($value =~ m{\s*(-?[0-9]+)\s*(?://.*)?$}) { + cfg_add($variable, int $1, $default, $output); + return 1; + } elsif ($value =~ m{\s*(0x[0-9A-F]+)\s*(?://.*)?$}) { + cfg_add($variable, hex $1, $default, $output); + return 1; + } elsif ($value =~ m{\s*(no|false|off)\s*(?://.*)?$}) { + cfg_add($variable, 0, $default, $output); + return 1; + } + return 0; +} + +sub parsecfg_bool($$$$) { + my ($variable, $value, $default, $output) = @_; + if ($value =~ m{\s*(yes|true|1|on)\s*(?://.*)?$}i) { + cfg_add($variable, "true", $default, $output); + return 1; + } elsif ($value =~ m{\s*(no|false|0|off)\s*(?://.*)?$}i) { + cfg_add($variable, "false", $default, $output); + return 1; + } + return 0; +} + +sub print_config($) { + my ($config) = @_; + + for my $variable (keys %$config) { + my $fullpath = $config->{$variable}->{path}; + $fullpath .= $variable if $fullpath =~ m{[:/]$}; + my ($filename, $configpath) = split(/:/, $fullpath, 2); + next unless $filename and $configpath; + my @path = split(/\//, $configpath); + next unless @path; + + my %output = (); + + my $setting = \%output; + while (scalar @path > 1) { + my $nodename = shift @path; + $setting->{$nodename} = {print => \&printcfg_tree, value => {}} unless $setting->{$nodename}; + $setting = $setting->{$nodename}->{value}; + } + $setting->{$path[0]} = {print => $config->{$variable}->{print}, value => $config->{$variable}->{value}}; + verbose("- Found setting: '$variable'.\n Please manually move the setting to '$filename.conf' as in the following example:\n", + "- '$filename.conf': (from $variable)\n"); + $output{$_}->{print}->($_, $output{$_}->{value}, 0) for keys %output; + } +} + +sub indent($$) { + my ($message, $nestlevel) = @_; + return print "\t" x ($nestlevel + 1) . $message; +} + +sub printcfg_tree($$$) { + my ($variable, $value, $nestlevel) = @_; + + indent("$variable: {\n", $nestlevel); + $value->{$_}{print}->($_, $value->{$_}{value}, $nestlevel + 1) for keys %$value; + indent("}\n", $nestlevel); +} + +sub printcfg_nil($$$) { +} + +sub printcfg_string($$$) { + my ($variable, $value, $nestlevel) = @_; + + indent("$variable: \"$value\"\n", $nestlevel); +} + +sub printcfg_int($$$) { + my ($variable, $value, $nestlevel) = @_; + + indent("$variable: $value\n", $nestlevel); +} + +sub printcfg_bool($$$) { + my ($variable, $value, $nestlevel) = @_; + + indent("$variable: $value\n", $nestlevel); +} + +sub printcfg_point($$$) { + my ($variable, $value, $nestlevel) = @_; + + indent("$variable: {\n", $nestlevel); + + my @point = split(/,/, $value, 3); + indent("map: \"$point[0]\"\n", $nestlevel + 1); + indent("x: $point[1]\n", $nestlevel + 1); + indent("y: $point[2]\n", $nestlevel + 1); + + indent("}\n", $nestlevel); +} + +sub printcfg_items($$$) { + my ($variable, $value, $nestlevel) = @_; + + indent("$variable: (\n", $nestlevel); + + my @items = split(/,/, $value); + while (scalar @items >= 3) { + my $id = shift @items; + my $amount = shift @items; + my $stackable = (shift @items) ? "true" : "false"; + indent("{\n", $nestlevel); + indent("id: $id\n", $nestlevel + 1); + indent("amount: $amount\n", $nestlevel + 1); + indent("stackable: $stackable\n", $nestlevel + 1); + indent("},\n", $nestlevel); + } + + indent(")\n", $nestlevel); +} + +sub process_conf($$) { + my ($files, $defaults) = @_; + my $found = 0; + for my $file (@$files) { + print "\nChecking $file..."; + print " Ok\n" and next unless open my $FH, '<', $file; # File not found or already converted + print " Old file is still present\n"; + my $output = parse_config($FH, $defaults); + close($FH); + my $count = scalar keys %$output; + print "$count non-default settings found."; + verbose(" The file '$file' is no longer used by Hercules and can be deleted.\n", "\n") and next unless $count; + verbose(" Please review and migrate the settings as described, then delete the file '$file', as it is no longer used by Hercules.\n", "\n"); + print_config($output); + $found = 1; + } + return $found; +} + +sub verbose($;$) { + my ($verbose_message, $silent_message) = @_; + return print $verbose_message unless $silent; + return print $silent_message if defined $silent_message; + return 1; +} + +my @defaults = ( + { + files => ['char-server.conf', 'import/char_conf.txt'], + settings => { + autosave_time => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/database/", default => 60}, + bind_ip => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/inter/", default => "127.0.0.1"}, + char_aegis_delete => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "char-server:char_configuration/player/deletion/use_aegis_delete", default => "false"}, + char_del_delay => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/player/deletion/delay", default => 86400}, + char_del_level => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/player/deletion/level", default => 0}, + char_ip => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/inter/", default => "127.0.0.1"}, + char_maintenance_min_group_id => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/permission/maintenance_min_group_id", default => 99}, + char_name_letters => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/player/name/name_letters", default => "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"}, + char_name_option => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/player/name/name_option", default => 1}, + char_new => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "char-server:char_configuration/permission/enable_char_creation", default => "true"}, + char_new_display => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "char-server:char_configuration/permission/display_new", default => "false"}, + char_port => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/inter/", default => 6121}, + char_server_type => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/permission/server_type", default => 0}, + console_silent => {parse => \&parsecfg_int, print => \&printcfg_int, path => "console:console/", default => 0}, + db_path => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/database/", default => "db"}, + fame_list_alchemist => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/fame/alchemist", default => 10}, + fame_list_blacksmith => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/fame/blacksmith", default => 10}, + fame_list_taekwon => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/fame/taekwon", default => 10}, + gm_allow_group => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/permission/", default => -1}, + guild_exp_rate => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/", default => 100}, + log_char => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "char-server:char_configuration/database/", default => "true"}, + login_ip => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/inter/", default => "127.0.0.1"}, + login_port => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/inter/", default => 6900}, + max_connect_user => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/permission/", default => -1}, + name_ignoring_case => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "char-server:char_configuration/player/name/", default => "false"}, + passwd => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/inter/", default => "p1"}, + pincode_changetime => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/pincode/change_time", default => 0}, + pincode_charselect => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/pincode/request", default => 0}, + pincode_enabled => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "char-server:char_configuration/pincode/enabled", default => "true"}, + pincode_maxtry => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/pincode/max_tries", default => 3}, + save_log => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "console:console/", default => "true"}, + server_name => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/", default => "Hercules"}, + start_items => {parse => \&parsecfg_string, print => \&printcfg_items, path => "char-server:char_configuration/player/new/", default => "1201,1,0,2301,1,0"}, + start_point_pre => {parse => \&parsecfg_string, print => \&printcfg_point, path => "char-server:char_configuration/player/new/", default => "new_1-1,53,111"}, + start_point_re => {parse => \&parsecfg_string, print => \&printcfg_point, path => "char-server:char_configuration/player/new/", default => "iz_int,97,90"}, + start_zeny => {parse => \&parsecfg_int, print => \&printcfg_int, path => "char-server:char_configuration/player/new/zeny", default => 0}, + stdout_with_ansisequence => {parse => \&parsecfg_bool, print => \&printcfg_bool, path => "console:console/", default => "false"}, + timestamp_format => {parse => \&parsecfg_string, print => \&printcfg_string, path => "console:console/", default => "[%d/%b %H:%M]"}, + unknown_char_name => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/player/name/", default => "Unknown"}, + userid => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/inter/", default => "s1"}, + wisp_server_name => {parse => \&parsecfg_string, print => \&printcfg_string, path => "char-server:char_configuration/", default => "Server"}, + import => {parse => \&parsecfg_string, print => \&printcfg_nil, path => "", default => "conf/import/char_conf.txt"}, + } + }, +); + +for (@ARGV) { + if (/^-q$/) { $silent = 1; } + elsif (/^-v$/) { $silent = 0; } + elsif (-d) { $confpath = $_; } + else { undef $confpath } +} + +verbose(<<'EOF'); +=============== Hercules Configuration Migration Helper =============== += _ _ _ = += | | | | | | = += | |_| | ___ _ __ ___ _ _| | ___ ___ = += | _ |/ _ \ '__/ __| | | | |/ _ \/ __| = += | | | | __/ | | (__| |_| | | __/\__ \ = += \_| |_/\___|_| \___|\__,_|_|\___||___/ = +======================================================================= +This tool will assist you through the migration of the old (txt-based) +configuration files to the new (libconfig-based) format. +Please follow the displayed instructions. +======================================================================= + +EOF + +die "Usage: ./$0 [-q | -v] [path to the conf directory]\nIf no options are passed, it acts as if called as ./$0 conf\n" unless defined $confpath and -d $confpath; + +my $count = 0; +for (@defaults) { + my @files = map { $confpath . '/' . $_ } @{$_->{files}}; + $count += process_conf(\@files, $_->{settings}) +} +verbose("\nThere are no files to migrate.\n") unless $count; |