Explorar o código

Merge pull request #70 from lighta/master

Merging Char_refact branch
lighta %!s(int64=11) %!d(string=hai) anos
pai
achega
6b9ec5fe0b

+ 8 - 0
src/char/CMakeLists.txt

@@ -12,6 +12,10 @@ if( BUILD_SQL_SERVERS )
 message( STATUS "Creating target char-server_sql" )
 set( SQL_CHAR_HEADERS
 	"${CMAKE_CURRENT_SOURCE_DIR}/char.h"
+	"${CMAKE_CURRENT_SOURCE_DIR}/char_clif.h"
+	"${CMAKE_CURRENT_SOURCE_DIR}/char_cnslif.h"
+	"${CMAKE_CURRENT_SOURCE_DIR}/char_logif.h"
+	"${CMAKE_CURRENT_SOURCE_DIR}/char_mapif.h"
 	"${CMAKE_CURRENT_SOURCE_DIR}/int_auction.h"
 	"${CMAKE_CURRENT_SOURCE_DIR}/int_elemental.h"
 	"${CMAKE_CURRENT_SOURCE_DIR}/int_guild.h"
@@ -26,6 +30,10 @@ set( SQL_CHAR_HEADERS
 	)
 set( SQL_CHAR_SOURCES
 	"${CMAKE_CURRENT_SOURCE_DIR}/char.c"
+	"${CMAKE_CURRENT_SOURCE_DIR}/char_clif.c"
+	"${CMAKE_CURRENT_SOURCE_DIR}/char_cnslif.c"
+	"${CMAKE_CURRENT_SOURCE_DIR}/char_logif.c"
+	"${CMAKE_CURRENT_SOURCE_DIR}/char_mapif.c"
 	"${CMAKE_CURRENT_SOURCE_DIR}/int_auction.c"
 	"${CMAKE_CURRENT_SOURCE_DIR}/int_elemental.c"
 	"${CMAKE_CURRENT_SOURCE_DIR}/int_guild.c"

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 143 - 353
src/char/char.c


+ 243 - 64
src/char/char.h

@@ -4,31 +4,24 @@
 #ifndef _CHAR_SQL_H_
 #define _CHAR_SQL_H_
 
+#define DB_NAME_LEN 256 //max len of dbs
+
 #include "../config/core.h"
 #include "../common/core.h" // CORE_ST_LAST
 #include "../common/msg_conf.h"
+#include "../common/mmo.h"
+
 
-enum E_CHARSERVER_ST
-{
+extern int login_fd; //login file descriptor
+extern int char_fd; //char file descriptor
+
+enum E_CHARSERVER_ST {
 	CHARSERVER_ST_RUNNING = CORE_ST_LAST,
 	CHARSERVER_ST_STARTING,
 	CHARSERVER_ST_SHUTDOWN,
 	CHARSERVER_ST_LAST
 };
 
-struct mmo_charstatus;
-
-#define MAX_MAP_SERVERS 30
-
-#define DEFAULT_AUTOSAVE_INTERVAL 300*1000
-
-#define msg_config_read(cfgName) char_msg_config_read(cfgName)
-#define msg_txt(msg_number) char_msg_txt(msg_number)
-#define do_final_msg() char_do_final_msg()
-int char_msg_config_read(char *cfgName);
-const char* char_msg_txt(int msg_number);
-void char_do_final_msg(void);
-
 enum {
 	TABLE_INVENTORY,
 	TABLE_CART,
@@ -36,11 +29,213 @@ enum {
 	TABLE_GUILD_STORAGE,
 };
 
-int memitemdata_to_sql(const struct item items[], int max, int id, int tableswitch);
+struct Schema_Config {
+	int db_use_sqldbs;
+	char db_path[1024];
+	char char_db[DB_NAME_LEN];
+	char scdata_db[DB_NAME_LEN];
+	char skillcooldown_db[DB_NAME_LEN];
+	char cart_db[DB_NAME_LEN];
+	char inventory_db[DB_NAME_LEN];
+	char charlog_db[DB_NAME_LEN];
+	char storage_db[DB_NAME_LEN];
+	char interlog_db[DB_NAME_LEN];
+	char reg_db[DB_NAME_LEN];
+	char skill_db[DB_NAME_LEN];
+	char memo_db[DB_NAME_LEN];
+	char guild_db[DB_NAME_LEN];
+	char guild_alliance_db[DB_NAME_LEN];
+	char guild_castle_db[DB_NAME_LEN];
+	char guild_expulsion_db[DB_NAME_LEN];
+	char guild_member_db[DB_NAME_LEN];
+	char guild_position_db[DB_NAME_LEN];
+	char guild_skill_db[DB_NAME_LEN];
+	char guild_storage_db[DB_NAME_LEN];
+	char party_db[DB_NAME_LEN];
+	char pet_db[DB_NAME_LEN];
+	char mail_db[DB_NAME_LEN]; // MAIL SYSTEM
+	char auction_db[DB_NAME_LEN]; // Auctions System
+	char friend_db[DB_NAME_LEN];
+	char hotkey_db[DB_NAME_LEN];
+	char quest_db[DB_NAME_LEN];
+	char homunculus_db[DB_NAME_LEN];
+	char skill_homunculus_db[DB_NAME_LEN];
+	char mercenary_db[DB_NAME_LEN];
+	char mercenary_owner_db[DB_NAME_LEN];
+	char ragsrvinfo_db[DB_NAME_LEN];
+	char elemental_db[DB_NAME_LEN];
+	char bonus_script_db[DB_NAME_LEN];
+};
+extern struct Schema_Config schema_config;
+
+// Pincode system
+enum pincode_state {
+	PINCODE_OK = 0,
+	PINCODE_ASK,
+	PINCODE_NOTSET,
+	PINCODE_EXPIRED,
+	PINCODE_NEW,
+	PINCODE_PASSED,
+	PINCODE_WRONG,
+	PINCODE_MAXSTATE
+};
+struct Pincode_Config {
+	bool pincode_enabled;
+	int pincode_changetime;
+	int pincode_maxtry;
+	bool pincode_force;
+};
+struct CharMove_Config {
+	bool char_move_enabled;
+	bool char_movetoused;
+	bool char_moves_unlimited;
+};
+struct Char_Config {
+	int char_per_account; //Maximum chars per account (default unlimited) [Sirius]
+	int char_del_level; //From which level u can delete character [Lupus]
+	int char_del_delay; //minimum delay before effectly do the deletion
+	bool name_ignoring_case; // Allow or not identical name for characters but with a different case by [Yor]
+	char unknown_char_name[NAME_LENGTH]; // Name to use when the requested name cannot be determined
+	char char_name_letters[1024]; // list of letters/symbols allowed (or not) in a character name. by [Yor]
+	int char_name_option; // Option to know which letters/symbols are authorised 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]
+	int char_del_option;	// Character deletion type, email = 1, birthdate = 2 (default)
+};
+
+#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]
+struct CharServ_Config {
+	char userid[24];
+	char passwd[24];
+	char server_name[20];
+	char wisp_server_name[NAME_LENGTH];
+	char login_ip_str[128];
+	uint32 login_ip;
+	uint16 login_port;
+	char char_ip_str[128];
+	uint32 char_ip;
+	char bind_ip_str[128];
+	uint32 bind_ip;
+	uint16 char_port;
+	int char_maintenance;
+	bool char_new;
+	int char_new_display;
+
+	struct CharMove_Config charmove_config;
+	struct Char_Config char_config;
+	struct Pincode_Config pincode_config;
+
+	int save_log; // show loading/saving messages
+	int log_char;	// loggin char or not [devil]
+	int log_inter;	// loggin inter or not [devil]
+	int char_check_db;	///cheking sql-table at begining ?
+
+	struct point start_point; // Initial position the player will spawn on server
+	int console;
+	int max_connect_user;
+	int gm_allow_group;
+	int autosave_interval;
+	int start_zeny;
+	int guild_exp_rate;
+};
+extern struct CharServ_Config charserv_config;
+
+#define MAX_MAP_SERVERS 30 //how many mapserver a char server can handle
+struct mmo_map_server {
+	int fd;
+	uint32 ip;
+	uint16 port;
+	int users;
+	unsigned short map[MAX_MAP_PER_SERVER];
+};
+extern struct mmo_map_server map_server[MAX_MAP_SERVERS];
+
+#define AUTH_TIMEOUT 30000
+struct auth_node {
+	int account_id;
+	int char_id;
+	uint32 login_id1;
+	uint32 login_id2;
+	uint32 ip;
+	int sex;
+	time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+	int group_id;
+	unsigned changing_mapservers : 1;
+	uint8 version;
+};
+DBMap* char_get_authdb(); // int account_id -> struct auth_node*
+
+struct online_char_data {
+	int account_id;
+	int char_id;
+	int fd;
+	int waiting_disconnect;
+	short server; // -2: unknown server, -1: not connected, 0+: id of server
+	bool pincode_success;
+};
+DBMap* char_get_onlinedb(); // int account_id -> struct online_char_data*
+
+struct char_session_data {
+	bool auth; // whether the session is authed or not
+	int account_id, login_id1, login_id2, sex;
+	int found_char[MAX_CHARS]; // ids of chars on this account
+	char email[40]; // e-mail (default: a@a.com) by [Yor]
+	time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+	int group_id; // permission
+	uint8 char_slots; // total number of characters that can be created
+	uint8 chars_vip;
+	uint8 chars_billing;
+	uint32 version;
+	uint8 clienttype;
+	char new_name[NAME_LENGTH];
+	char birthdate[10+1];  // YYYY-MM-DD
+	// Pincode system
+	char pincode[PINCODE_LENGTH+1];
+	uint32 pincode_seed;
+	time_t pincode_change;
+	uint16 pincode_try;
+	// Addon system
+	int bank_vault;
+	unsigned int char_moves[MAX_CHARS]; // character moves left
+	uint8 isvip;
+	time_t unban_time[MAX_CHARS];
+	int charblock_timer;
+};
+
+
+struct mmo_charstatus;
+DBMap* char_get_chardb(); // int char_id -> struct mmo_charstatus*
+
+//Custom limits for the fame lists. [Skotlex]
+extern int fame_list_size_chemist;
+extern int fame_list_size_smith;
+extern int fame_list_size_taekwon;
+// Char-server-side stored fame lists [DracoRPG]
+extern struct fame_list smith_fame_list[MAX_FAME_LIST];
+extern struct fame_list chemist_fame_list[MAX_FAME_LIST];
+extern struct fame_list taekwon_fame_list[MAX_FAME_LIST];
+
+#define DEFAULT_AUTOSAVE_INTERVAL 300*1000
+#define MAX_CHAR_BUF 144 //Max size (for WFIFOHEAD calls)
+
+int char_search_mapserver(unsigned short map, uint32 ip, uint16 port);
+int char_lan_subnetcheck(uint32 ip);
 
-int mapif_sendall(unsigned char *buf,unsigned int len);
-int mapif_sendallwos(int fd,unsigned char *buf,unsigned int len);
-int mapif_send(int fd,unsigned char *buf,unsigned int len);
+int char_count_users(void);
+DBData char_create_online_data(DBKey key, va_list args);
+int char_db_setoffline(DBKey key, DBData *data, va_list ap);
+void char_set_char_online(int map_id, int char_id, int account_id);
+void char_set_char_offline(int char_id, int account_id);
+void char_set_all_offline(int id);
+void char_disconnect_player(int account_id);
+int char_chardb_waiting_disconnect(int tid, unsigned int tick, int id, intptr_t data);
+
+int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p);
+int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p);
+int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything);
+int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf);
+int char_delete_char_sql(int char_id);
+int char_rename_char_sql(struct char_session_data *sd, int char_id);
+int char_divorce_char_sql(int partner_id1, int partner_id2);
+int char_memitemdata_to_sql(const struct item items[], int max, int id, int tableswitch);
 
 void disconnect_player(int account_id);
 
@@ -48,50 +243,34 @@ int char_married(int pl1,int pl2);
 int char_child(int parent_id, int child_id);
 int char_family(int pl1,int pl2,int pl3);
 
-int request_accreg2(int account_id, int char_id);
-int save_accreg2(unsigned char* buf, int len);
-
-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];
-extern char cart_db[256];
-extern char inventory_db[256];
-extern char charlog_db[256];
-extern char storage_db[256];
-extern char interlog_db[256];
-extern char reg_db[256];
-extern char skill_db[256];
-extern char memo_db[256];
-extern char guild_db[256];
-extern char guild_alliance_db[256];
-extern char guild_castle_db[256];
-extern char guild_expulsion_db[256];
-extern char guild_member_db[256];
-extern char guild_position_db[256];
-extern char guild_skill_db[256];
-extern char guild_storage_db[256];
-extern char party_db[256];
-extern char pet_db[256];
-extern char mail_db[256];
-extern char auction_db[256];
-extern char quest_db[256];
-extern char homunculus_db[256];
-extern char skill_homunculus_db[256];
-extern char mercenary_db[256];
-extern char mercenary_owner_db[256];
-extern char elemental_db[256];
-extern char ragsrvinfo_db[256];
-extern char bonus_script_db[256];
-extern char skillcooldown_db[256];
-
-extern int db_use_sqldbs; // added for sql item_db read for char server [Valaris]
-
-extern int guild_exp_rate;
-extern int log_inter;
+int char_request_accreg2(int account_id, int char_id);
+int char_save_accreg2(unsigned char* buf, int len);
+
+//extern bool char_gm_read;
+int char_loadName(int char_id, char* name);
+int char_check_char_name(char * name, char * esc_name);
+
+void char_pincode_decrypt( uint32 userSeed, char* pin );
+int char_pincode_compare( int fd, struct char_session_data* sd, char* pin );
+void char_auth_ok(int fd, struct char_session_data *sd);
+void char_set_charselect(int account_id);
+void char_read_fame_list(void);
+
+#if PACKETVER >= 20120307
+int char_make_new_char_sql(struct char_session_data* sd, char* name_, int slot, int hair_color, int hair_style);
+#else
+int char_make_new_char_sql(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style);
+#endif
+
+//For use in packets that depend on an sd being present [Skotlex]
+#define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL || !sd->auth) { RFIFOSKIP(fd,rest); return 0; } }
+
+#define msg_config_read(cfgName) char_msg_config_read(cfgName)
+#define msg_txt(msg_number) char_msg_txt(msg_number)
+#define do_final_msg() char_do_final_msg()
+int char_msg_config_read(char *cfgName);
+const char* char_msg_txt(int msg_number);
+void char_do_final_msg(void);
+
 
 #endif /* _CHAR_SQL_H_ */

+ 1172 - 0
src/char/char_clif.c

@@ -0,0 +1,1172 @@
+// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/mmo.h"
+#include "../common/socket.h"
+#include "../common/sql.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/mapindex.h"
+#include "../common/malloc.h"
+#include "../common/strlib.h"
+#include "../common/utils.h"
+#include "../common/timer.h"
+#include "inter.h"
+#include "char.h"
+#include "char_logif.h"
+#include "char_mapif.h"
+#include "char_clif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+//------------------------------------------------
+//Add On system
+//------------------------------------------------
+// reason
+// 0: success
+// 1: failed
+void chclif_moveCharSlotReply( int fd, struct char_session_data* sd, unsigned short index, short reason ){
+	WFIFOHEAD(fd,8);
+	WFIFOW(fd,0) = 0x8d5;
+	WFIFOW(fd,2) = 8;
+	WFIFOW(fd,4) = reason;
+	WFIFOW(fd,6) = sd->char_moves[index];
+	WFIFOSET(fd,8);
+}
+
+/*
+ * Client is requesting to move a charslot
+ */
+int chclif_parse_moveCharSlot( int fd, struct char_session_data* sd){
+	uint16 from, to;
+
+	if( RFIFOREST(fd) < 8 )
+		return 0;
+	from = RFIFOW(fd,2);
+	to = RFIFOW(fd,4);
+	//Cnt = RFIFOW(fd,6); //how many time we have left to change (client.. lol we don't trust him)
+	RFIFOSKIP(fd,8);
+
+	// Have we changed to often or is it disabled?
+	if( (charserv_config.charmove_config.char_move_enabled)==0
+	|| ( (charserv_config.charmove_config.char_moves_unlimited)==0 && sd->char_moves[from] <= 0 ) ){
+		chclif_moveCharSlotReply( fd, sd, from, 1 );
+		return 1;
+	}
+
+	// We dont even have a character on the chosen slot?
+	if( sd->found_char[from] <= 0 ){
+		chclif_moveCharSlotReply( fd, sd, from, 1 );
+		return 1;
+	}
+
+	if( sd->found_char[to] > 0 ){
+		// We want to move to a used position
+		if( charserv_config.charmove_config.char_movetoused ){ // TODO: check if the target is in deletion process
+			// Admin is friendly and uses triangle exchange
+			if( SQL_ERROR == Sql_QueryStr(sql_handle, "START TRANSACTION")
+				|| SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id` = '%d'",schema_config.char_db, to, sd->found_char[from] )
+				|| SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id` = '%d'", schema_config.char_db, from, sd->found_char[to] )
+				|| SQL_ERROR == Sql_QueryStr(sql_handle, "COMMIT")
+				){
+				chclif_moveCharSlotReply( fd, sd, from, 1 );
+				Sql_ShowDebug(sql_handle);
+				Sql_QueryStr(sql_handle,"ROLLBACK");
+				return 1;
+			}
+		}else{
+			// Admin doesnt allow us to
+			chclif_moveCharSlotReply( fd, sd, from, 1 );
+			return 1;
+		}
+	}else if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d'", schema_config.char_db, to, sd->found_char[from] ) ){
+		Sql_ShowDebug(sql_handle);
+		chclif_moveCharSlotReply( fd, sd, from, 1 );
+		return 1;
+	}
+
+	if( (charserv_config.charmove_config.char_moves_unlimited)==0 ){
+		sd->char_moves[from]--;
+		Sql_Query(sql_handle, "UPDATE `%s` SET `moves`='%d' WHERE `char_id`='%d'", schema_config.char_db, sd->char_moves[from], sd->found_char[from] );
+	}
+
+	// We successfully moved the char - time to notify the client
+	chclif_moveCharSlotReply( fd, sd, from, 0 );
+	chclif_mmo_char_send(fd, sd);
+	return 1;
+}
+
+/* pincode_sendstate transmist the pincode state to client
+ * S 08b9 <seed>.L <aid>.L <state>.W (HC_SECOND_PASSWD_LOGIN)
+ * state :
+ *   0 = disabled / pin is correct
+ *   1 = ask for pin - client sends 0x8b8
+ *   2 = create new pin - client sends 0x8ba
+ *   3 = pin must be changed - client 0x8be
+ *   4 = create new pin - client sends 0x8ba
+ *   5 = client shows msgstr(1896)
+ *   6 = client shows msgstr(1897) Unable to use your KSSN number
+ *   7 = char select window shows a button - client sends 0x8c5
+ *   8 = pincode was incorrect
+*/
+void chclif_pincode_sendstate( int fd, struct char_session_data* sd, enum pincode_state state ){
+	WFIFOHEAD(fd, 12);
+	WFIFOW(fd, 0) = 0x8b9;
+	WFIFOL(fd, 2) = sd->pincode_seed = rnd() % 0xFFFF;
+	WFIFOL(fd, 6) = sd->account_id;
+	WFIFOW(fd,10) = state;
+	WFIFOSET(fd,12);
+}
+
+/*
+ * Client just entering charserv from login, send him pincode confirmation
+ */
+int chclif_parse_reqpincode_window(int fd, struct char_session_data* sd){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	if( charserv_config.pincode_config.pincode_enabled && RFIFOL(fd,2) == sd->account_id ){
+		if( strlen( sd->pincode ) <= 0 ){
+			chclif_pincode_sendstate( fd, sd, PINCODE_NEW );
+		}else{
+			chclif_pincode_sendstate( fd, sd, PINCODE_ASK );
+		}
+	}
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+/*
+ * Client as anwsered pincode questionning, checking if valid anwser
+ */
+int chclif_parse_pincode_check( int fd, struct char_session_data* sd ){
+	char pin[PINCODE_LENGTH+1];
+
+	if( RFIFOREST(fd) < 10 )
+		return 0;
+	if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id )
+		return 1;
+
+	memset(pin,0,PINCODE_LENGTH+1);
+	strncpy((char*)pin, (char*)RFIFOP(fd, 6), PINCODE_LENGTH);
+	RFIFOSKIP(fd,10);
+
+	char_pincode_decrypt(sd->pincode_seed, pin );
+	if( char_pincode_compare( fd, sd, pin ) ){
+		chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
+	}
+	return 1;
+}
+
+/*
+ * Client request to change pincode
+ */
+int chclif_parse_pincode_change( int fd, struct char_session_data* sd ){
+	if( RFIFOREST(fd) < 14 )
+		return 0;
+	if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id )
+		return 1;
+        else {
+            char oldpin[PINCODE_LENGTH+1];
+            char newpin[PINCODE_LENGTH+1];
+        
+            memset(oldpin,0,PINCODE_LENGTH+1);
+            memset(newpin,0,PINCODE_LENGTH+1);
+            strncpy(oldpin, (char*)RFIFOP(fd,6), PINCODE_LENGTH);
+            strncpy(newpin, (char*)RFIFOP(fd,10), PINCODE_LENGTH);
+            RFIFOSKIP(fd,14);
+
+            char_pincode_decrypt(sd->pincode_seed,oldpin);
+            if( !char_pincode_compare( fd, sd, oldpin ) )
+                    return 1;
+            char_pincode_decrypt(sd->pincode_seed,newpin);
+
+            chlogif_pincode_notifyLoginPinUpdate( sd->account_id, newpin );
+            strncpy(sd->pincode, newpin, sizeof(newpin));
+
+            chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
+        }
+	return 1;
+}
+
+/*
+ * activate PIN system and set first PIN
+ */
+int chclif_parse_pincode_setnew( int fd, struct char_session_data* sd ){
+	if( RFIFOREST(fd) < 10 )
+		return 0;
+
+	if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id )
+		return 1;
+        else {
+            char newpin[PINCODE_LENGTH+1];
+            memset(newpin,0,PINCODE_LENGTH+1);
+            strncpy( newpin, (char*)RFIFOP(fd,6), PINCODE_LENGTH );
+            RFIFOSKIP(fd,10);
+
+            char_pincode_decrypt( sd->pincode_seed, newpin );
+
+            chlogif_pincode_notifyLoginPinUpdate( sd->account_id, newpin );
+            strncpy( sd->pincode, newpin, strlen( newpin ) );
+
+            chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
+        }
+	return 1;
+}
+
+
+//----------------------------------------
+// Tell client how many pages, kRO sends 17 (Yommy)
+//----------------------------------------
+void chclif_charlist_notify( int fd, struct char_session_data* sd ){
+	WFIFOHEAD(fd, 6);
+	WFIFOW(fd, 0) = 0x9a0;
+	// pages to req / send them all in 1 until mmo_chars_fromsql can split them up
+	WFIFOL(fd, 2) = (sd->char_slots>3)?sd->char_slots/3:1; //int TotalCnt (nb page to load)
+	WFIFOSET(fd,6);
+}
+
+//----------------------------------------
+// Function to send characters to a player
+//----------------------------------------
+int chclif_mmo_send006b(int fd, struct char_session_data* sd){
+	int j, offset = 0;
+	bool newvers = (sd->version >= date2version(20100413) );
+	if(newvers) //20100413
+		offset += 3;
+	if (charserv_config.save_log)
+		ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id);
+
+	j = 24 + offset; // offset
+	WFIFOHEAD(fd,j + MAX_CHARS*MAX_CHAR_BUF);
+	WFIFOW(fd,0) = 0x6b;
+	if(newvers){ //20100413
+		WFIFOB(fd,4) = MAX_CHARS; // Max slots.
+		WFIFOB(fd,5) = sd->char_slots; // Available slots. (PremiumStartSlot)
+		WFIFOB(fd,6) = MAX_CHARS; // Premium slots. (Any existent chars past sd->char_slots but within MAX_CHARS will show a 'Premium Service' in red)
+	}
+	memset(WFIFOP(fd,4 + offset), 0, 20); // unknown bytes
+	j+=char_mmo_chars_fromsql(sd, WFIFOP(fd,j));
+	WFIFOW(fd,2) = j; // packet len
+	WFIFOSET(fd,j);
+
+	return 0;
+}
+
+//----------------------------------------
+// Notify client about charselect window data [Ind]
+//----------------------------------------
+void chclif_mmo_send082d(int fd, struct char_session_data* sd) {
+	if (charserv_config.save_log)
+		ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id);
+	WFIFOHEAD(fd,29);
+	WFIFOW(fd,0) = 0x82d;
+	WFIFOW(fd,2) = 29;
+	WFIFOB(fd,4) = sd->char_slots;
+	WFIFOB(fd,5) = MAX_CHARS - sd->char_slots;
+	WFIFOB(fd,6) = MAX_CHARS - sd->char_slots;
+	WFIFOB(fd,7) = sd->char_slots;
+	WFIFOB(fd,8) = sd->char_slots;
+	memset(WFIFOP(fd,9), 0, 20); // unused bytes
+	WFIFOSET(fd,29);
+}
+
+void chclif_mmo_send099d(int fd, struct char_session_data *sd) {
+	WFIFOHEAD(fd,4 + (MAX_CHARS*MAX_CHAR_BUF));
+	WFIFOW(fd,0) = 0x99d;
+	WFIFOW(fd,2) = char_mmo_chars_fromsql(sd, WFIFOP(fd,4)) + 4;
+	WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/*
+ * Function to choose wich kind of charlist to send to client depending on his version
+ */
+void chclif_mmo_char_send(int fd, struct char_session_data* sd){
+	ShowInfo("sd->version = %d\n",sd->version);
+	if(sd->version >= date2version(20130000) ){
+		chclif_mmo_send082d(fd,sd);
+		chclif_charlist_notify(fd,sd);
+		chclif_block_character(fd,sd);
+	}
+	//@FIXME dump from kro doesn't show 6b transmission
+	chclif_mmo_send006b(fd,sd);
+}
+
+/*
+ * Transmit auth result to client
+ * <result>.B ()
+ * result :
+ *  1 : Server closed
+ *  2 : Someone has already logged in with this id
+ *  8 : already online
+ */
+void chclif_send_auth_result(int fd,char result){
+	WFIFOHEAD(fd,3);
+	WFIFOW(fd,0) = 0x81;
+	WFIFOB(fd,2) = result;
+	WFIFOSET(fd,3);
+}
+
+/// @param result
+/// 0 (0x718): An unknown error has occurred.
+/// 1: none/success
+/// 3 (0x719): A database error occurred.
+/// 4 (0x71a): To delete a character you must withdraw from the guild.
+/// 5 (0x71b): To delete a character you must withdraw from the party.
+/// Any (0x718): An unknown error has occurred.
+/// HC: <0828>.W <char id>.L <Msg:0-5>.L <deleteDate>.L
+void chclif_char_delete2_ack(int fd, int char_id, uint32 result, time_t delete_date) {
+	WFIFOHEAD(fd,14);
+	WFIFOW(fd,0) = 0x828;
+	WFIFOL(fd,2) = char_id;
+	WFIFOL(fd,6) = result;
+	WFIFOL(fd,10) = TOL(delete_date);
+	WFIFOSET(fd,14);
+}
+
+/// @param result
+/// 0 (0x718): An unknown error has occurred.
+/// 1: none/success
+/// 2 (0x71c): Due to system settings can not be deleted.
+/// 3 (0x719): A database error occurred.
+/// 4 (0x71d): Deleting not yet possible time.
+/// 5 (0x71e): Date of birth do not match.
+/// Any (0x718): An unknown error has occurred.
+/// HC: <082a>.W <char id>.L <Msg:0-5>.L
+void chclif_char_delete2_accept_ack(int fd, int char_id, uint32 result) {
+	WFIFOHEAD(fd,10);
+	WFIFOW(fd,0) = 0x82a;
+	WFIFOL(fd,2) = char_id;
+	WFIFOL(fd,6) = result;
+	WFIFOSET(fd,10);
+}
+
+/// @param result
+/// 1 (0x718): none/success, (if char id not in deletion process): An unknown error has occurred.
+/// 2 (0x719): A database error occurred.
+/// Any (0x718): An unknown error has occurred.
+/// HC: <082c>.W <char id>.L <Msg:1-2>.L
+void chclif_char_delete2_cancel_ack(int fd, int char_id, uint32 result) {
+	WFIFOHEAD(fd,10);
+	WFIFOW(fd,0) = 0x82c;
+	WFIFOL(fd,2) = char_id;
+	WFIFOL(fd,6) = result;
+	WFIFOSET(fd,10);
+}
+
+// CH: <0827>.W <char id>.L
+int chclif_parse_char_delete2_req(int fd, struct char_session_data* sd) {
+    FIFOSD_CHECK(6)
+    {
+        int char_id, i;
+		char* data;
+		time_t delete_date;
+
+		char_id = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+
+		ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id );
+		if( i == MAX_CHARS )
+		{// character not found
+			chclif_char_delete2_ack(fd, char_id, 3, 0);
+			return 1;
+		}
+
+		if( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `delete_date` FROM `%s` WHERE `char_id`='%d'", schema_config.char_db, char_id) || SQL_SUCCESS != Sql_NextRow(sql_handle) )
+		{
+			Sql_ShowDebug(sql_handle);
+			chclif_char_delete2_ack(fd, char_id, 3, 0);
+			return 1;
+		}
+
+		Sql_GetData(sql_handle, 0, &data, NULL); delete_date = strtoul(data, NULL, 10);
+
+		if( delete_date ) {// character already queued for deletion
+			chclif_char_delete2_ack(fd, char_id, 0, 0);
+			return 1;
+		}
+
+	/*
+		// Aegis imposes these checks probably to avoid dead member
+		// entries in guilds/parties, otherwise they are not required.
+		// TODO: Figure out how these are enforced during waiting.
+		if( guild_id )
+		{// character in guild
+			chclif_char_delete2_ack(fd, char_id, 4, 0);
+			return 1;
+		}
+
+		if( party_id )
+		{// character in party
+			chclif_char_delete2_ack(fd, char_id, 5, 0);
+			return 1;
+		}
+	*/
+		// success
+		delete_date = time(NULL)+(charserv_config.char_config.char_del_delay);
+
+		if( SQL_SUCCESS != Sql_Query(sql_handle, "UPDATE `%s` SET `delete_date`='%lu' WHERE `char_id`='%d'", schema_config.char_db, (unsigned long)delete_date, char_id) )
+		{
+			Sql_ShowDebug(sql_handle);
+			chclif_char_delete2_ack(fd, char_id, 3, 0);
+			return 1;
+		}
+
+		chclif_char_delete2_ack(fd, char_id, 1, delete_date);
+    }
+    return 1;
+}
+
+// CH: <0829>.W <char id>.L <birth date:YYMMDD>.6B
+int chclif_parse_char_delete2_accept(int fd, struct char_session_data* sd) {
+    FIFOSD_CHECK(12)
+    {
+        char birthdate[8+1];
+		int char_id, i, k;
+		unsigned int base_level;
+		char* data;
+		time_t delete_date;
+		char_id = RFIFOL(fd,2);
+
+		ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, char_id);
+
+		// construct "YY-MM-DD"
+		birthdate[0] = RFIFOB(fd,6);
+		birthdate[1] = RFIFOB(fd,7);
+		birthdate[2] = '-';
+		birthdate[3] = RFIFOB(fd,8);
+		birthdate[4] = RFIFOB(fd,9);
+		birthdate[5] = '-';
+		birthdate[6] = RFIFOB(fd,10);
+		birthdate[7] = RFIFOB(fd,11);
+		birthdate[8] = 0;
+		RFIFOSKIP(fd,12);
+
+		ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id );
+		if( i == MAX_CHARS )
+		{// character not found
+			chclif_char_delete2_accept_ack(fd, char_id, 3);
+			return 1;
+		}
+
+		if( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `base_level`,`delete_date` FROM `%s` WHERE `char_id`='%d'", schema_config.char_db, char_id) || SQL_SUCCESS != Sql_NextRow(sql_handle) )
+		{// data error
+			Sql_ShowDebug(sql_handle);
+			chclif_char_delete2_accept_ack(fd, char_id, 3);
+			return 1;
+		}
+
+		Sql_GetData(sql_handle, 0, &data, NULL); base_level = (unsigned int)strtoul(data, NULL, 10);
+		Sql_GetData(sql_handle, 1, &data, NULL); delete_date = strtoul(data, NULL, 10);
+
+		if( !delete_date || delete_date>time(NULL) )
+		{// not queued or delay not yet passed
+			chclif_char_delete2_accept_ack(fd, char_id, 4);
+			return 1;
+		}
+
+		if( strcmp(sd->birthdate+2, birthdate) )  // +2 to cut off the century
+		{// birth date is wrong
+			chclif_char_delete2_accept_ack(fd, char_id, 5);
+			return 1;
+		}
+
+		if( ( charserv_config.char_config.char_del_level > 0 && base_level >= (unsigned int)charserv_config.char_config.char_del_level )
+		|| ( charserv_config.char_config.char_del_level < 0 && base_level <= (unsigned int)(-charserv_config.char_config.char_del_level) ) )
+		{// character level config restriction
+			chclif_char_delete2_accept_ack(fd, char_id, 2);
+			return 1;
+		}
+
+		// success
+		if( char_delete_char_sql(char_id) < 0 ){
+			chclif_char_delete2_accept_ack(fd, char_id, 3);
+			return 1;
+		}
+
+		// refresh character list cache
+		for(k = i; k < MAX_CHARS-1; k++){
+			sd->found_char[k] = sd->found_char[k+1];
+		}
+		sd->found_char[MAX_CHARS-1] = -1;
+
+		chclif_char_delete2_accept_ack(fd, char_id, 1);
+    }
+    return 1;
+}
+
+// CH: <082b>.W <char id>.L
+int chclif_parse_char_delete2_cancel(int fd, struct char_session_data* sd) {
+	int char_id, i;
+
+	FIFOSD_CHECK(6)
+
+	char_id = RFIFOL(fd,2);
+	RFIFOSKIP(fd,6);
+
+	ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id );
+	if( i == MAX_CHARS )
+	{// character not found
+		chclif_char_delete2_cancel_ack(fd, char_id, 2);
+		return 1;
+	}
+
+	// there is no need to check, whether or not the character was
+	// queued for deletion, as the client prints an error message by
+	// itself, if it was not the case (@see char_delete2_cancel_ack)
+	if( SQL_SUCCESS != Sql_Query(sql_handle, "UPDATE `%s` SET `delete_date`='0' WHERE `char_id`='%d'", schema_config.char_db, char_id) )
+	{
+		Sql_ShowDebug(sql_handle);
+		chclif_char_delete2_cancel_ack(fd, char_id, 2);
+		return 1;
+	}
+
+	chclif_char_delete2_cancel_ack(fd, char_id, 1);
+	return 1;
+}
+
+/*
+ * Register a new mapserver into that char-serv
+ * charserv can handle a MAX_SERVERS mapservs
+ */
+int chclif_parse_maplogin(int fd){
+	int i;
+
+	if (RFIFOREST(fd) < 60)
+		return 0;
+	else {
+		char* l_user = (char*)RFIFOP(fd,2);
+		char* l_pass = (char*)RFIFOP(fd,26);
+		l_user[23] = '\0';
+		l_pass[23] = '\0';
+		ARR_FIND( 0, ARRAYLENGTH(map_server), i, map_server[i].fd <= 0 );
+		if( runflag != CHARSERVER_ST_RUNNING ||
+			i == ARRAYLENGTH(map_server) ||
+			strcmp(l_user, charserv_config.userid) != 0 ||
+			strcmp(l_pass, charserv_config.passwd) != 0 )
+		{
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x2af9;
+			WFIFOB(fd,2) = 3;
+			WFIFOSET(fd,3);
+		} else {
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x2af9;
+			WFIFOB(fd,2) = 0;
+			WFIFOSET(fd,3);
+
+			map_server[i].fd = fd;
+			map_server[i].ip = ntohl(RFIFOL(fd,54));
+			map_server[i].port = ntohs(RFIFOW(fd,58));
+			map_server[i].users = 0;
+			memset(map_server[i].map, 0, sizeof(map_server[i].map));
+			session[fd]->func_parse = chmapif_parse;
+			session[fd]->flag.server = 1;
+			realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
+			chmapif_init(fd);
+		}
+		RFIFOSKIP(fd,60);
+	}
+	return 0;
+}
+
+// 0065 <account id>.L <login id1>.L <login id2>.L <???>.W <sex>.B
+int chclif_parse_reqtoconnect(int fd, struct char_session_data* sd,uint32 ipl){
+	if( RFIFOREST(fd) < 17 ) // request to connect
+		return 0;
+	else {
+		struct auth_node* node;
+		DBMap *auth_db = char_get_authdb();
+
+		int account_id = RFIFOL(fd,2);
+		uint32 login_id1 = RFIFOL(fd,6);
+		uint32 login_id2 = RFIFOL(fd,10);
+		int sex = RFIFOB(fd,16);
+		RFIFOSKIP(fd,17);
+
+		ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", account_id, login_id1, login_id2);
+
+		if (sd) {
+			//Received again auth packet for already authentified account?? Discard it.
+			//TODO: Perhaps log this as a hack attempt?
+			//TODO: and perhaps send back a reply?
+			ShowInfo("Already registered break\n");
+			return 1;
+		}
+
+		CREATE(session[fd]->session_data, struct char_session_data, 1);
+		sd = (struct char_session_data*)session[fd]->session_data;
+		sd->account_id = account_id;
+		sd->login_id1 = login_id1;
+		sd->login_id2 = login_id2;
+		sd->sex = sex;
+		sd->auth = false; // not authed yet
+
+		// send back account_id
+		WFIFOHEAD(fd,4);
+		WFIFOL(fd,0) = account_id;
+		WFIFOSET(fd,4);
+
+		if( runflag != CHARSERVER_ST_RUNNING )
+		{
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x6c;
+			WFIFOB(fd,2) = 0;// rejected from server
+			WFIFOSET(fd,3);
+			return 1;
+		}
+
+		// search authentification
+		node = (struct auth_node*)idb_get(auth_db, account_id);
+		if( node != NULL &&
+		    node->account_id == account_id &&
+			node->login_id1  == login_id1 &&
+			node->login_id2  == login_id2 /*&&
+			node->ip         == ipl*/ )
+		{// authentication found (coming from map server)
+                        sd->version = node->version;
+                        idb_remove(auth_db, account_id);
+			char_auth_ok(fd, sd);
+		}
+		else
+		{// authentication not found (coming from login server)
+			if (login_fd > 0) { // don't send request if no login-server
+				WFIFOHEAD(login_fd,23);
+				WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account
+				WFIFOL(login_fd,2) = sd->account_id;
+				WFIFOL(login_fd,6) = sd->login_id1;
+				WFIFOL(login_fd,10) = sd->login_id2;
+				WFIFOB(login_fd,14) = sd->sex;
+				WFIFOL(login_fd,15) = htonl(ipl);
+				WFIFOL(login_fd,19) = fd;
+				WFIFOSET(login_fd,23);
+			} else { // if no login-server, we must refuse connection
+				WFIFOHEAD(fd,3);
+				WFIFOW(fd,0) = 0x6c;
+				WFIFOB(fd,2) = 0;
+				WFIFOSET(fd,3);
+			}
+		}
+	}
+	return 1;
+}
+
+//struct PACKET_CH_CHARLIST_REQ { 0x0 short PacketType}
+int chclif_parse_req_charlist(int fd, struct char_session_data* sd){
+	if( RFIFOREST(fd) < 2 )
+		return 0;
+	RFIFOSKIP(fd,2);
+	chclif_mmo_send099d(fd,sd);
+	return 1;
+}
+
+int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl){
+	FIFOSD_CHECK(3);
+	{
+		struct mmo_charstatus char_dat;
+		struct mmo_charstatus *cd;
+		char* data;
+		int char_id;
+		uint32 subnet_map_ip;
+		struct auth_node* node;
+		int i, map_fd;
+		DBMap *auth_db = char_get_authdb();
+		DBMap *char_db_ = char_get_chardb();
+
+		int slot = RFIFOB(fd,2);
+		RFIFOSKIP(fd,3);
+
+		if ( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'", schema_config.char_db, sd->account_id, slot)
+		  || SQL_SUCCESS != Sql_NextRow(sql_handle)
+		  || SQL_SUCCESS != Sql_GetData(sql_handle, 0, &data, NULL) )
+		{	//Not found?? May be forged packet.
+			Sql_ShowDebug(sql_handle);
+			Sql_FreeResult(sql_handle);
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x6c;
+			WFIFOB(fd,2) = 0; // rejected from server
+			WFIFOSET(fd,3);
+			return 1;
+		}
+
+		char_id = atoi(data);
+		Sql_FreeResult(sql_handle);
+
+		/* client doesn't let it get to this point if you're banned, so its a forged packet */
+		if( sd->found_char[slot] == char_id && sd->unban_time[slot] > time(NULL) ) {
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x6c;
+			WFIFOB(fd,2) = 0; // rejected from server
+			WFIFOSET(fd,3);
+			return 1;
+		}
+                
+		/* set char as online prior to loading its data so 3rd party applications will realise the sql data is not reliable */
+		char_set_char_online(-2,char_id,sd->account_id);
+		if( !char_mmo_char_fromsql(char_id, &char_dat, true) ) { /* failed? set it back offline */
+			char_set_char_offline(char_id, sd->account_id);
+			/* failed to load something. REJECT! */
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x6c;
+			WFIFOB(fd,2) = 0;
+			WFIFOSET(fd,3);
+			return 1;/* jump off this boat */
+		}
+
+		//Have to switch over to the DB instance otherwise data won't propagate [Kevin]
+		cd = (struct mmo_charstatus *)idb_get(char_db_, char_id);
+		cd->sex = sd->sex;
+
+		if (charserv_config.log_char) {
+			char esc_name[NAME_LENGTH*2+1];
+
+			Sql_EscapeStringLen(sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH));
+			if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')",
+				schema_config.charlog_db, sd->account_id, slot, esc_name) )
+				Sql_ShowDebug(sql_handle);
+		}
+		ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name);
+
+		// searching map server
+		i = char_search_mapserver(cd->last_point.map, -1, -1);
+
+		// if map is not found, we check major cities
+		if (i < 0 || !cd->last_point.map) {
+			unsigned short j;
+			//First check that there's actually a map server online.
+			ARR_FIND( 0, ARRAYLENGTH(map_server), j, map_server[j].fd >= 0 && map_server[j].map[0] );
+			if (j == ARRAYLENGTH(map_server)) {
+				ShowInfo("Connection Closed. No map servers available.\n");
+				chclif_send_auth_result(fd,1); // 01 = Server closed
+				return 1;
+			}
+			if ((i = char_search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) {
+				cd->last_point.x = 273;
+				cd->last_point.y = 354;
+			} else if ((i = char_search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) {
+				cd->last_point.x = 120;
+				cd->last_point.y = 100;
+			} else if ((i = char_search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) {
+				cd->last_point.x = 160;
+				cd->last_point.y = 94;
+			} else if ((i = char_search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) {
+				cd->last_point.x = 116;
+				cd->last_point.y = 57;
+			} else if ((i = char_search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) {
+				cd->last_point.x = 87;
+				cd->last_point.y = 117;
+			} else if ((i = char_search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) {
+				cd->last_point.x = 94;
+				cd->last_point.y = 103;
+			} else {
+				ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(cd->last_point.map));
+				chclif_send_auth_result(fd,1); // 01 = Server closed
+				return 1;
+			}
+			ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(cd->last_point.map), mapindex_id2name(j));
+			cd->last_point.map = j;
+		}
+
+		//Send NEW auth packet [Kevin]
+		//FIXME: is this case even possible? [ultramage]
+		if ((map_fd = map_server[i].fd) < 1 || session[map_fd] == NULL)
+		{
+			ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i);
+			map_server[i].fd = -1;
+			memset(&map_server[i], 0, sizeof(struct mmo_map_server));
+			chclif_send_auth_result(fd,1);  //Send server closed.
+			return 1;
+		}
+
+		//Send player to map
+		WFIFOHEAD(fd,28);
+		WFIFOW(fd,0) = 0x71;
+		WFIFOL(fd,2) = cd->char_id;
+		mapindex_getmapname_ext(mapindex_id2name(cd->last_point.map), (char*)WFIFOP(fd,6));
+		subnet_map_ip = char_lan_subnetcheck(ipl); // Advanced subnet check [LuzZza]
+		WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : map_server[i].ip);
+		WFIFOW(fd,26) = ntows(htons(map_server[i].port)); // [!] LE byte order here [!]
+		WFIFOSET(fd,28);
+
+		// create temporary auth entry
+		CREATE(node, struct auth_node, 1);
+		node->account_id = sd->account_id;
+		node->char_id = cd->char_id;
+		node->login_id1 = sd->login_id1;
+		node->login_id2 = sd->login_id2;
+		node->sex = sd->sex;
+		node->expiration_time = sd->expiration_time;
+		node->group_id = sd->group_id;
+		node->ip = ipl;
+		idb_put(auth_db, sd->account_id, node);
+
+	}
+	return 1;
+}
+
+// S 0970 <name>.24B <slot>.B <hair color>.W <hair style>.W
+// S 0067 <name>.24B <str>.B <agi>.B <vit>.B <int>.B <dex>.B <luk>.B <slot>.B <hair color>.W <hair style>.W
+int chclif_parse_createnewchar(int fd, struct char_session_data* sd,int cmd){
+	int i=0, ch;
+
+	if (cmd == 0x970) FIFOSD_CHECK(31) //>=20120307
+	else if (cmd == 0x67) FIFOSD_CHECK(37)
+	else return 0;
+
+	if( (charserv_config.char_new)==0 ) //turn character creation on/off [Kevin]
+		i = -2;
+	else {
+		if (cmd == 0x970){
+			i = char_make_new_char_sql(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOW(fd,27),RFIFOW(fd,29));
+			RFIFOSKIP(fd,31);
+		}
+		else if(cmd == 0x67){
+#if PACKETVER < 20120307
+			i = char_make_new_char_sql(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOB(fd,27),RFIFOB(fd,28),RFIFOB(fd,29),RFIFOB(fd,30),RFIFOB(fd,31),RFIFOB(fd,32),RFIFOW(fd,33),RFIFOW(fd,35));
+			RFIFOSKIP(fd,37);
+#endif
+		}
+	}
+
+	//'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3)
+	if (i < 0) {
+		WFIFOHEAD(fd,3);
+		WFIFOW(fd,0) = 0x6e;
+		/* Others I found [Ind] */
+		/* 0x02 = Symbols in Character Names are forbidden */
+		/* 0x03 = You are not elegible to open the Character Slot. */
+		switch (i) {
+			case -1: WFIFOB(fd,2) = 0x00; break;
+			case -2: WFIFOB(fd,2) = 0xFF; break;
+			case -3: WFIFOB(fd,2) = 0x01; break;
+			case -4: WFIFOB(fd,2) = 0x03; break;
+		}
+		WFIFOSET(fd,3);
+	} else {
+		int len;
+		// retrieve data
+		struct mmo_charstatus char_dat;
+		char_mmo_char_fromsql(i, &char_dat, false); //Only the short data is needed.
+
+		// send to player
+		WFIFOHEAD(fd,2+MAX_CHAR_BUF);
+		WFIFOW(fd,0) = 0x6d;
+		len = 2 + char_mmo_char_tobuf(WFIFOP(fd,2), &char_dat);
+		WFIFOSET(fd,len);
+
+		// add new entry to the chars list
+		ARR_FIND( 0, MAX_CHARS, ch, sd->found_char[ch] == -1 );
+		if( ch < MAX_CHARS )
+			sd->found_char[ch] = i; // the char_id of the new char
+	}
+	return 1;
+}
+
+int chclif_parse_delchar(int fd,struct char_session_data* sd, int cmd){
+    if (cmd == 0x68) FIFOSD_CHECK(46)
+    else if (cmd == 0x1fb) FIFOSD_CHECK(56)
+    else return 0;
+    {
+        char email[40];
+        int i, ch;
+        int cid = RFIFOL(fd,2);
+
+        ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid);
+        memcpy(email, RFIFOP(fd,6), 40);
+        RFIFOSKIP(fd,( cmd == 0x68) ? 46 : 56);
+
+        // Check if e-mail is correct
+        if(strcmpi(email, sd->email) && //email does not matches and
+        (
+                strcmp("a@a.com", sd->email) || //it is not default email, or
+                (strcmp("a@a.com", email) && strcmp("", email)) //email sent does not matches default
+        )) {	//Fail
+                WFIFOHEAD(fd,3);
+                WFIFOW(fd,0) = 0x70;
+                WFIFOB(fd,2) = 0; // 00 = Incorrect Email address
+                WFIFOSET(fd,3);
+                return 1;
+        }
+
+        // check if this char exists
+        ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
+        if( i == MAX_CHARS )
+        { // Such a character does not exist in the account
+                WFIFOHEAD(fd,3);
+                WFIFOW(fd,0) = 0x70;
+                WFIFOB(fd,2) = 0;
+                WFIFOSET(fd,3);
+                return 1;
+        }
+
+        // remove char from list and compact it
+        for(ch = i; ch < MAX_CHARS-1; ch++)
+                sd->found_char[ch] = sd->found_char[ch+1];
+        sd->found_char[MAX_CHARS-1] = -1;
+
+        /* Delete character */
+        if(char_delete_char_sql(cid)<0){
+                //can't delete the char
+                //either SQL error or can't delete by some CONFIG conditions
+                //del fail
+                WFIFOHEAD(fd,3);
+                WFIFOW(fd, 0) = 0x70;
+                WFIFOB(fd, 2) = 0;
+                WFIFOSET(fd, 3);
+                return 1;
+        }
+        /* Char successfully deleted.*/
+        WFIFOHEAD(fd,2);
+        WFIFOW(fd,0) = 0x6f;
+        WFIFOSET(fd,2);
+    }
+    return 1;
+}
+
+// R 0187 <account ID>.l
+int chclif_parse_keepalive(int fd){
+	if (RFIFOREST(fd) < 6)
+		return 0;
+	//int aid = RFIFOL(fd,2);
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+// R 08fc <char ID>.l <new name>.24B
+// R 028d <account ID>.l <char ID>.l <new name>.24B
+int chclif_parse_reqrename(int fd, struct char_session_data* sd, int cmd){
+    int i, cid=0;
+    char name[NAME_LENGTH];
+    char esc_name[NAME_LENGTH*2+1];
+
+    if(cmd == 0x8fc){
+            FIFOSD_CHECK(30)
+            cid =RFIFOL(fd,2);
+            safestrncpy(name, (char *)RFIFOP(fd,6), NAME_LENGTH);
+            RFIFOSKIP(fd,30);
+    }
+    else if(cmd == 0x28d) {
+            FIFOSD_CHECK(34)
+            int aid = RFIFOL(fd,2);
+            cid =RFIFOL(fd,6);
+            safestrncpy(name, (char *)RFIFOP(fd,10), NAME_LENGTH);
+            RFIFOSKIP(fd,34);
+            if( aid != sd->account_id )
+                    return 1;
+    }
+
+    ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
+    if( i == MAX_CHARS )
+            return 1;
+
+    normalize_name(name,TRIM_CHARS);
+    Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
+    if( !char_check_char_name(name,esc_name) )
+    {
+            i = 1;
+            safestrncpy(sd->new_name, name, NAME_LENGTH);
+    }
+    else
+            i = 0;
+
+    WFIFOHEAD(fd, 4);
+    WFIFOW(fd,0) = 0x28e;
+    WFIFOW(fd,2) = i;
+    WFIFOSET(fd,4);
+    return 1;
+}
+
+
+int charblock_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+	struct char_session_data* sd=NULL;
+	int i=0;
+	ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == id);
+
+	if(sd == NULL || sd->charblock_timer==INVALID_TIMER) //has disconected or was required to stop
+		return 0;
+	if (sd->charblock_timer != tid){
+		sd->charblock_timer = INVALID_TIMER;
+		return 0;
+	}
+	chclif_block_character(i,sd);
+	return 0;
+}
+
+/*
+ * 0x20d <PacketLength>.W <TAG_CHARACTER_BLOCK_INFO>24B (HC_BLOCK_CHARACTER)
+ * <GID>L <szExpireDate>20B (TAG_CHARACTER_BLOCK_INFO)
+ */
+void chclif_block_character( int fd, struct char_session_data* sd){
+	int i=0, j=0, len=4;
+	time_t now = time(NULL);
+
+	WFIFOHEAD(fd, 4+MAX_CHARS*24);
+	WFIFOW(fd, 0) = 0x20d;
+
+	for(i=0; i<MAX_CHARS; i++){
+		if(sd->found_char[i] == -1)
+			continue;
+		if(sd->unban_time[i]){
+			if( sd->unban_time[i] > now ) {
+				char szExpireDate[21];
+				WFIFOL(fd, 4+j*24) = sd->found_char[i];
+				timestamp2string(szExpireDate, 20, sd->unban_time[i], "%Y-%m-%d %H:%M:%S");
+				memcpy(WFIFOP(fd,8+j*24),szExpireDate,20);
+			}
+			else {
+				WFIFOL(fd, 4+j*24) = 0;
+				sd->unban_time[i] = 0;
+				if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `unban_time`='0' WHERE `char_id`='%d' LIMIT 1", schema_config.char_db, sd->found_char[i]) )
+					Sql_ShowDebug(sql_handle);
+			}
+			len+=24;
+			j++; //pkt list idx
+		}
+	}
+	WFIFOW(fd, 2) = len; //packet len
+	WFIFOSET(fd,len);
+
+	ARR_FIND(0, MAX_CHARS, i, sd->unban_time[i] > now); //sd->charslot only have productible char
+	if(i < MAX_CHARS ){
+		sd->charblock_timer = add_timer(
+			gettick() + 10000,	// each 10s resend that list
+			charblock_timer, sd->account_id, 0);
+	}
+}
+
+// 0x28f <char_id>.L
+int chclif_parse_ackrename(int fd, struct char_session_data* sd){
+    // 0: Successful
+    // 1: This character's name has already been changed. You cannot change a character's name more than once.
+    // 2: User information is not correct.
+    // 3: You have failed to change this character's name.
+    // 4: Another user is using this character name, so please select another one.
+    FIFOSD_CHECK(6)
+    {
+        int i;
+        int cid = RFIFOL(fd,2);
+        RFIFOSKIP(fd,6);
+
+        ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
+        if( i == MAX_CHARS )
+                return 1;
+        i = char_rename_char_sql(sd, cid);
+
+        WFIFOHEAD(fd, 4);
+        WFIFOW(fd,0) = 0x290;
+        WFIFOW(fd,2) = i;
+        WFIFOSET(fd,4);
+    }
+    return 1;
+}
+
+int chclif_ack_captcha(int fd){
+    WFIFOHEAD(fd,5);
+    WFIFOW(fd,0) = 0x7e9;
+    WFIFOW(fd,2) = 5;
+    WFIFOB(fd,4) = 1;
+    WFIFOSET(fd,5);
+    return 1;
+}
+
+// R 07e5 <?>.w <aid>.l
+int chclif_parse_reqcaptcha(int fd){
+    //FIFOSD_CHECK(8)
+    RFIFOSKIP(fd,8);
+    chclif_ack_captcha(fd); 
+    return 1;
+}
+
+// R 07e7 <len>.w <aid>.l <code>.b10 <?>.b14
+int chclif_parse_chkcaptcha(int fd){
+    //FIFOSD_CHECK(32)
+    RFIFOSKIP(fd,32);
+    chclif_ack_captcha(fd);
+    return 1;
+}
+
+/**
+ * Entry point from client to char-serv
+ * function that check incoming command then split it to correct handler.
+ * @param fd: file descriptor to parse, (link to client)
+ */
+int chclif_parse(int fd) {
+	unsigned short cmd;
+	struct char_session_data* sd = (struct char_session_data*)session[fd]->session_data;
+	uint32 ipl = session[fd]->client_addr;
+    
+	// disconnect any player if no login-server.
+	if(login_fd < 0)
+		set_eof(fd);
+
+	if(session[fd]->flag.eof)
+	{
+		if( sd != NULL && sd->auth )
+		{	// already authed client
+			DBMap *online_char_db = char_get_onlinedb();
+			struct online_char_data* data = (struct online_char_data*)idb_get(online_char_db, sd->account_id);
+			if( data != NULL && data->fd == fd)
+				data->fd = -1;
+			if( data == NULL || data->server == -1) //If it is not in any server, send it offline. [Skotlex]
+				char_set_char_offline(-1,sd->account_id);
+		}
+		do_close(fd);
+		return 0;
+	}
+
+	while( RFIFOREST(fd) >= 2 )
+	{
+		int next=1;
+		cmd = RFIFOW(fd,0);
+		switch( cmd )
+		{
+		case 0x65: next=chclif_parse_reqtoconnect(fd,sd,ipl); break;
+		// char select
+		case 0x66: next=chclif_parse_charselect(fd,sd,ipl); break;
+		// createnewchar
+		case 0x970: next=chclif_parse_createnewchar(fd,sd,cmd); break;
+		case 0x67: next=chclif_parse_createnewchar(fd,sd,cmd); break;
+		// delete char
+		case 0x68: next=chclif_parse_delchar(fd,sd,cmd); break; //
+		case 0x1fb: next=chclif_parse_delchar(fd,sd,cmd); break; // 2004-04-19aSakexe+ langtype 12 char deletion packet
+		// client keep-alive packet (every 12 seconds)
+		case 0x187: next=chclif_parse_keepalive(fd); break;
+		// char rename
+		case 0x8fc: next=chclif_parse_reqrename(fd,sd,cmd); break; //request <date/version?>
+		case 0x28d: next=chclif_parse_reqrename(fd,sd,cmd); break; //request <date/version?>
+		case 0x28f: next=chclif_parse_ackrename(fd,sd); break; //Confirm change name.
+		// captcha
+		case 0x7e5: next=chclif_parse_reqcaptcha(fd); break; // captcha code request (not implemented)
+		case 0x7e7: next=chclif_parse_chkcaptcha(fd); break; // captcha code check (not implemented)
+		// deletion timer request
+		case 0x827: next=chclif_parse_char_delete2_req(fd, sd); break;
+		// deletion accept request
+		case 0x829: next=chclif_parse_char_delete2_accept(fd, sd); break;
+		// deletion cancel request
+		case 0x82b: next=chclif_parse_char_delete2_cancel(fd, sd); break;
+		// login as map-server
+		case 0x2af8: chclif_parse_maplogin(fd); return 0; // avoid processing of followup packets here
+		//pincode
+		case 0x8b8: next=chclif_parse_pincode_check( fd, sd ); break; // checks the entered pin
+		case 0x8c5: next=chclif_parse_reqpincode_window(fd,sd); break; // request for PIN window
+		case 0x8be: next=chclif_parse_pincode_change( fd, sd ); break; // pincode change request
+		case 0x8ba: next=chclif_parse_pincode_setnew( fd, sd ); break; // activate PIN system and set first PIN
+		// character movement request
+		case 0x8d4: next=chclif_parse_moveCharSlot(fd,sd); break;
+		case 0x9a1: next=chclif_parse_req_charlist(fd,sd); break;
+		// unknown packet received
+		default:
+			ShowError("parse_char: Received unknown packet "CL_WHITE"0x%x"CL_RESET" from ip '"CL_WHITE"%s"CL_RESET"'! Disconnecting!\n", RFIFOW(fd,0), ip2str(ipl, NULL));
+			set_eof(fd);
+			return 0;
+		}
+		if(next==0) return 0; // avoid processing of followup packets (prev was probably incomplete)
+	}
+
+	RFIFOFLUSH(fd);
+	return 0;
+}

+ 67 - 0
src/char/char_clif.h

@@ -0,0 +1,67 @@
+/*
+ * File:   char_clif.h
+ * Author: lighta
+ *
+ * Created on June 15, 2013, 12:06 PM
+ */
+
+#ifndef CHAR_CLIF_H
+#define	CHAR_CLIF_H
+
+#include "char.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+void chclif_moveCharSlotReply( int fd, struct char_session_data* sd, unsigned short index, short reason );
+int chclif_parse_moveCharSlot( int fd, struct char_session_data* sd);
+void chclif_pincode_sendstate( int fd, struct char_session_data* sd, enum pincode_state state );
+int chclif_parse_reqpincode_window(int fd, struct char_session_data* sd);
+int chclif_parse_pincode_check( int fd, struct char_session_data* sd );
+int chclif_parse_pincode_change( int fd, struct char_session_data* sd );
+int chclif_parse_pincode_setnew( int fd, struct char_session_data* sd );
+
+void chclif_charlist_notify( int fd, struct char_session_data* sd );
+void chclif_block_character( int fd, struct char_session_data* sd );
+int chclif_mmo_send006b(int fd, struct char_session_data* sd);
+void chclif_mmo_send082d(int fd, struct char_session_data* sd);
+void chclif_mmo_send099d(int fd, struct char_session_data *sd);
+void chclif_mmo_char_send(int fd, struct char_session_data* sd);
+void chclif_send_auth_result(int fd,char result);
+void chclif_char_delete2_ack(int fd, int char_id, uint32 result, time_t delete_date);
+void chclif_char_delete2_accept_ack(int fd, int char_id, uint32 result);
+void chclif_char_delete2_cancel_ack(int fd, int char_id, uint32 result);
+
+int chclif_parse_char_delete2_req(int fd, struct char_session_data* sd);
+int chclif_parse_char_delete2_accept(int fd, struct char_session_data* sd);
+int chclif_parse_char_delete2_cancel(int fd, struct char_session_data* sd);
+
+int chclif_parse_maplogin(int fd);
+int chclif_parse_reqtoconnect(int fd, struct char_session_data* sd,uint32 ipl);
+int chclif_parse_req_charlist(int fd, struct char_session_data* sd);
+int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl);
+int chclif_parse_createnewchar(int fd, struct char_session_data* sd,int cmd);
+int chclif_parse_delchar(int fd,struct char_session_data* sd, int cmd);
+int chclif_parse_keepalive(int fd);
+int chclif_parse_reqrename(int fd, struct char_session_data* sd, int cmd);
+int chclif_parse_ackrename(int fd, struct char_session_data* sd);
+int chclif_ack_captcha(int fd);
+int chclif_parse_reqcaptcha(int fd);
+int chclif_parse_chkcaptcha(int fd);
+void chclif_block_character( int fd, struct char_session_data* sd);
+
+int chclif_parse(int fd);
+
+
+
+
+
+
+    
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* CHAR_CLIF_H */
+

+ 99 - 0
src/char/char_cnslif.c

@@ -0,0 +1,99 @@
+// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/socket.h"
+#include "../common/showmsg.h"
+#include "../common/timer.h"
+#include "../common/ers.h"
+#include "../common/cli.h"
+#include "char.h"
+#include "char_cnslif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*======================================================
+ * Login-Server help option info
+ *------------------------------------------------------*/
+void display_helpscreen(bool do_exit)
+{
+	ShowInfo("Usage: %s [options]\n", SERVER_NAME);
+	ShowInfo("\n");
+	ShowInfo("Options:\n");
+	ShowInfo("  -?, -h [--help]\t\tDisplays this help screen.\n");
+	ShowInfo("  -v [--version]\t\tDisplays the server's version.\n");
+	ShowInfo("  --run-once\t\t\tCloses server after loading (testing).\n");
+	ShowInfo("  --char-config <file>\t\tAlternative char-server configuration.\n");
+	ShowInfo("  --lan-config <file>\t\tAlternative lag configuration.\n");
+	ShowInfo("  --inter-config <file>\t\tAlternative inter-server configuration.\n");
+	ShowInfo("  --msg-config <file>\t\tAlternative message configuration.\n");
+	if( do_exit )
+		exit(EXIT_SUCCESS);
+}
+
+/**
+ * Timered function to check if the console has a new event to be read.
+ * @param tid: timer id
+ * @param tick: tick of execution
+ * @param id: user account id
+ * @param data: unused
+ * @return 0
+ */
+int cnslif_console_timer(int tid, unsigned int tick, int id, intptr_t data) {
+	char buf[MAX_CONSOLE_IN]; //max cmd atm is 63+63+63+3+3
+
+	memset(buf,0,MAX_CONSOLE_IN); //clear out buf
+
+	if(cli_hasevent()){
+		if(fgets(buf, MAX_CONSOLE_IN, stdin)==NULL)
+			return -1;
+		else if(strlen(buf)>MIN_CONSOLE_IN)
+			cnslif_parse(buf);
+	}
+	return 0;
+}
+
+// Console Command Parser [Wizputer]
+int cnslif_parse(const char* buf)
+{
+	char type[64];
+	char command[64];
+	int n=0;
+
+	if( ( n = sscanf(buf, "%63[^:]:%63[^\n]", type, command) ) < 2 ){
+		if((n = sscanf(buf, "%63[^\n]", type))<1) return -1; //nothing to do no arg
+	}
+	if( n != 2 ){ //end string
+		ShowNotice("Type: '%s'\n",type);
+		command[0] = '\0';
+	}
+	else
+		ShowNotice("Type of command: '%s' || Command: '%s'\n",type,command);
+
+	if( n == 2 && strcmpi("server", type) == 0 ){
+		if( strcmpi("shutdown", command) == 0 || strcmpi("exit", command) == 0 || strcmpi("quit", command) == 0 ){
+			runflag = 0;
+		}
+		else if( strcmpi("alive", command) == 0 || strcmpi("status", command) == 0 )
+			ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
+	}
+	else if( strcmpi("ers_report", type) == 0 ){
+		ers_report();
+	}
+	else if( strcmpi("help", type) == 0 ){
+		ShowInfo("Available commands:\n");
+		ShowInfo("\t server:shutdown => Stops the server.\n");
+		ShowInfo("\t server:alive => Checks if the server is running.\n");
+		ShowInfo("\t ers_report => Displays database usage.\n");
+	}
+
+	return 0;
+}
+
+void do_init_chcnslif(void){
+	if( charserv_config.console ){ //start listening
+		add_timer_func_list(cnslif_console_timer, "cnslif_console_timer");
+		add_timer_interval(gettick()+1000, cnslif_console_timer, 0, 0, 1000); //start in 1s each 1sec
+	}
+}

+ 25 - 0
src/char/char_cnslif.h

@@ -0,0 +1,25 @@
+/*
+ * File:   char_cnslif.h
+ * Author: lighta
+ *
+ * Created on June 15, 2013, 12:07 PM
+ */
+
+#ifndef CHAR_CNSLIF_H
+#define	CHAR_CNSLIF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+void display_helpscreen(bool do_exit);
+int cnslif_parse(const char* buf);
+void do_init_chcnslif(void);
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* CHAR_CNSLIF_H */
+

+ 774 - 0
src/char/char_logif.c

@@ -0,0 +1,774 @@
+// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/showmsg.h"
+#include "../common/sql.h"
+#include "../common/utils.h"
+#include "../common/strlib.h"
+#include "inter.h"
+#include "int_guild.h"
+#include "char.h"
+#include "char_clif.h"
+#include "char_mapif.h"
+#include "char_logif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+//early declaration
+void chlogif_on_ready(void);
+void chlogif_on_disconnect(void);
+
+int chlogif_pincode_notifyLoginPinError( int account_id ){
+	if (login_fd > 0 && session[login_fd] && !session[login_fd]->flag.eof){
+		WFIFOHEAD(login_fd,6);
+		WFIFOW(login_fd,0) = 0x2739;
+		WFIFOL(login_fd,2) = account_id;
+		WFIFOSET(login_fd,6);
+		return 1;
+	}
+	return 0;
+}
+
+int chlogif_pincode_notifyLoginPinUpdate( int account_id, char* pin ){
+	if (login_fd > 0 && session[login_fd] && !session[login_fd]->flag.eof){
+		WFIFOHEAD(login_fd,11);
+		WFIFOW(login_fd,0) = 0x2738;
+		WFIFOL(login_fd,2) = account_id;
+		strncpy( (char*)WFIFOP(login_fd,6), pin, PINCODE_LENGTH+1 );
+		WFIFOSET(login_fd,11);
+		return 1;
+	}
+	return 0;
+}
+
+void chlogif_pincode_start(int fd, struct char_session_data* sd){
+	if( charserv_config.pincode_config.pincode_enabled ){
+		// PIN code system enabled
+		if( sd->pincode[0] == '\0' ){
+			// No PIN code has been set yet
+			if( charserv_config.pincode_config.pincode_force ){
+				chclif_pincode_sendstate( fd, sd, PINCODE_NEW );
+			}else{
+				chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
+			}
+		}else{
+			if( !(charserv_config.pincode_config.pincode_changetime)
+			|| ( sd->pincode_change + charserv_config.pincode_config.pincode_changetime ) > time(NULL) ){
+				DBMap*  online_char_db = char_get_onlinedb();
+				struct online_char_data* node = (struct online_char_data*)idb_get( online_char_db, sd->account_id );
+
+				if( node != NULL && node->pincode_success ){
+					// User has already passed the check
+					chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
+				}else{
+					// Ask user for his PIN code
+					chclif_pincode_sendstate( fd, sd, PINCODE_ASK );
+				}
+			}else{
+				// User hasnt changed his PIN code too long
+				chclif_pincode_sendstate( fd, sd, PINCODE_EXPIRED );
+			}
+		}
+	}else{
+		// PIN code system disabled
+		chclif_pincode_sendstate( fd, sd, PINCODE_OK );
+	}
+}
+
+/**
+ * Load this character's account id into the 'online accounts' packet
+ * @see DBApply
+ */
+static int chlogif_send_acc_tologin_sub(DBKey key, DBData *data, va_list ap) {
+	struct online_char_data* character = db_data2ptr(data);
+	int* i = va_arg(ap, int*);
+	if(character->server > -1) {
+		WFIFOL(login_fd,8+(*i)*4) = character->account_id;
+		(*i)++;
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * Timered function to send all account_id connected to login-serv
+ * @param tid : Timer id
+ * @param tick : Scheduled tick
+ * @param id : GID linked to that timered call
+ * @param data : data transmited for delayed function
+ * @return 
+ */
+int chlogif_send_acc_tologin(int tid, unsigned int tick, int id, intptr_t data) {
+	if ( chlogif_isconnected() ){
+		DBMap*  online_char_db = char_get_onlinedb();
+		// send account list to login server
+		int users = online_char_db->size(online_char_db);
+		int i = 0;
+
+		WFIFOHEAD(login_fd,8+users*4);
+		WFIFOW(login_fd,0) = 0x272d;
+		online_char_db->foreach(online_char_db, chlogif_send_acc_tologin_sub, &i, users);
+		WFIFOW(login_fd,2) = 8+ i*4;
+		WFIFOL(login_fd,4) = i;
+		WFIFOSET(login_fd,WFIFOW(login_fd,2));
+		return 1;
+	}
+	return 0;
+}
+
+int chlogif_send_usercount(int users){
+	if( login_fd > 0 && session[login_fd] )
+	{
+		// send number of user to login server
+		WFIFOHEAD(login_fd,6);
+		WFIFOW(login_fd,0) = 0x2714;
+		WFIFOL(login_fd,2) = users;
+		WFIFOSET(login_fd,6);
+		return 1;
+	}
+	return 0;
+}
+
+
+int chlogif_broadcast_user_count(int tid, unsigned int tick, int id, intptr_t data)
+{
+	uint8 buf[6];
+	int users = char_count_users();
+
+	// only send an update when needed
+	static int prev_users = 0;
+	if( prev_users == users )
+		return 0;
+	prev_users = users;
+
+	if( chlogif_isconnected() )
+	{
+		// send number of user to login server
+		WFIFOHEAD(login_fd,6);
+		WFIFOW(login_fd,0) = 0x2714;
+		WFIFOL(login_fd,2) = users;
+		WFIFOSET(login_fd,6);
+	}
+
+	// send number of players to all map-servers
+	WBUFW(buf,0) = 0x2b00;
+	WBUFL(buf,2) = users;
+	chmapif_sendall(buf,6);
+
+	return 0;
+}
+
+//Send packet forward to login-server for account saving
+int chlogif_save_accreg2(unsigned char* buf, int len){
+	if (login_fd > 0) {
+		WFIFOHEAD(login_fd,len+4);
+		memcpy(WFIFOP(login_fd,4), buf, len);
+		WFIFOW(login_fd,0) = 0x2728;
+		WFIFOW(login_fd,2) = len+4;
+		WFIFOSET(login_fd,len+4);
+		return 1;
+	}
+	return 0;
+}
+
+int chlogif_request_accreg2(int account_id, int char_id){
+	if (login_fd > 0) {
+		WFIFOHEAD(login_fd,10);
+		WFIFOW(login_fd,0) = 0x272e;
+		WFIFOL(login_fd,2) = account_id;
+		WFIFOL(login_fd,6) = char_id;
+		WFIFOSET(login_fd,10);
+		return 1;
+	}
+	return 0;
+}
+
+int chlogif_send_reqaccdata(int fd, struct char_session_data *sd){
+        //loginif_isconnected
+	if (login_fd > 0) { // request account data
+		// request account data
+		WFIFOHEAD(fd,6);
+		WFIFOW(fd,0) = 0x2716;
+		WFIFOL(fd,2) = sd->account_id;
+		WFIFOSET(fd,6);
+		return 1;
+	}
+	return 0;
+}
+
+int chlogif_send_setacconline(int aid){
+        //loginif_isconnected
+	if (login_fd > 0 && !session[login_fd]->flag.eof){
+		WFIFOHEAD(login_fd,6);
+		WFIFOW(login_fd,0) = 0x272b;
+		WFIFOL(login_fd,2) = aid;
+		WFIFOSET(login_fd,6);
+		return 1;
+	}
+	return 0;
+}
+
+void chlogif_send_setallaccoffline(int fd){
+	WFIFOHEAD(fd,2);
+	WFIFOW(fd,0) = 0x2737;
+	WFIFOSET(fd,2);
+}
+
+int chlogif_send_setaccoffline(int fd, int aid){
+	if (chlogif_isconnected()){
+		WFIFOHEAD(fd,6);
+		WFIFOW(fd,0) = 0x272c;
+		WFIFOL(fd,2) = aid;
+		WFIFOSET(fd,6);
+		return 1;
+	}
+	return 0;
+}
+
+int chlogif_parse_ackconnect(int fd, struct char_session_data* sd){
+	if (RFIFOREST(fd) < 3)
+		return 0;
+
+	if (RFIFOB(fd,2)) {
+		//printf("connect login server error : %d\n", RFIFOB(fd,2));
+		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 map_athena.conf and char_athena.conf\n");
+		set_eof(fd);
+		return 0;
+	} else {
+		ShowStatus("Connected to login-server (connection #%d).\n", fd);
+		chlogif_on_ready();
+	}
+	RFIFOSKIP(fd,3);
+	return 1;
+}
+
+int chlogif_parse_ackaccreq(int fd, struct char_session_data* sd){
+	if (RFIFOREST(fd) < 25)
+		return 0;
+	{
+		int account_id = RFIFOL(fd,2);
+		uint32 login_id1 = RFIFOL(fd,6);
+		uint32 login_id2 = RFIFOL(fd,10);
+		uint8 sex = RFIFOB(fd,14);
+		uint8 result = RFIFOB(fd,15);
+		int request_id = RFIFOL(fd,16);
+		uint32 version = RFIFOL(fd,20);
+		uint8 clienttype = RFIFOB(fd,24);
+		RFIFOSKIP(fd,25);
+
+		if( session_isActive(request_id) && (sd=(struct char_session_data*)session[request_id]->session_data) &&
+			!sd->auth && sd->account_id == account_id && sd->login_id1 == login_id1 && sd->login_id2 == login_id2 && sd->sex == sex )
+		{
+			int client_fd = request_id;
+			sd->version = version;
+			sd->clienttype = clienttype;
+			if(sd->version != date2version(PACKETVER))
+				ShowWarning("s aid=%d has an incorect version=%d in clientinfo. Server compiled for %d\n",
+					sd->account_id,sd->version,date2version(PACKETVER));
+
+			switch( result )
+			{
+			case 0:// ok
+				char_auth_ok(client_fd, sd);
+				break;
+			case 1:// auth failed
+				WFIFOHEAD(client_fd,3);
+				WFIFOW(client_fd,0) = 0x6c;
+				WFIFOB(client_fd,2) = 0;// rejected from server
+				WFIFOSET(client_fd,3);
+				break;
+			}
+		}
+	}
+	return 1;
+}
+
+int chlogif_parse_reqaccdata(int fd, struct char_session_data* sd){
+	int u_fd; //user fd
+	if (RFIFOREST(fd) < 79)
+		return 0;
+
+	// find the authenticated session with this account id
+	ARR_FIND( 0, fd_max, u_fd, session[u_fd] && (sd = (struct char_session_data*)session[u_fd]->session_data) && sd->auth && sd->account_id == RFIFOL(fd,2) );
+	if( u_fd < fd_max )
+	{
+		int server_id;
+		memcpy(sd->email, RFIFOP(fd,6), 40);
+		sd->expiration_time = (time_t)RFIFOL(fd,46);
+		sd->group_id = RFIFOB(fd,50);
+		sd->char_slots = RFIFOB(fd,51);
+		if( sd->char_slots > MAX_CHARS ) {
+			ShowError("Account '%d' `character_slots` column is higher than supported MAX_CHARS (%d), update MAX_CHARS in mmo.h! capping to MAX_CHARS...\n",sd->account_id,sd->char_slots);
+			sd->char_slots = MAX_CHARS;/* cap to maximum */
+		} else if ( !sd->char_slots )/* no value aka 0 in sql */
+			sd->char_slots = MIN_CHARS;/* cap to minimum */
+		safestrncpy(sd->birthdate, (const char*)RFIFOP(fd,52), sizeof(sd->birthdate));
+                safestrncpy(sd->pincode, (const char*)RFIFOP(fd,63), sizeof(sd->pincode));
+                sd->pincode_change = (time_t)RFIFOL(fd,68);
+                sd->bank_vault = RFIFOL(fd,72);
+                sd->isvip = RFIFOB(fd,76);
+                sd->chars_vip = RFIFOB(fd,77);
+                sd->chars_billing = RFIFOB(fd,78);
+		ARR_FIND( 0, ARRAYLENGTH(map_server), server_id, map_server[server_id].fd > 0 && map_server[server_id].map[0] );
+		// continued from char_auth_ok...
+		if( server_id == ARRAYLENGTH(map_server) || //server not online, bugreport:2359
+			(charserv_config.max_connect_user == 0 && sd->group_id != charserv_config.gm_allow_group) ||
+			( charserv_config.max_connect_user > 0 && char_count_users() >= charserv_config.max_connect_user && sd->group_id != charserv_config.gm_allow_group ) ) {
+			// refuse connection (over populated)
+			WFIFOHEAD(u_fd,3);
+			WFIFOW(u_fd,0) = 0x6c;
+			WFIFOW(u_fd,2) = 0;
+			WFIFOSET(u_fd,3);
+		} else {
+			// send characters to player
+			chclif_mmo_char_send(u_fd, sd);
+			if(sd->version >= date2version(20110309)){
+				ShowInfo("Asking to start pincode\n");
+				chlogif_pincode_start(u_fd,sd);
+			}
+		}
+	}
+	RFIFOSKIP(fd,79);
+	return 1;
+}
+
+int chlogif_parse_keepalive(int fd, struct char_session_data* sd){
+	if (RFIFOREST(fd) < 2)
+		return 0;
+	RFIFOSKIP(fd,2);
+	session[fd]->flag.ping = 0;
+	return 1;
+}
+
+int chlogif_parse_ackchangesex(int fd, struct char_session_data* sd){
+	if (RFIFOREST(fd) < 7)
+		return 0;
+	{
+		unsigned char buf[7];
+		int i;
+
+		int acc = RFIFOL(fd,2);
+		int sex = RFIFOB(fd,6);
+		RFIFOSKIP(fd,7);
+
+		if( acc > 0 )
+		{// TODO: Is this even possible?
+			int char_id[MAX_CHARS];
+			int class_[MAX_CHARS];
+			int guild_id[MAX_CHARS];
+			int num;
+			char* data;
+			DBMap*  auth_db = char_get_authdb();
+
+			struct auth_node* node = (struct auth_node*)idb_get(auth_db, acc);
+			if( node != NULL )
+				node->sex = sex;
+
+			// get characters
+			if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`class`,`guild_id` FROM `%s` WHERE `account_id` = '%d'", schema_config.char_db, acc) )
+				Sql_ShowDebug(sql_handle);
+			for( i = 0; i < MAX_CHARS && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i )
+			{
+				Sql_GetData(sql_handle, 0, &data, NULL); char_id[i] = atoi(data);
+				Sql_GetData(sql_handle, 1, &data, NULL); class_[i] = atoi(data);
+				Sql_GetData(sql_handle, 2, &data, NULL); guild_id[i] = atoi(data);
+			}
+			num = i;
+			for( i = 0; i < num; ++i )
+			{
+				if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER ||
+					class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY ||
+					class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER ||
+					class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER ||
+					class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T ||
+					class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER ||
+					class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO )
+				{
+					// job modification
+					if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER )
+						class_[i] = (sex ? JOB_BARD : JOB_DANCER);
+					else if( class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY )
+						class_[i] = (sex ? JOB_CLOWN : JOB_GYPSY);
+					else if( class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER )
+						class_[i] = (sex ? JOB_BABY_BARD : JOB_BABY_DANCER);
+					else if( class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER )
+						class_[i] = (sex ? JOB_MINSTREL : JOB_WANDERER);
+					else if( class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T )
+						class_[i] = (sex ? JOB_MINSTREL_T : JOB_WANDERER_T);
+					else if( class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER )
+						class_[i] = (sex ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER);
+					else if( class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO )
+						class_[i] = (sex ? JOB_KAGEROU : JOB_OBORO);
+				}
+
+				if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `class`='%d', `weapon`='0', `shield`='0', `head_top`='0', `head_mid`='0', `head_bottom`='0' WHERE `char_id`='%d'", schema_config.char_db, class_[i], char_id[i]) )
+					Sql_ShowDebug(sql_handle);
+
+				if( guild_id[i] )// If there is a guild, update the guild_member data [Skotlex]
+					inter_guild_sex_changed(guild_id[i], acc, char_id[i], sex);
+			}
+			Sql_FreeResult(sql_handle);
+
+			// disconnect player if online on char-server
+			char_disconnect_player(acc);
+		}
+
+		// notify all mapservers about this change
+		WBUFW(buf,0) = 0x2b0d;
+		WBUFL(buf,2) = acc;
+		WBUFB(buf,6) = sex;
+		chmapif_sendall(buf, 7);
+	}
+	return 1;
+}
+
+int chlogif_parse_ackacc2req(int fd, struct char_session_data* sd){
+	if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+		return 0;
+
+	{	//Receive account_reg2 registry, forward to map servers.
+		unsigned char buf[13+ACCOUNT_REG2_NUM*sizeof(struct global_reg)];
+		memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2));
+		WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex]
+		chmapif_sendall(buf, WBUFW(buf,2));
+		RFIFOSKIP(fd, RFIFOW(fd,2));
+	}
+	return 1;
+}
+
+int chlogif_parse_accbannotification(int fd, struct char_session_data* sd){
+	if (RFIFOREST(fd) < 11)
+		return 0;
+
+	{	// send to all map-servers to disconnect the player
+		unsigned char buf[11];
+		WBUFW(buf,0) = 0x2b14;
+		WBUFL(buf,2) = RFIFOL(fd,2);
+		WBUFB(buf,6) = RFIFOB(fd,6); // 0: change of statut, 1: ban
+		WBUFL(buf,7) = RFIFOL(fd,7); // status or final date of a banishment
+		chmapif_sendall(buf, 11);
+	}
+	// disconnect player if online on char-server
+	char_disconnect_player(RFIFOL(fd,2));
+	RFIFOSKIP(fd,11);
+	return 1;
+}
+
+int chlogif_parse_askkick(int fd, struct char_session_data* sd){
+	if (RFIFOREST(fd) < 6)
+		return 0;
+	{
+		DBMap*  online_char_db = char_get_onlinedb();
+		DBMap*  auth_db = char_get_authdb();
+		int aid = RFIFOL(fd,2);
+		struct online_char_data* character = (struct online_char_data*)idb_get(online_char_db, aid);
+		RFIFOSKIP(fd,6);
+		if( character != NULL )
+		{// account is already marked as online!
+			if( character->server > -1 )
+			{	//Kick it from the map server it is on.
+				mapif_disconnectplayer(map_server[character->server].fd, character->account_id, character->char_id, 2);
+				if (character->waiting_disconnect == INVALID_TIMER)
+					character->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, char_chardb_waiting_disconnect, character->account_id, 0);
+			}
+			else
+			{// Manual kick from char server.
+				struct char_session_data *tsd;
+				int i;
+				ARR_FIND( 0, fd_max, i, session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid );
+				if( i < fd_max )
+				{
+					chclif_send_auth_result(i,2);  //Send "Someone has already logged in with this id"
+					set_eof(i);
+				}
+				else // still moving to the map-server
+					char_set_char_offline(-1, aid);
+			}
+		}
+		idb_remove(auth_db, aid);// reject auth attempts from map-server
+	}
+	return 1;
+}
+
+int chlogif_parse_updip(int fd, struct char_session_data* sd){
+	unsigned char buf[2];
+	uint32 new_ip = 0;
+
+	WBUFW(buf,0) = 0x2b1e;
+	chmapif_sendall(buf, 2);
+
+	new_ip = host2ip(charserv_config.login_ip_str);
+	if (new_ip && new_ip != charserv_config.login_ip)
+		charserv_config.login_ip = new_ip; //Update login ip, too.
+
+	new_ip = host2ip(charserv_config.char_ip_str);
+	if (new_ip && new_ip != charserv_config.char_ip)
+	{	//Update ip.
+		charserv_config.char_ip = new_ip;
+		ShowInfo("Updating IP for [%s].\n", charserv_config.char_ip_str);
+		// notify login server about the change
+		WFIFOHEAD(fd,6);
+		WFIFOW(fd,0) = 0x2736;
+		WFIFOL(fd,2) = htonl(charserv_config.char_ip);
+		WFIFOSET(fd,6);
+	}
+
+	RFIFOSKIP(fd,2);
+	return 1;
+}
+
+/**
+ * Send to login-serv the request of banking operation from map
+ * HA 0x2740<aid>L <type>B <data>L
+ * @param account_id
+ * @param type : 0 = select, 1 = update
+ * @param data
+ * @return
+ */
+int chlogif_BankingReq(int32 account_id, int8 type, int32 data){
+	loginif_check(-1);
+
+	WFIFOHEAD(login_fd,11);
+	WFIFOW(login_fd,0) = 0x2740;
+	WFIFOL(login_fd,2) = account_id;
+	WFIFOB(login_fd,6) = type;
+	WFIFOL(login_fd,7) = data;
+	WFIFOSET(login_fd,11);
+	return 0;
+}
+
+/*
+ * Received the banking data from login and transmit it to all map-serv
+ * AH 0x2741<aid>L <bank_vault>L <not_fw>B
+ * HZ 0x2b29 <aid>L <bank_vault>L
+ */
+int chlogif_parse_BankingAck(int fd){
+	if (RFIFOREST(fd) < 11)
+            return 0;
+	else {
+		uint32 aid = RFIFOL(fd,2);
+		int32 bank_vault = RFIFOL(fd,6);
+		char not_fw = RFIFOB(fd,10);
+		RFIFOSKIP(fd,11);
+
+		if(not_fw==0) chmapif_BankingAck(aid, bank_vault);
+	}
+	return 1;
+}
+
+/*
+ * AH 0x2743
+ * We received the info from login-serv, transmit it to map
+ */
+int chlogif_parse_vipack(int fd) {
+#ifdef VIP_ENABLE
+	if (RFIFOREST(fd) < 20)
+		return 0;
+	else {
+		uint32 aid = RFIFOL(fd,2); //aid
+		uint32 vip_time = RFIFOL(fd,6); //vip_time
+		uint8 isvip = RFIFOB(fd,10); //isvip
+		uint32 groupid = RFIFOL(fd,11); //new group id
+		uint8 isgm = RFIFOB(fd,15); //isgm
+		int mapfd = RFIFOL(fd,16); //link to mapserv for ack
+		RFIFOSKIP(fd,20);
+		mapif_vipack(mapfd,aid,vip_time,isvip,isgm,groupid);
+	}
+#endif
+	return 1;
+}
+
+/**
+ * HZ 0x2b2b
+ * Request vip data from loginserv
+ * @param aid : account_id to request the vip data
+ * @param type : &2 define new duration, &1 load info
+ * @param add_vip_time : tick to add to vip timestamp
+ * @param mapfd: link to mapserv for ack
+ * @return 0 if success
+ */
+int chlogif_reqvipdata(uint32 aid, uint8 type, int32 timediff, int mapfd) {
+	loginif_check(-1);
+#ifdef VIP_ENABLE
+	WFIFOHEAD(login_fd,15);
+	WFIFOW(login_fd,0) = 0x2742;
+	WFIFOL(login_fd,2) = aid; //aid
+	WFIFOB(login_fd,6) = type; //type
+	WFIFOL(login_fd,7) = timediff; //req_inc_duration
+	WFIFOL(login_fd,11) = mapfd; //req_inc_duration
+	WFIFOSET(login_fd,15);
+#endif
+	return 0;
+}
+
+
+int chlogif_parse(int fd) {
+	struct char_session_data* sd = NULL;
+        
+	// only process data from the login-server
+	if( fd != login_fd ) {
+		ShowDebug("parse_fromlogin: Disconnecting invalid session #%d (is not the login-server)\n", fd);
+		do_close(fd);
+		return 0;
+	}
+
+	if( session[fd]->flag.eof ) {
+		do_close(fd);
+		login_fd = -1;
+		chlogif_on_disconnect();
+		return 0;
+	} else if ( session[fd]->flag.ping ) {/* we've reached stall time */
+		if( DIFF_TICK(last_tick, session[fd]->rdata_tick) > (stall_time * 2) ) {/* we can't wait any longer */
+			set_eof(fd);
+			return 0;
+		} else if( session[fd]->flag.ping != 2 ) { /* we haven't sent ping out yet */
+			WFIFOHEAD(fd,2);// sends a ping packet to login server (will receive pong 0x2718)
+			WFIFOW(fd,0) = 0x2719;
+			WFIFOSET(fd,2);
+
+			session[fd]->flag.ping = 2;
+		}
+	}
+
+	sd = (struct char_session_data*)session[fd]->session_data;
+
+	while(RFIFOREST(fd) >= 2) {
+		int next=1;
+		uint16 command = RFIFOW(fd,0);
+		switch( command )
+		{
+			case 0x2741: next=chlogif_parse_BankingAck(fd); break;
+			case 0x2743: next=chlogif_parse_vipack(fd); break;
+			// acknowledgement of connect-to-loginserver request
+			case 0x2711: next=chlogif_parse_ackconnect(fd,sd); break;
+			// acknowledgement of account authentication request
+			case 0x2713: next=chlogif_parse_ackaccreq(fd, sd); break;
+			// account data
+			case 0x2717: next=chlogif_parse_reqaccdata(fd, sd); break;
+			// login-server alive packet
+			case 0x2718: next=chlogif_parse_keepalive(fd, sd); break;
+			// changesex reply
+			case 0x2723: next=chlogif_parse_ackchangesex(fd, sd); break;
+			// reply to an account_reg2 registry request
+			case 0x2729: next=chlogif_parse_ackacc2req(fd, sd); break;
+			// State change of account/ban notification (from login-server)
+			case 0x2731: next=chlogif_parse_accbannotification(fd, sd); break;
+			// Login server request to kick a character out. [Skotlex]
+			case 0x2734: next=chlogif_parse_askkick(fd,sd); break;
+			// ip address update signal from login server
+			case 0x2735: next=chlogif_parse_updip(fd,sd); break;
+			default:
+				ShowError("Unknown packet 0x%04x received from login-server, disconnecting.\n", command);
+				set_eof(fd);
+				return 0;
+		}
+		if(next==0) return 0; //do not parse next data
+	}
+
+	RFIFOFLUSH(fd);
+	return 0;
+}
+
+int chlogif_check_connect_logserver(int tid, unsigned int tick, int id, intptr_t data) {
+	if (login_fd > 0 && session[login_fd] != NULL)
+		return 0;
+
+	ShowInfo("Attempt to connect to login-server...\n");
+	login_fd = make_connection(charserv_config.login_ip, charserv_config.login_port, false,10);
+	if (login_fd == -1)
+	{	//Try again later. [Skotlex]
+		login_fd = 0;
+		return 0;
+	}
+	session[login_fd]->func_parse = chlogif_parse;
+	session[login_fd]->flag.server = 1;
+	realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
+
+	WFIFOHEAD(login_fd,86);
+	WFIFOW(login_fd,0) = 0x2710;
+	memcpy(WFIFOP(login_fd,2), charserv_config.userid, 24);
+	memcpy(WFIFOP(login_fd,26), charserv_config.passwd, 24);
+	WFIFOL(login_fd,50) = 0;
+	WFIFOL(login_fd,54) = htonl(charserv_config.char_ip);
+	WFIFOW(login_fd,58) = htons(charserv_config.char_port);
+	memcpy(WFIFOP(login_fd,60), charserv_config.server_name, 20);
+	WFIFOW(login_fd,80) = 0;
+	WFIFOW(login_fd,82) = charserv_config.char_maintenance;
+	WFIFOW(login_fd,84) = charserv_config.char_new_display; //only display (New) if they want to [Kevin]
+	WFIFOSET(login_fd,86);
+
+	return 1;
+}
+
+
+
+int chlogif_isconnected(){
+	return (login_fd > 0 && session[login_fd] && !session[login_fd]->flag.eof);
+}
+
+void do_init_chlogif(void) {
+	// establish char-login connection if not present
+	add_timer_func_list(chlogif_check_connect_logserver, "check_connect_login_server");
+	add_timer_interval(gettick() + 1000, chlogif_check_connect_logserver, 0, 0, 10 * 1000);
+
+	// send a list of all online account IDs to login server
+	add_timer_func_list(chlogif_send_acc_tologin, "send_accounts_tologin");
+	add_timer_interval(gettick() + 1000, chlogif_send_acc_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour
+}
+
+/// Resets all the data.
+void chlogif_reset(void){
+	int id;
+	// TODO kick everyone out and reset everything or wait for connect and try to reaquire locks [FlavioJS]
+	for( id = 0; id < ARRAYLENGTH(map_server); ++id )
+		chmapif_server_reset(id);
+	flush_fifos();
+	exit(EXIT_FAILURE);
+}
+
+/// Checks the conditions for the server to stop.
+/// Releases the cookie when all characters are saved.
+/// If all the conditions are met, it stops the core loop.
+void chlogif_check_shutdown(void)
+{
+	if( runflag != CHARSERVER_ST_SHUTDOWN )
+		return;
+	runflag = CORE_ST_STOP;
+}
+
+/// Called when the connection to Login Server is disconnected.
+void chlogif_on_disconnect(void){
+	ShowWarning("Connection to Login Server lost.\n\n");
+}
+
+/// Called when all the connection steps are completed.
+void chlogif_on_ready(void)
+{
+	int i;
+
+	chlogif_check_shutdown();
+
+	//Send online accounts to login server.
+	chlogif_send_acc_tologin(INVALID_TIMER, gettick(), 0, 0);
+
+	// if no map-server already connected, display a message...
+	ARR_FIND( 0, ARRAYLENGTH(map_server), i, map_server[i].fd > 0 && map_server[i].map[0] );
+	if( i == ARRAYLENGTH(map_server) )
+		ShowStatus("Awaiting maps from map-server.\n");
+}
+
+void do_final_chlogif(void)
+{
+	if( login_fd != -1 )
+	{
+		do_close(login_fd);
+		login_fd = -1;
+	}
+}

+ 63 - 0
src/char/char_logif.h

@@ -0,0 +1,63 @@
+/*
+ * File:   char_logif.h
+ * Author: lighta
+ *
+ * Created on June 15, 2013, 12:05 PM
+ */
+
+#ifndef CHAR_LOGIF_H
+#define	CHAR_LOGIF_H
+
+#include "char.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+int chlogif_pincode_notifyLoginPinError( int account_id );
+int chlogif_pincode_notifyLoginPinUpdate( int account_id, char* pin );
+void chlogif_pincode_start(int fd, struct char_session_data* sd);
+int chlogif_send_acc_tologin(int tid, unsigned int tick, int id, intptr_t data);
+int chlogif_broadcast_user_count(int tid, unsigned int tick, int id, intptr_t data);
+int chlogif_send_usercount(int users);
+int chlogif_save_accreg2(unsigned char* buf, int len);
+int chlogif_request_accreg2(int account_id, int char_id);
+int chlogif_send_reqaccdata(int fd, struct char_session_data *sd);
+int chlogif_send_setacconline(int aid);
+void chlogif_send_setallaccoffline(int fd);
+int chlogif_send_setaccoffline(int fd, int aid);
+int chlogif_parse_ackconnect(int fd, struct char_session_data* sd);
+int chlogif_parse_ackaccreq(int fd, struct char_session_data* sd);
+int chlogif_parse_reqaccdata(int fd, struct char_session_data* sd);
+int chlogif_parse_keepalive(int fd, struct char_session_data* sd);
+int chlogif_parse_ackchangesex(int fd, struct char_session_data* sd);
+int chlogif_parse_ackacc2req(int fd, struct char_session_data* sd);
+int chlogif_parse_accbannotification(int fd, struct char_session_data* sd);
+int chlogif_parse_askkick(int fd, struct char_session_data* sd);
+int chlogif_parse_updip(int fd, struct char_session_data* sd);
+
+int chlogif_BankingReq(int32 account_id, int8 type, int32 data);
+int chlogif_parse_BankingAck(int fd);
+int chlogif_parse_vipack(int fd);
+int chlogif_reqvipdata(uint32 aid, uint8 type, int32 timediff, int mapfd);
+
+int chlogif_parse(int fd);
+
+int chlogif_isconnected();
+int chlogif_check_connect_logserver(int tid, unsigned int tick, int id, intptr_t data);
+void do_init_chlogif(void);
+void chlogif_reset(void);
+void chlogif_check_shutdown(void);
+void chlogif_on_disconnect(void);
+void chlogif_on_ready(void);
+void do_final_chlogif(void);
+
+
+#define loginif_check(a) { if(!chlogif_isconnected()) return a; }
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* CHAR_LOGIF_H */
+

+ 1501 - 0
src/char/char_mapif.c

@@ -0,0 +1,1501 @@
+
+
+/**
+ * @file char_mapif.c
+ * Module purpose is to handle incoming and outgoing requests with map-server.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#include "../common/socket.h"
+#include "../common/sql.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+#include "../common/strlib.h"
+#include "inter.h"
+#include "char.h"
+#include "char_logif.h"
+#include "char_mapif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Packet send to all map-servers, attach to ourself
+ * @param buf: packet to send in form of an array buffer
+ * @param len: size of packet
+ * @return : the number of map-serv the packet was sent to
+ */
+int chmapif_sendall(unsigned char *buf, unsigned int len){
+	int i, c;
+
+	c = 0;
+	for(i = 0; i < ARRAYLENGTH(map_server); i++) {
+		int fd;
+		if ((fd = map_server[i].fd) > 0) {
+			WFIFOHEAD(fd,len);
+			memcpy(WFIFOP(fd,0), buf, len);
+			WFIFOSET(fd,len);
+			c++;
+		}
+	}
+
+	return c;
+}
+
+/**
+ * Packet send to all map-servers, except one. (wos: without our self) attach to ourself
+ * @param sfd: fd to discard sending to
+ * @param buf: packet to send in form of an array buffer
+ * @param len: size of packet
+ * @return : the number of map-serv the packet was sent to
+ */
+int chmapif_sendallwos(int sfd, unsigned char *buf, unsigned int len){
+	int i, c;
+
+	c = 0;
+	for(i = 0; i < ARRAYLENGTH(map_server); i++) {
+		int fd;
+		if ((fd = map_server[i].fd) > 0 && fd != sfd) {
+			WFIFOHEAD(fd,len);
+			memcpy(WFIFOP(fd,0), buf, len);
+			WFIFOSET(fd,len);
+			c++;
+		}
+	}
+
+	return c;
+}
+
+/**
+ * Packet send to all char-servers, except one. (wos: without our self)
+ * @param fd: fd to send packet too
+ * @param buf: packet to send in form of an array buffer
+ * @param len: size of packet
+ * @return : the number of map-serv the packet was sent to (O|1)
+ */
+int chmapif_send(int fd, unsigned char *buf, unsigned int len){
+	if (fd >= 0) {
+		int i;
+		ARR_FIND( 0, ARRAYLENGTH(map_server), i, fd == map_server[i].fd );
+		if( i < ARRAYLENGTH(map_server) )
+		{
+			WFIFOHEAD(fd,len);
+			memcpy(WFIFOP(fd,0), buf, len);
+			WFIFOSET(fd,len);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+
+
+/**
+ * Send map-servers fames ranking lists
+ *  Defaut fame list are 32B, (id+point+names)
+ *  S <len>.W <len bs + alchi>.W <len bs>.W <smith_rank>?B <alchi_rank>?B <taek_rank>?B
+ * @param fd: fd to send packet too (map-serv) if -1 send to all
+ * @return : 0 success
+ */
+int chmapif_send_fame_list(int fd){
+	int i, len = 8;
+	unsigned char buf[32000];
+
+	WBUFW(buf,0) = 0x2b1b;
+
+	for(i = 0; i < fame_list_size_smith && smith_fame_list[i].id; i++) {
+		memcpy(WBUFP(buf, len), &smith_fame_list[i], sizeof(struct fame_list));
+		len += sizeof(struct fame_list);
+	}
+	// add blacksmith's block length
+	WBUFW(buf, 6) = len;
+
+	for(i = 0; i < fame_list_size_chemist && chemist_fame_list[i].id; i++) {
+		memcpy(WBUFP(buf, len), &chemist_fame_list[i], sizeof(struct fame_list));
+		len += sizeof(struct fame_list);
+	}
+	// add alchemist's block length
+	WBUFW(buf, 4) = len;
+
+	for(i = 0; i < fame_list_size_taekwon && taekwon_fame_list[i].id; i++) {
+		memcpy(WBUFP(buf, len), &taekwon_fame_list[i], sizeof(struct fame_list));
+		len += sizeof(struct fame_list);
+	}
+	// add total packet length
+	WBUFW(buf, 2) = len;
+
+	if (fd != -1)
+		chmapif_send(fd, buf, len);
+	else
+		chmapif_sendall(buf, len);
+
+	return 0;
+}
+
+/**
+ * Send to map-servers the updated fame ranking lists
+ *  We actually just send this one when we only need to update rankpoint but pos didn't change
+ * @param type: ranking type
+ * @param index: position in the ranking
+ * @param fame: number of points
+ */
+void chmapif_update_fame_list(int type, int index, int fame) {
+	unsigned char buf[8];
+	WBUFW(buf,0) = 0x2b22;
+	WBUFB(buf,2) = type;
+	WBUFB(buf,3) = index;
+	WBUFL(buf,4) = fame;
+	chmapif_sendall(buf, 8);
+}
+
+/**
+ * Send to map-servers the users count on this char-serv, (meaning the total of all mapserv)
+ * @param users: number of players on this char-serv
+ */
+void chmapif_sendall_playercount(int users){
+	uint8 buf[6];
+	// send number of players to all map-servers
+	WBUFW(buf,0) = 0x2b00;
+	WBUFL(buf,2) = users;
+	chmapif_sendall(buf,6);
+}
+
+/**
+ * This function is called when the map-serv initialise is chrif interface.
+ * Map-serv sent us his map indexes so we can transfert a player from a map-serv to another when necessary
+ * We reply by sending back the char_serv_wisp_name  fame list and
+ * @param fd: wich fd to parse from
+ * @param id: wich map_serv id
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_getmapname(int fd, int id){
+	int j = 0, i = 0;
+	if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+		return 0;
+
+	//Retain what map-index that map-serv contains
+	memset(map_server[id].map, 0, sizeof(map_server[id].map));
+	for(i = 4; i < RFIFOW(fd,2); i += 4) {
+		map_server[id].map[j] = RFIFOW(fd,i);
+		j++;
+	}
+
+	ShowStatus("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n",
+				id, j, CONVIP(map_server[id].ip), map_server[id].port);
+	ShowStatus("Map-server %d loading complete.\n", id);
+
+	// send name for wisp to player
+	WFIFOHEAD(fd, 3 + NAME_LENGTH);
+	WFIFOW(fd,0) = 0x2afb;
+	WFIFOB(fd,2) = 0; //0 succes, 1:failure
+	memcpy(WFIFOP(fd,3), charserv_config.wisp_server_name, NAME_LENGTH);
+	WFIFOSET(fd,3+NAME_LENGTH);
+
+	chmapif_send_fame_list(fd); //Send fame list.
+
+	{
+		int x;
+		if (j == 0) {
+			ShowWarning("Map-server %d has NO maps.\n", id);
+		} else {
+			unsigned char buf[16384];
+			// Transmitting maps information to the other map-servers
+			WBUFW(buf,0) = 0x2b04;
+			WBUFW(buf,2) = j * 4 + 10;
+			WBUFL(buf,4) = htonl(map_server[id].ip);
+			WBUFW(buf,8) = htons(map_server[id].port);
+			memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 4);
+			chmapif_sendallwos(fd, buf, WBUFW(buf,2));
+		}
+		// Transmitting the maps of the other map-servers to the new map-server
+		for(x = 0; x < ARRAYLENGTH(map_server); x++) {
+			if (map_server[x].fd > 0 && x != id) {
+				WFIFOHEAD(fd,10 +4*ARRAYLENGTH(map_server[x].map));
+				WFIFOW(fd,0) = 0x2b04;
+				WFIFOL(fd,4) = htonl(map_server[x].ip);
+				WFIFOW(fd,8) = htons(map_server[x].port);
+				j = 0;
+				for(i = 0; i < ARRAYLENGTH(map_server[x].map); i++)
+					if (map_server[x].map[i])
+						WFIFOW(fd,10+(j++)*4) = map_server[x].map[i];
+				if (j > 0) {
+					WFIFOW(fd,2) = j * 4 + 10;
+					WFIFOSET(fd,WFIFOW(fd,2));
+				}
+			}
+		}
+	}
+	RFIFOSKIP(fd,RFIFOW(fd,2));
+	return 1;
+}
+
+/**
+ * Map-serv requesting to send the list of sc_data the player has saved
+ * @author [Skotlex]
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_askscdata(int fd){
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	{
+#ifdef ENABLE_SC_SAVING
+		int aid, cid;
+		aid = RFIFOL(fd,2);
+		cid = RFIFOL(fd,6);
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT type, tick, val1, val2, val3, val4 from `%s` WHERE `account_id` = '%d' AND `char_id`='%d'",
+			schema_config.scdata_db, aid, cid) )
+		{
+			Sql_ShowDebug(sql_handle);
+			return 1;
+		}
+		if( Sql_NumRows(sql_handle) > 0 )
+		{
+			struct status_change_data scdata;
+			int count;
+			char* data;
+
+			WFIFOHEAD(fd,14+50*sizeof(struct status_change_data));
+			WFIFOW(fd,0) = 0x2b1d;
+			WFIFOL(fd,4) = aid;
+			WFIFOL(fd,8) = cid;
+			for( count = 0; count < 50 && SQL_SUCCESS == Sql_NextRow(sql_handle); ++count )
+			{
+				Sql_GetData(sql_handle, 0, &data, NULL); scdata.type = atoi(data);
+				Sql_GetData(sql_handle, 1, &data, NULL); scdata.tick = atoi(data);
+				Sql_GetData(sql_handle, 2, &data, NULL); scdata.val1 = atoi(data);
+				Sql_GetData(sql_handle, 3, &data, NULL); scdata.val2 = atoi(data);
+				Sql_GetData(sql_handle, 4, &data, NULL); scdata.val3 = atoi(data);
+				Sql_GetData(sql_handle, 5, &data, NULL); scdata.val4 = atoi(data);
+				memcpy(WFIFOP(fd, 14+count*sizeof(struct status_change_data)), &scdata, sizeof(struct status_change_data));
+			}
+			if (count >= 50)
+				ShowWarning("Too many status changes for %d:%d, some of them were not loaded.\n", aid, cid);
+			if (count > 0)
+			{
+				WFIFOW(fd,2) = 14 + count*sizeof(struct status_change_data);
+				WFIFOW(fd,12) = count;
+				WFIFOSET(fd,WFIFOW(fd,2));
+			}
+		}
+		Sql_FreeResult(sql_handle);
+#endif
+		RFIFOSKIP(fd, 10);
+	}
+	return 1;
+}
+
+/**
+ * Map-serv sent us his new users count, updating info
+ * @param fd: wich fd to parse from
+ * @param id: wich map_serv id
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_getusercount(int fd, int id){
+	if (RFIFOREST(fd) < 4)
+		return 0;
+	if (RFIFOW(fd,2) != map_server[id].users) {
+		map_server[id].users = RFIFOW(fd,2);
+		ShowInfo("User Count: %d (Server: %d)\n", map_server[id].users, id);
+	}
+	RFIFOSKIP(fd, 4);
+	return 1;
+}
+
+/**
+ * Map-serv sent us all his users info, (aid and cid) so we can update online_char_db
+ * @param fd: wich fd to parse from
+ * @param id: wich map_serv id
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_regmapuser(int fd, int id){
+	if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2))
+		return 0;
+	{
+		//TODO: When data mismatches memory, update guild/party online/offline states.
+		int aid, cid, i;
+		struct online_char_data* character;
+		DBMap* online_char_db = char_get_onlinedb();
+
+		map_server[id].users = RFIFOW(fd,4);
+		online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown'
+		for(i = 0; i < map_server[id].users; i++) {
+			aid = RFIFOL(fd,6+i*8);
+			cid = RFIFOL(fd,6+i*8+4);
+			character = idb_ensure(online_char_db, aid, char_create_online_data);
+			if( character->server > -1 && character->server != id )
+			{
+				ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n",
+					character->account_id, character->char_id, character->server, id, aid, cid);
+				mapif_disconnectplayer(map_server[character->server].fd, character->account_id, character->char_id, 2);
+			}
+			character->server = id;
+			character->char_id = cid;
+		}
+		//If any chars remain in -2, they will be cleaned in the cleanup timer.
+		RFIFOSKIP(fd,RFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Map-serv request to save mmo_char_status in sql
+ * Receive character data from map-server for saving
+ * @param fd: wich fd to parse from
+ * @param id: wich map_serv id
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_reqsavechar(int fd, int id){
+	if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+            return 0;
+	{
+		int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2);
+		struct online_char_data* character;
+		DBMap* online_char_db = char_get_onlinedb();
+
+		if (size - 13 != sizeof(struct mmo_charstatus))
+		{
+			ShowError("parse_from_map (save-char): Size mismatch! %d != %d\n", size-13, sizeof(struct mmo_charstatus));
+			RFIFOSKIP(fd,size);
+			return 1;
+		}
+		//Check account only if this ain't final save. Final-save goes through because of the char-map reconnect
+		if (RFIFOB(fd,12) || RFIFOB(fd,13) || (
+			(character = (struct online_char_data*)idb_get(online_char_db, aid)) != NULL &&
+			character->char_id == cid))
+		{
+			struct mmo_charstatus char_dat;
+			memcpy(&char_dat, RFIFOP(fd,13), sizeof(struct mmo_charstatus));
+			char_mmo_char_tosql(cid, &char_dat);
+		} else {	//This may be valid on char-server reconnection, when re-sending characters that already logged off.
+			ShowError("parse_from_map (save-char): Received data for non-existant/offline character (%d:%d).\n", aid, cid);
+			char_set_char_online(id, cid, aid);
+		}
+
+		if (RFIFOB(fd,12))
+		{	//Flag, set character offline after saving. [Skotlex]
+			char_set_char_offline(cid, aid);
+			WFIFOHEAD(fd,10);
+			WFIFOW(fd,0) = 0x2b21; //Save ack only needed on final save.
+			WFIFOL(fd,2) = aid;
+			WFIFOL(fd,6) = cid;
+			WFIFOSET(fd,10);
+		}
+		RFIFOSKIP(fd,size);
+	}
+	return 1;
+}
+
+/**
+ * Player Requesting char-select from map_serv
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_authok(int fd){
+	if( RFIFOREST(fd) < 19 )
+		return 0;
+	else{
+		int account_id = RFIFOL(fd,2);
+		uint32 login_id1 = RFIFOL(fd,6);
+		uint32 login_id2 = RFIFOL(fd,10);
+		uint32 ip = RFIFOL(fd,14);
+		int version = RFIFOB(fd,18);
+		RFIFOSKIP(fd,19);
+
+		if( runflag != CHARSERVER_ST_RUNNING ){
+			WFIFOHEAD(fd,7);
+			WFIFOW(fd,0) = 0x2b03;
+			WFIFOL(fd,2) = account_id;
+			WFIFOB(fd,6) = 0;// not ok
+			WFIFOSET(fd,7);
+		}else{
+			struct auth_node* node;
+			DBMap*  auth_db = char_get_authdb();
+			DBMap* online_char_db = char_get_onlinedb();
+
+			// create temporary auth entry
+			CREATE(node, struct auth_node, 1);
+			node->account_id = account_id;
+			node->char_id = 0;
+			node->login_id1 = login_id1;
+			node->login_id2 = login_id2;
+			//node->sex = 0;
+			node->ip = ntohl(ip);
+			node->version = version; //upd version for mapserv
+			//node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server)
+			//node->gmlevel = 0;
+			idb_put(auth_db, account_id, node);
+
+			//Set char to "@ char select" in online db [Kevin]
+			char_set_charselect(account_id);
+			{
+				struct online_char_data* character = (struct online_char_data*)idb_get(online_char_db, account_id);
+				if( character != NULL ){
+					character->pincode_success = true;
+				}
+			}
+
+			WFIFOHEAD(fd,7);
+			WFIFOW(fd,0) = 0x2b03;
+			WFIFOL(fd,2) = account_id;
+			WFIFOB(fd,6) = 1;// ok
+			WFIFOSET(fd,7);
+		}
+	}
+	return 1;
+}
+
+//Request to save skill cooldown data
+int chmapif_parse_req_saveskillcooldown(int fd){
+	if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) )
+            return 0;
+	else {
+		int count, aid, cid;
+		aid = RFIFOL(fd,4);
+		cid = RFIFOL(fd,8);
+		count = RFIFOW(fd,12);
+		if( count > 0 )
+		{
+			struct skill_cooldown_data data;
+			StringBuf buf;
+			int i;
+
+			StringBuf_Init(&buf);
+			StringBuf_Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `skill`, `tick`) VALUES ", schema_config.skillcooldown_db);
+			for( i = 0; i < count; ++i )
+			{
+				memcpy(&data,RFIFOP(fd,14+i*sizeof(struct skill_cooldown_data)),sizeof(struct skill_cooldown_data));
+				if( i > 0 )
+					StringBuf_AppendStr(&buf, ", ");
+				StringBuf_Printf(&buf, "('%d','%d','%d','%d')", aid, cid, data.skill_id, data.tick);
+			}
+			if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
+				Sql_ShowDebug(sql_handle);
+			StringBuf_Destroy(&buf);
+		}
+		RFIFOSKIP(fd, RFIFOW(fd, 2));
+	}
+	return 1;
+}
+
+//Request skillcooldown data 0x2b0a
+int chmapif_parse_req_skillcooldown(int fd){
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	else {
+		int aid, cid;
+		aid = RFIFOL(fd,2);
+		cid = RFIFOL(fd,6);
+		RFIFOSKIP(fd, 10);
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT skill, tick FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'",
+			schema_config.skillcooldown_db, aid, cid) )
+		{
+			Sql_ShowDebug(sql_handle);
+			return 1;
+		}
+		if( Sql_NumRows(sql_handle) > 0 )
+		{
+			int count;
+			char* data;
+			struct skill_cooldown_data scd;
+
+			WFIFOHEAD(fd,14 + MAX_SKILLCOOLDOWN * sizeof(struct skill_cooldown_data));
+			WFIFOW(fd,0) = 0x2b0b;
+			WFIFOL(fd,4) = aid;
+			WFIFOL(fd,8) = cid;
+			for( count = 0; count < MAX_SKILLCOOLDOWN && SQL_SUCCESS == Sql_NextRow(sql_handle); ++count )
+			{
+				Sql_GetData(sql_handle, 0, &data, NULL); scd.skill_id = atoi(data);
+				Sql_GetData(sql_handle, 1, &data, NULL); scd.tick = atoi(data);
+				memcpy(WFIFOP(fd,14+count*sizeof(struct skill_cooldown_data)), &scd, sizeof(struct skill_cooldown_data));
+			}
+			if( count >= MAX_SKILLCOOLDOWN )
+				ShowWarning("Too many skillcooldowns for %d:%d, some of them were not loaded.\n", aid, cid);
+			if( count > 0 )
+			{
+				WFIFOW(fd,2) = 14 + count * sizeof(struct skill_cooldown_data);
+				WFIFOW(fd,12) = count;
+				WFIFOSET(fd,WFIFOW(fd,2));
+				//Clear the data once loaded.
+				if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", schema_config.skillcooldown_db, aid, cid) )
+					Sql_ShowDebug(sql_handle);
+			}
+		}
+		Sql_FreeResult(sql_handle);
+	}
+	return 1;
+}
+
+/**
+ * Player requesting to change map-serv
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_reqchangemapserv(int fd){
+	if (RFIFOREST(fd) < 39)
+		return 0;
+	{
+		int map_id, map_fd = -1;
+		struct mmo_charstatus* char_data;
+		struct mmo_charstatus char_dat;
+		DBMap* char_db_ = char_get_chardb();
+
+		map_id = char_search_mapserver(RFIFOW(fd,18), ntohl(RFIFOL(fd,24)), ntohs(RFIFOW(fd,28))); //Locate mapserver by ip and port.
+		if (map_id >= 0)
+			map_fd = map_server[map_id].fd;
+		//Char should just had been saved before this packet, so this should be safe. [Skotlex]
+		char_data = (struct mmo_charstatus*)uidb_get(char_db_,RFIFOL(fd,14));
+		if (char_data == NULL) {	//Really shouldn't happen.
+			char_mmo_char_fromsql(RFIFOL(fd,14), &char_dat, true);
+			char_data = (struct mmo_charstatus*)uidb_get(char_db_,RFIFOL(fd,14));
+		}
+
+		if( runflag == CHARSERVER_ST_RUNNING &&
+			session_isActive(map_fd) &&
+			char_data )
+		{	//Send the map server the auth of this player.
+			struct online_char_data* data;
+			struct auth_node* node;
+			DBMap*  auth_db = char_get_authdb();
+			DBMap* online_char_db = char_get_onlinedb();
+
+			int aid = RFIFOL(fd,2);
+
+			//Update the "last map" as this is where the player must be spawned on the new map server.
+			char_data->last_point.map = RFIFOW(fd,18);
+			char_data->last_point.x = RFIFOW(fd,20);
+			char_data->last_point.y = RFIFOW(fd,22);
+			char_data->sex = RFIFOB(fd,30);
+
+			// create temporary auth entry
+			CREATE(node, struct auth_node, 1);
+			node->account_id = aid;
+			node->char_id = RFIFOL(fd,14);
+			node->login_id1 = RFIFOL(fd,6);
+			node->login_id2 = RFIFOL(fd,10);
+			node->sex = RFIFOB(fd,30);
+			node->expiration_time = 0; // FIXME (this thing isn't really supported we could as well purge it instead of fixing)
+			node->ip = ntohl(RFIFOL(fd,31));
+			node->group_id = RFIFOL(fd,35);
+			node->changing_mapservers = 1;
+			idb_put(auth_db, aid, node);
+
+			data = idb_ensure(online_char_db, aid, char_create_online_data);
+			data->char_id = char_data->char_id;
+			data->server = map_id; //Update server where char is.
+
+			//Reply with an ack.
+			WFIFOHEAD(fd,30);
+			WFIFOW(fd,0) = 0x2b06;
+			memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28);
+			WFIFOSET(fd,30);
+		} else { //Reply with nak
+			WFIFOHEAD(fd,30);
+			WFIFOW(fd,0) = 0x2b06;
+			memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28);
+			WFIFOL(fd,6) = 0; //Set login1 to 0.
+			WFIFOSET(fd,30);
+		}
+		RFIFOSKIP(fd,39);
+	}
+	return 1;
+}
+
+/**
+ * Player requesting to remove friend from list
+ * Remove RFIFOL(fd,6) (friend_id) from RFIFOL(fd,2) (char_id) friend list
+ * @author [Ind]
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_askrmfriend(int fd){
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	{
+		int char_id, friend_id;
+		char_id = RFIFOL(fd,2);
+		friend_id = RFIFOL(fd,6);
+		if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d' AND `friend_id`='%d' LIMIT 1",
+			schema_config.friend_db, char_id, friend_id) ) {
+			Sql_ShowDebug(sql_handle);
+			return 1;
+		}
+		RFIFOSKIP(fd,10);
+	}
+	return 1;
+}
+
+/**
+ * Lookup to search if that char_id correspond to a name.
+ * Comming from map-serv to search on other map-serv
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_reqcharname(int fd){
+	if (RFIFOREST(fd) < 6)
+		return 0;
+
+	WFIFOHEAD(fd,30);
+	WFIFOW(fd,0) = 0x2b09;
+	WFIFOL(fd,2) = RFIFOL(fd,2);
+	char_loadName((int)RFIFOL(fd,2), (char*)WFIFOP(fd,6));
+	WFIFOSET(fd,30);
+
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+/**
+ * Forward an email update request to login-serv
+ * Map server send information to change an email of an account -> login-server
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_reqnewemail(int fd){
+	if (RFIFOREST(fd) < 86)
+		return 0;
+	if (chlogif_isconnected()) { // don't send request if no login-server
+		WFIFOHEAD(login_fd,86);
+		memcpy(WFIFOP(login_fd,0), RFIFOP(fd,0),86); // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B
+		WFIFOW(login_fd,0) = 0x2722;
+		WFIFOSET(login_fd,86);
+	}
+	RFIFOSKIP(fd, 86);
+	return 1;
+}
+
+/**
+ * Forward a change of status for account to login-serv
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_fwlog_changestatus(int fd){
+	if (RFIFOREST(fd) < 44)
+		return 0;
+	else {
+		int result = 0; // 0-login-server request done, 1-player not found, 2-gm level too low, 3-login-server offline, 4-current group level > VIP group level
+		char esc_name[NAME_LENGTH*2+1];
+		char answer = true;
+
+		int aid = RFIFOL(fd,2); // account_id of who ask (-1 if server itself made this request)
+		const char* name = (char*)RFIFOP(fd,6); // name of the target character
+		int operation = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex, 6-vip, 7-bank
+		int32 timediff = RFIFOL(fd,32);
+		int val1 = RFIFOL(fd,36);
+		int val2 = RFIFOL(fd,40);
+		RFIFOSKIP(fd,44);
+
+		Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`name`,`char_id`,`unban_time` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, esc_name) )
+			Sql_ShowDebug(sql_handle);
+		else if( Sql_NumRows(sql_handle) == 0 ) {
+			result = 1; // 1-player not found
+		}
+		else if( SQL_SUCCESS != Sql_NextRow(sql_handle) ) {
+			Sql_ShowDebug(sql_handle);
+			result = 1;
+		} else {
+			char name[NAME_LENGTH];
+			int account_id;
+			char* data;
+
+			Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data);
+			Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(name, data, sizeof(name));
+			Sql_FreeResult(sql_handle);
+
+			if(!chlogif_isconnected())
+				result = 3; // 3-login-server offline
+			//FIXME: need to move this check to login server [ultramage]
+			//	if( acc != -1 && isGM(acc) < isGM(account_id) )
+			//		result = 2; // 2-gm level too low
+			else {
+				switch( operation ) {
+				case 1: // block
+					WFIFOHEAD(login_fd,10);
+					WFIFOW(login_fd,0) = 0x2724;
+					WFIFOL(login_fd,2) = account_id;
+					WFIFOL(login_fd,6) = 5; // new account status
+					WFIFOSET(login_fd,10);
+				break;
+				case 2: // ban
+					WFIFOHEAD(login_fd,10);
+					WFIFOW(login_fd, 0) = 0x2725;
+					WFIFOL(login_fd, 2) = account_id;
+					WFIFOL(login_fd, 6) = timediff;
+					WFIFOSET(login_fd,10);
+				break;
+				case 3: // unblock
+					WFIFOHEAD(login_fd,10);
+					WFIFOW(login_fd,0) = 0x2724;
+					WFIFOL(login_fd,2) = account_id;
+					WFIFOL(login_fd,6) = 0; // new account status
+					WFIFOSET(login_fd,10);
+				break;
+				case 4: // unban
+					WFIFOHEAD(login_fd,6);
+					WFIFOW(login_fd,0) = 0x272a;
+					WFIFOL(login_fd,2) = account_id;
+					WFIFOSET(login_fd,6);
+				break;
+				case 5: // changesex
+					answer = false;
+					WFIFOHEAD(login_fd,6);
+					WFIFOW(login_fd,0) = 0x2727;
+					WFIFOL(login_fd,2) = account_id;
+					WFIFOSET(login_fd,6);
+				break;
+				case 6:
+					answer = (val1&4); // vip_req val1=type, &1 login send return, &2 update timestamp, &4 map send answer
+					chlogif_reqvipdata(account_id, val1, timediff, fd);
+					break;
+				case 7:
+					answer = (val1&1); //val&1 request answer, val1&2 save data
+					chlogif_BankingReq(aid, val1, val2);
+					break;
+				} //end switch operation
+			} //login is connected
+		}
+
+		// send answer if a player asks, not if the server asks
+		if( aid != -1 && answer) { // Don't send answer for changesex
+			WFIFOHEAD(fd,34);
+			WFIFOW(fd, 0) = 0x2b0f;
+			WFIFOL(fd, 2) = aid;
+			safestrncpy((char*)WFIFOP(fd,6), name, NAME_LENGTH);
+			WFIFOW(fd,30) = operation;
+			WFIFOW(fd,32) = result;
+			WFIFOSET(fd,34);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Transmit the acknolegement of divorce of partner_id1 and partner_id2
+ * Update the list associated and transmit the new ranking
+ * @param partner_id1: char id1 divorced
+ * @param partner_id2: char id2 divorced
+ */
+void chmapif_send_ackdivorce(int partner_id1, int partner_id2){
+	unsigned char buf[11];
+	WBUFW(buf,0) = 0x2b12;
+	WBUFL(buf,2) = partner_id1;
+	WBUFL(buf,6) = partner_id2;
+	chmapif_sendall(buf,10);
+}
+
+/**
+ * Received a divorce request
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_reqdivorce(int fd){
+	if( RFIFOREST(fd) < 10 )
+		return 0;
+	char_divorce_char_sql(RFIFOL(fd,2), RFIFOL(fd,6));
+	RFIFOSKIP(fd,10);
+	return 1;
+}
+
+/**
+ * Receive rates of this map index
+ * @author [Wizputer]
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_updmapinfo(int fd){
+	if( RFIFOREST(fd) < 14 )
+		return 0;
+	{
+		char esc_server_name[sizeof(charserv_config.server_name)*2+1];
+		Sql_EscapeString(sql_handle, esc_server_name, charserv_config.server_name);
+		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` SET `index`='%d',`name`='%s',`exp`='%d',`jexp`='%d',`drop`='%d'",
+			schema_config.ragsrvinfo_db, fd, esc_server_name, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)) )
+			Sql_ShowDebug(sql_handle);
+		RFIFOSKIP(fd,14);
+	}
+	return 1;
+}
+
+/**
+ *  Character disconnected set online 0
+ * @author [Wizputer]
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_setcharoffline(int fd){
+	if (RFIFOREST(fd) < 6)
+		return 0;
+	char_set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6));
+	RFIFOSKIP(fd,10);
+	return 1;
+}
+
+
+/**
+ * Reset all chars to offline
+ * @author [Wizputer]
+ * @param fd: wich fd to parse from
+ * @param id: wich map_serv id
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_setalloffline(int fd, int id){
+	char_set_all_offline(id);
+	RFIFOSKIP(fd,2);
+	return 1;
+}
+
+/**
+ * Character set online
+ * @author [Wizputer]
+ * @param fd: wich fd to parse from
+ * @param id: wich map_serv id
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_setcharonline(int fd, int id){
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	char_set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6));
+	RFIFOSKIP(fd,10);
+	return 1;
+}
+
+/**
+ * Build and send fame ranking lists
+ * @author [DracoRPG]
+ * @param fd: wich fd to parse from
+ * @param id: wich map_serv id
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_reqfamelist(int fd){
+	if (RFIFOREST(fd) < 2)
+		return 0;
+	char_read_fame_list();
+	chmapif_send_fame_list(-1);
+	RFIFOSKIP(fd,2);
+	return 1;
+}
+
+/**
+ * Request to save status change data.s
+ * @author [Skotlex]
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_save_scdata(int fd){
+	if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+		return 0;
+	{
+#ifdef ENABLE_SC_SAVING
+		int count, aid, cid;
+
+		aid = RFIFOL(fd, 4);
+		cid = RFIFOL(fd, 8);
+		count = RFIFOW(fd, 12);
+
+		// Whatever comes from the mapserver, now is the time to drop previous entries
+		if( Sql_Query( sql_handle, "DELETE FROM `%s` where `account_id` = %d and `char_id` = %d;", schema_config.scdata_db, aid, cid ) != SQL_SUCCESS ){
+				Sql_ShowDebug( sql_handle );
+		}
+		else if( count > 0 )
+		{
+			struct status_change_data data;
+			StringBuf buf;
+			int i;
+
+			StringBuf_Init(&buf);
+			StringBuf_Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ", schema_config.scdata_db);
+			for( i = 0; i < count; ++i )
+			{
+				memcpy (&data, RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data));
+				if( i > 0 )
+					StringBuf_AppendStr(&buf, ", ");
+				StringBuf_Printf(&buf, "('%d','%d','%hu','%d','%d','%d','%d','%d')", aid, cid,
+					data.type, data.tick, data.val1, data.val2, data.val3, data.val4);
+			}
+			if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
+				Sql_ShowDebug(sql_handle);
+			StringBuf_Destroy(&buf);
+		}
+#endif
+		RFIFOSKIP(fd, RFIFOW(fd, 2));
+	}
+	return 1;
+}
+
+/**
+ * map-server keep alive packet, awnser back map that we alive as well
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_keepalive(int fd){
+	WFIFOHEAD(fd,2);
+	WFIFOW(fd,0) = 0x2b24;
+	WFIFOSET(fd,2);
+	RFIFOSKIP(fd,2);
+	return 1;
+}
+
+/**
+ * auth request from map-server
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_reqauth(int fd, int id){
+    if (RFIFOREST(fd) < 20)
+            return 0;
+
+    {
+        int account_id;
+        int char_id;
+        int login_id1;
+        char sex;
+        uint32 ip;
+        struct auth_node* node;
+        struct mmo_charstatus* cd;
+        struct mmo_charstatus char_dat;
+        bool autotrade = false;
+
+        DBMap*  auth_db = char_get_authdb();
+        DBMap* char_db_ = char_get_chardb();
+
+        account_id = RFIFOL(fd,2);
+        char_id    = RFIFOL(fd,6);
+        login_id1  = RFIFOL(fd,10);
+        sex        = RFIFOB(fd,14);
+        ip         = ntohl(RFIFOL(fd,15));
+        autotrade = RFIFOB(fd,19);
+        RFIFOSKIP(fd,20);
+
+        node = (struct auth_node*)idb_get(auth_db, account_id);
+        cd = (struct mmo_charstatus*)uidb_get(char_db_,char_id);
+        if( cd == NULL )
+        {	//Really shouldn't happen.
+                char_mmo_char_fromsql(char_id, &char_dat, true);
+                cd = (struct mmo_charstatus*)uidb_get(char_db_,char_id);
+        }
+        if( runflag == CHARSERVER_ST_RUNNING && autotrade && cd ){
+            uint32 mmo_charstatus_len = sizeof(struct mmo_charstatus) + 25;
+            cd->sex = sex;
+
+            WFIFOHEAD(fd,mmo_charstatus_len);
+            WFIFOW(fd,0) = 0x2afd;
+            WFIFOW(fd,2) = mmo_charstatus_len;
+            WFIFOL(fd,4) = account_id;
+            WFIFOL(fd,8) = 0;
+            WFIFOL(fd,12) = 0;
+            WFIFOL(fd,16) = 0;
+            WFIFOL(fd,20) = 0;
+            WFIFOB(fd,24) = 0;
+            memcpy(WFIFOP(fd,25), cd, sizeof(struct mmo_charstatus));
+            WFIFOSET(fd, WFIFOW(fd,2));
+
+            char_set_char_online(id, char_id, account_id);
+        } else if( runflag == CHARSERVER_ST_RUNNING &&
+            cd != NULL &&
+            node != NULL &&
+            node->account_id == account_id &&
+            node->char_id == char_id &&
+            node->login_id1 == login_id1 &&
+            node->sex == sex /*&&
+            node->ip == ip*/ )
+        {// auth ok
+            uint32 mmo_charstatus_len = sizeof(struct mmo_charstatus) + 25;
+            cd->sex = sex;
+
+            WFIFOHEAD(fd,mmo_charstatus_len);
+            WFIFOW(fd,0) = 0x2afd;
+            WFIFOW(fd,2) = mmo_charstatus_len;
+            WFIFOL(fd,4) = account_id;
+            WFIFOL(fd,8) = node->login_id1;
+            WFIFOL(fd,12) = node->login_id2;
+            WFIFOL(fd,16) = (uint32)node->expiration_time; // FIXME: will wrap to negative after "19-Jan-2038, 03:14:07 AM GMT"
+            WFIFOL(fd,20) = node->group_id;
+            WFIFOB(fd,24) = node->changing_mapservers;
+            memcpy(WFIFOP(fd,25), cd, sizeof(struct mmo_charstatus));
+            WFIFOSET(fd, WFIFOW(fd,2));
+
+            // only use the auth once and mark user online
+            idb_remove(auth_db, account_id);
+            char_set_char_online(id, char_id, account_id);
+        } else {// auth failed
+                WFIFOHEAD(fd,19);
+                WFIFOW(fd,0) = 0x2b27;
+                WFIFOL(fd,2) = account_id;
+                WFIFOL(fd,6) = char_id;
+                WFIFOL(fd,10) = login_id1;
+                WFIFOB(fd,14) = sex;
+                WFIFOL(fd,15) = htonl(ip);
+                WFIFOSET(fd,19);
+        }
+    }
+    return 1;
+}
+
+/**
+ * ip address update
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_updmapip(int fd, int id){
+	if (RFIFOREST(fd) < 6) 
+            return 0;
+	map_server[id].ip = ntohl(RFIFOL(fd, 2));
+	ShowInfo("Updated IP address of map-server #%d to %d.%d.%d.%d.\n", id, CONVIP(map_server[id].ip));
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+/**
+ *  transmit emu usage for anom stats
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_fw_configstats(int fd){
+	if( RFIFOREST(fd) < RFIFOW(fd,4) )
+		return 0;/* packet wasn't fully received yet (still fragmented) */
+	else {
+		int sfd;/* stat server fd */
+		RFIFOSKIP(fd, 2);/* we skip first 2 bytes which are the 0x3008, so we end up with a buffer equal to the one we send */
+
+		if( (sfd = make_connection(host2ip("stats.rathena.org"),(uint16)25421,true,10) ) == -1 ) {
+			RFIFOSKIP(fd, RFIFOW(fd,2) );/* skip this packet */
+			return 0;/* connection not possible, we drop the report */
+		}
+
+		session[sfd]->flag.server = 1;/* to ensure we won't drop our own packet */
+		WFIFOHEAD(sfd, RFIFOW(fd,2) );
+		memcpy((char*)WFIFOP(sfd,0), (char*)RFIFOP(fd, 0), RFIFOW(fd,2));
+		WFIFOSET(sfd, RFIFOW(fd,2) );
+		flush_fifo(sfd);
+		do_close(sfd);
+		RFIFOSKIP(fd, RFIFOW(fd,2) );/* skip this packet */
+	}
+	return 1;
+}
+
+/**
+ * Received an update of fame point  for char_id cid
+ * Update the list associated and transmit the new ranking
+ * @param fd: wich fd to parse from
+ * @return : 0 not enough data received, 1 success
+ */
+int chmapif_parse_updfamelist(int fd){
+    if (RFIFOREST(fd) < 11)
+        return 0;
+    {
+            int cid = RFIFOL(fd, 2);
+            int fame = RFIFOL(fd, 6);
+            char type = RFIFOB(fd, 10);
+            int size;
+            struct fame_list* list;
+            int player_pos;
+            int fame_pos;
+
+            switch(type)
+            {
+                    case 1:  size = fame_list_size_smith;   list = smith_fame_list;   break;
+                    case 2:  size = fame_list_size_chemist; list = chemist_fame_list; break;
+                    case 3:  size = fame_list_size_taekwon; list = taekwon_fame_list; break;
+                    default: size = 0;                      list = NULL;              break;
+            }
+
+            ARR_FIND(0, size, player_pos, list[player_pos].id == cid);// position of the player
+            ARR_FIND(0, size, fame_pos, list[fame_pos].fame <= fame);// where the player should be
+
+            if( player_pos == size && fame_pos == size )
+                    ;// not on list and not enough fame to get on it
+            else if( fame_pos == player_pos )
+            {// same position
+                    list[player_pos].fame = fame;
+                    chmapif_update_fame_list(type, player_pos, fame);
+            }
+            else
+            {// move in the list
+                    if( player_pos == size )
+                    {// new ranker - not in the list
+                            ARR_MOVE(size - 1, fame_pos, list, struct fame_list);
+                            list[fame_pos].id = cid;
+                            list[fame_pos].fame = fame;
+                            char_loadName(cid, list[fame_pos].name);
+                    }
+                    else
+                    {// already in the list
+                            if( fame_pos == size )
+                                    --fame_pos;// move to the end of the list
+                            ARR_MOVE(player_pos, fame_pos, list, struct fame_list);
+                            list[fame_pos].fame = fame;
+                    }
+                    chmapif_send_fame_list(-1);
+            }
+
+            RFIFOSKIP(fd,11);
+    }
+    return 1;
+}
+
+//HZ 0x2b29 <aid>L <bank_vault>L
+int chmapif_BankingAck(int32 account_id, int32 bank_vault){
+	unsigned char buf[11];
+	WBUFW(buf,0) = 0x2b29;
+	WBUFL(buf,2) = account_id;
+	WBUFL(buf,6) = bank_vault;
+	chmapif_sendall(buf, 10); //inform all maps-attached
+	return 1;
+}
+
+
+/*
+ * HZ 0x2b2b
+ * Transmist vip data to mapserv
+ */
+int chmapif_vipack(int mapfd, uint32 aid, uint32 vip_time, uint8 isvip, uint8 isgm, uint32 groupid) {
+#ifdef VIP_ENABLE
+	uint8 buf[16];
+	WBUFW(buf,0) = 0x2b2b;
+	WBUFL(buf,2) = aid;
+	WBUFL(buf,6) = vip_time;
+	WBUFB(buf,10) = isvip;
+	WBUFB(buf,11) = isgm;
+	WBUFL(buf,12) = groupid;
+	chmapif_send(mapfd,buf,16);  // inform the mapserv back
+#endif
+	return 0;
+}
+
+int chmapif_parse_reqcharban(int fd){
+	if (RFIFOREST(fd) < 10+NAME_LENGTH)
+		return 0;
+	else {
+		//int aid = RFIFOL(fd,2); aid of player who as requested the ban
+		int timediff = RFIFOL(fd,6);
+		const char* name = (char*)RFIFOP(fd,10); // name of the target character
+		RFIFOSKIP(fd,10+NAME_LENGTH);
+
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`char_id`,`unban_time` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, name) )
+			Sql_ShowDebug(sql_handle);
+		else if( Sql_NumRows(sql_handle) == 0 ){
+			return 1; // 1-player not found
+		}
+		else if( SQL_SUCCESS != Sql_NextRow(sql_handle) ){
+			Sql_ShowDebug(sql_handle);
+			Sql_FreeResult(sql_handle);
+			return 1;
+		} else {
+			int t_cid=0,t_aid=0;
+			char* data;
+			time_t unban_time;
+			time_t now = time(NULL);
+			SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
+
+			Sql_GetData(sql_handle, 0, &data, NULL); t_aid = atoi(data);
+			Sql_GetData(sql_handle, 1, &data, NULL); t_cid = atoi(data);
+			Sql_GetData(sql_handle, 2, &data, NULL); unban_time = atol(data);
+			Sql_FreeResult(sql_handle);
+
+			if(timediff<0 && unban_time==0) 
+				return 1; //attemp to reduce time of a non banned account ?!?
+			else if(unban_time<now) unban_time=now; //new entry
+			unban_time += timediff; //alterate the time
+			if( unban_time < now ) unban_time=0; //we have totally reduce the time
+
+			if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
+					  "UPDATE `%s` SET `unban_time` = ? WHERE `char_id` = ? LIMIT 1",
+					  schema_config.char_db)
+				|| SQL_SUCCESS != SqlStmt_BindParam(stmt,  0, SQLDT_LONG,   (void*)&unban_time,   sizeof(unban_time))
+				|| SQL_SUCCESS != SqlStmt_BindParam(stmt,  1, SQLDT_INT,    (void*)&t_cid,     sizeof(t_cid))
+				|| SQL_SUCCESS != SqlStmt_Execute(stmt)
+
+				) {
+				SqlStmt_ShowDebug(stmt);
+				SqlStmt_Free(stmt);
+				return 1;
+			}
+			SqlStmt_Free(stmt);
+
+			// condition applies; send to all map-servers to disconnect the player
+			if( unban_time > now ) {
+					unsigned char buf[11];
+					WBUFW(buf,0) = 0x2b14;
+					WBUFL(buf,2) = t_cid;
+					WBUFB(buf,6) = 2;
+					WBUFL(buf,7) = (unsigned int)unban_time;
+					chmapif_sendall(buf, 11);
+					// disconnect player if online on char-server
+					char_disconnect_player(t_aid);
+			}
+		}
+	}
+	return 1;
+}
+
+int chmapif_parse_reqcharunban(int fd){
+	if (RFIFOREST(fd) < 6)
+            return 0;
+	else {
+		int cid = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+
+		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `unban_time` = '0' WHERE `char_id` = '%d' LIMIT 1", schema_config.char_db, cid) ) {
+			Sql_ShowDebug(sql_handle);
+			return 1;
+		}
+	}
+	return 1;
+}
+
+/** [Cydh]
+* Get bonus_script data(s) from table to load
+* @param fd
+*/
+int chmapif_bonus_script_get(int fd) {
+	if (RFIFOREST(fd) < 6)
+            return 0;
+	else {
+		int cid;
+		cid = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+
+		if (SQL_ERROR == Sql_Query(sql_handle,"SELECT `script`, `tick`, `flag`, `type`, `icon` FROM `%s` WHERE `char_id`='%d'",
+			schema_config.bonus_script_db,cid))
+		{
+			Sql_ShowDebug(sql_handle);
+			return 1;
+		}
+		if (Sql_NumRows(sql_handle) > 0) {
+			struct bonus_script_data bsdata;
+			int count;
+			char *data;
+
+			WFIFOHEAD(fd,10+50*sizeof(struct bonus_script_data));
+			WFIFOW(fd,0) = 0x2b2f;
+			WFIFOL(fd,4) = cid;
+			for (count = 0; count < 20 && SQL_SUCCESS == Sql_NextRow(sql_handle); ++count) {
+				Sql_GetData(sql_handle,0,&data,NULL); memcpy(bsdata.script,data,strlen(data)+1);
+				Sql_GetData(sql_handle,1,&data,NULL); bsdata.tick = atoi(data);
+				Sql_GetData(sql_handle,2,&data,NULL); bsdata.flag = atoi(data);
+				Sql_GetData(sql_handle,3,&data,NULL); bsdata.type = atoi(data);
+				Sql_GetData(sql_handle,4,&data,NULL); bsdata.icon = atoi(data);
+				memcpy(WFIFOP(fd,10+count*sizeof(struct bonus_script_data)),&bsdata,sizeof(struct bonus_script_data));
+			}
+			if (count >= 20)
+				ShowWarning("Too many bonus_script for %d, some of them were not loaded.\n",cid);
+			if (count > 0) {
+				WFIFOW(fd,2) = 10 + count*sizeof(struct bonus_script_data);
+				WFIFOW(fd,8) = count;
+				WFIFOSET(fd,WFIFOW(fd,2));
+
+				//Clear the data once loaded.
+				if (SQL_ERROR == Sql_Query(sql_handle,"DELETE FROM `%s` WHERE `char_id`='%d'",schema_config.bonus_script_db,cid))
+					Sql_ShowDebug(sql_handle);
+				ShowInfo("Loaded %d bonus_script for char_id: %d\n",count,cid);
+			}
+		}
+		Sql_FreeResult(sql_handle);
+	}
+	return 1;
+}
+
+/** [Cydh]
+* Save bonus_script data(s) to the table
+* @param fd
+*/
+int chmapif_bonus_script_save(int fd) {
+	if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+            return 0;
+	else {
+		int count, cid;
+
+		cid = RFIFOL(fd,4);
+		count = RFIFOW(fd,8);
+
+		if (count > 0) {
+			struct bonus_script_data bs;
+			StringBuf buf;
+			int i;
+			char esc_script[MAX_BONUS_SCRIPT_LENGTH] = "";
+
+			StringBuf_Init(&buf);
+			StringBuf_Printf(&buf,"INSERT INTO `%s` (`char_id`, `script`, `tick`, `flag`, `type`, `icon`) VALUES ",schema_config.bonus_script_db);
+			for (i = 0; i < count; ++i) {
+				memcpy(&bs,RFIFOP(fd,10+i*sizeof(struct bonus_script_data)),sizeof(struct bonus_script_data));
+				Sql_EscapeString(sql_handle,esc_script,bs.script);
+				if (i > 0)
+					StringBuf_AppendStr(&buf,", ");
+				StringBuf_Printf(&buf,"('%d','%s','%d','%d','%d','%d')",cid,esc_script,bs.tick,bs.flag,bs.type,bs.icon);
+			}
+			if (SQL_ERROR == Sql_QueryStr(sql_handle,StringBuf_Value(&buf)))
+				Sql_ShowDebug(sql_handle);
+			StringBuf_Destroy(&buf);
+			ShowInfo("Saved %d bonus_script for char_id: %d\n",count,cid);
+		}
+		RFIFOSKIP(fd,RFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Entry point from map-server to char-server.
+ * Function that checks incoming command, then splits it to the correct handler.
+ * If not found any hander here transmis packet to inter
+ * @param fd: file descriptor to parse, (link to map-serv)
+ * @return 0=invalid server,marked for disconnection,unknow packet; 1=success
+ */
+int chmapif_parse(int fd){
+	int id; //mapserv id
+        
+	ARR_FIND( 0, ARRAYLENGTH(map_server), id, map_server[id].fd == fd );
+	if( id == ARRAYLENGTH(map_server) )
+	{// not a map server
+		ShowDebug("parse_frommap: Disconnecting invalid session #%d (is not a map-server)\n", fd);
+		do_close(fd);
+		return 0;
+	}
+	if( session[fd]->flag.eof )
+	{
+		do_close(fd);
+		map_server[id].fd = -1;
+		chmapif_on_disconnect(id);
+		return 0;
+	}
+
+	while(RFIFOREST(fd) >= 2){
+		int next=1;
+		switch(RFIFOW(fd,0)){
+            case 0x2736: next=chmapif_parse_updmapip(fd,id); break;
+            case 0x2afa: next=chmapif_parse_getmapname(fd,id); break;
+            case 0x2afc: next=chmapif_parse_askscdata(fd); break;
+            case 0x2afe: next=chmapif_parse_getusercount(fd,id); break; //get nb user
+            case 0x2aff: next=chmapif_parse_regmapuser(fd,id); break; //register users
+            case 0x2b01: next=chmapif_parse_reqsavechar(fd,id); break;
+            case 0x2b02: next=chmapif_parse_authok(fd); break;
+            case 0x2b05: next=chmapif_parse_reqchangemapserv(fd); break;
+            case 0x2b07: next=chmapif_parse_askrmfriend(fd); break;
+            case 0x2b08: next=chmapif_parse_reqcharname(fd); break;
+            case 0x2b0a: next=chmapif_parse_req_skillcooldown(fd); break;
+            case 0x2b0c: next=chmapif_parse_reqnewemail(fd); break;
+            case 0x2b0e: next=chmapif_parse_fwlog_changestatus(fd); break;
+            case 0x2b10: next=chmapif_parse_updfamelist(fd); break;
+            case 0x2b11: next=chmapif_parse_reqdivorce(fd); break;
+            case 0x2b15: next=chmapif_parse_req_saveskillcooldown(fd); break;
+            case 0x2b16: next=chmapif_parse_updmapinfo(fd); break;
+            case 0x2b17: next=chmapif_parse_setcharoffline(fd); break;
+            case 0x2b18: next=chmapif_parse_setalloffline(fd,id); break;
+            case 0x2b19: next=chmapif_parse_setcharonline(fd,id); break;
+            case 0x2b1a: next=chmapif_parse_reqfamelist(fd); break;
+            case 0x2b1c: next=chmapif_parse_save_scdata(fd); break;
+            case 0x2b23: next=chmapif_parse_keepalive(fd); break;
+            case 0x2b26: next=chmapif_parse_reqauth(fd,id); break;
+            case 0x2b28: chmapif_parse_reqcharban(fd); break; //charban
+            case 0x2b2a: chmapif_parse_reqcharunban(fd); break; //charunban
+            //case 0x2b2c: /*free*/; break;
+            case 0x2b2d: chmapif_bonus_script_get(fd); break; //Load data
+            case 0x2b2e: chmapif_bonus_script_save(fd); break;//Save data
+            case 0x3008: next=chmapif_parse_fw_configstats(fd); break;
+            default:
+            {
+                    // inter server - packet
+                    int r = inter_parse_frommap(fd);
+                    if (r == 1) break;		// processed
+                    if (r == 2) return 0;	// need more packet
+                    // no inter server packet. no char server packet -> disconnect
+                    ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0));
+                    set_eof(fd);
+                    return 0;
+            }
+		} // switch
+		if(next==0) return 0; //avoid processing rest of packet
+	} // while
+	return 1;
+}
+
+
+// Initialization process (currently only initialization inter_mapif)
+int chmapif_init(int fd){
+	return inter_mapif_init(fd);
+}
+
+/**
+ * Initializes a server structure.
+ * @param id: id of map-serv (should be >0, FIXME)
+ */
+void chmapif_server_init(int id) {
+	memset(&map_server[id], 0, sizeof(map_server[id]));
+	map_server[id].fd = -1;
+}
+
+/**
+ * Destroys a server structure.
+ * @param id: id of map-serv (should be >0, FIXME)
+ */
+void chmapif_server_destroy(int id){
+	if( map_server[id].fd == -1 ){
+		do_close(map_server[id].fd);
+		map_server[id].fd = -1;
+	}
+}
+
+/**
+ * chmapif constructor
+ *  Initialisation, function called at start of the char-serv.
+ */
+void do_init_chmapif(void){
+	int i;
+	for( i = 0; i < ARRAYLENGTH(map_server); ++i )
+		chmapif_server_init(i);
+}
+
+/**
+ * Resets all the data related to a server.
+ *  Actually destroys then recreates the struct.
+ * @param id: id of map-serv (should be >0, FIXME)
+ */
+void chmapif_server_reset(int id){
+	int i,j;
+	unsigned char buf[16384];
+	int fd = map_server[id].fd;
+	DBMap* online_char_db = char_get_onlinedb();
+
+	//Notify other map servers that this one is gone. [Skotlex]
+	WBUFW(buf,0) = 0x2b20;
+	WBUFL(buf,4) = htonl(map_server[id].ip);
+	WBUFW(buf,8) = htons(map_server[id].port);
+	j = 0;
+	for(i = 0; i < MAX_MAP_PER_SERVER; i++)
+		if (map_server[id].map[i])
+			WBUFW(buf,10+(j++)*4) = map_server[id].map[i];
+	if (j > 0) {
+		WBUFW(buf,2) = j * 4 + 10;
+		chmapif_sendallwos(fd, buf, WBUFW(buf,2));
+	}
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `index`='%d'", schema_config.ragsrvinfo_db, map_server[id].fd) )
+		Sql_ShowDebug(sql_handle);
+	online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server.
+	chmapif_server_destroy(id);
+	chmapif_server_init(id);
+}
+
+
+/**
+ * Called when the connection to Map Server is disconnected.
+ * @param id: id of map-serv (should be >0, FIXME)
+ */
+void chmapif_on_disconnect(int id){
+	ShowStatus("Map-server #%d has disconnected.\n", id);
+	chmapif_server_reset(id);
+}
+
+
+/**
+ * chmapif destructor
+ *  dealloc..., function called at exit of the char-serv
+ */
+void do_final_chmapif(void){
+	int i;
+	for( i = 0; i < ARRAYLENGTH(map_server); ++i )
+		chmapif_server_destroy(i);
+}

+ 73 - 0
src/char/char_mapif.h

@@ -0,0 +1,73 @@
+/**
+ * @file char_mapif.h
+ * Module purpose is to handle incoming and outgoing requests with map-server.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+
+#ifndef CHAR_MAPIF_H
+#define	CHAR_MAPIF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+int chmapif_sendall(unsigned char *buf, unsigned int len);
+int chmapif_sendallwos(int sfd, unsigned char *buf, unsigned int len);
+int chmapif_send(int fd, unsigned char *buf, unsigned int len);
+int chmapif_send_fame_list(int fd);
+void chmapif_update_fame_list(int type, int index, int fame);
+void chmapif_sendall_playercount(int users);
+int chmapif_parse_getmapname(int fd, int id);
+int chmapif_parse_askscdata(int fd);
+int chmapif_parse_getusercount(int fd, int id);
+int chmapif_parse_regmapuser(int fd, int id);
+int chmapif_parse_reqsavechar(int fd, int id);
+int chmapif_parse_authok(int fd);
+int chmapif_parse_req_saveskillcooldown(int fd);
+int chmapif_parse_req_skillcooldown(int fd);
+int chmapif_parse_reqchangemapserv(int fd);
+int chmapif_parse_askrmfriend(int fd);
+int chmapif_parse_reqcharname(int fd);
+int chmapif_parse_reqnewemail(int fd);
+int chmapif_parse_fwlog_changestatus(int fd);
+int chmapif_parse_updfamelist(int fd);
+void chmapif_send_ackdivorce(int partner_id1, int partner_id2);
+int chmapif_parse_reqdivorce(int fd);
+int chmapif_parse_updmapinfo(int fd);
+int chmapif_parse_setcharoffline(int fd);
+int chmapif_parse_setalloffline(int fd, int id);
+int chmapif_parse_setcharonline(int fd, int id);
+int chmapif_parse_reqfamelist(int fd);
+int chmapif_parse_save_scdata(int fd);
+int chmapif_parse_keepalive(int fd);
+int chmapif_parse_reqauth(int fd, int id);
+int chmapif_parse_updmapip(int fd, int id);
+int chmapif_parse_fw_configstats(int fd);
+
+int chmapif_BankingAck(int32 account_id, int32 bank_vault);
+int chmapif_vipack(int mapfd, uint32 aid, uint32 vip_time, uint8 isvip, uint8 isgm, uint32 groupid);
+int chmapif_parse_reqcharban(int fd);
+int chmapif_parse_reqcharunban(int fd);
+int chmapif_bonus_script_get(int fd);
+int chmapif_bonus_script_save(int fd);
+
+int chmapif_parse(int fd);
+int chmapif_init(int fd);
+void chmapif_server_init(int id);
+void chmapif_server_destroy(int id);
+void do_init_chmapif(void);
+void chmapif_server_reset(int id);
+void chmapif_on_disconnect(int id);
+void do_final_chmapif(void);
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* CHAR_MAPIF_H */
+

+ 6 - 5
src/char/int_auction.c

@@ -10,6 +10,7 @@
 #include "../common/sql.h"
 #include "../common/timer.h"
 #include "char.h"
+#include "char_mapif.h"
 #include "inter.h"
 #include "int_mail.h"
 #include "int_auction.h"
@@ -50,7 +51,7 @@ void auction_save(struct auction_data *auction)
 
 	StringBuf_Init(&buf);
 	StringBuf_Printf(&buf, "UPDATE `%s` SET `seller_id` = '%d', `seller_name` = ?, `buyer_id` = '%d', `buyer_name` = ?, `price` = '%d', `buynow` = '%d', `hours` = '%d', `timestamp` = '%lu', `nameid` = '%d', `item_name` = ?, `type` = '%d', `refine` = '%d', `attribute` = '%d'",
-		auction_db, auction->seller_id, auction->buyer_id, auction->price, auction->buynow, auction->hours, (unsigned long)auction->timestamp, auction->item.nameid, auction->type, auction->item.refine, auction->item.attribute);
+		schema_config.auction_db, auction->seller_id, auction->buyer_id, auction->price, auction->buynow, auction->hours, (unsigned long)auction->timestamp, auction->item.nameid, auction->type, auction->item.refine, auction->item.attribute);
 	for( j = 0; j < MAX_SLOTS; j++ )
 		StringBuf_Printf(&buf, ", `card%d` = '%d'", j, auction->item.card[j]);
 	StringBuf_Printf(&buf, " WHERE `auction_id` = '%d'", auction->auction_id);
@@ -81,7 +82,7 @@ unsigned int auction_create(struct auction_data *auction)
 	auction->timestamp = time(NULL) + (auction->hours * 3600);
 
 	StringBuf_Init(&buf);
-	StringBuf_Printf(&buf, "INSERT INTO `%s` (`seller_id`,`seller_name`,`buyer_id`,`buyer_name`,`price`,`buynow`,`hours`,`timestamp`,`nameid`,`item_name`,`type`,`refine`,`attribute`,`unique_id`", auction_db);
+	StringBuf_Printf(&buf, "INSERT INTO `%s` (`seller_id`,`seller_name`,`buyer_id`,`buyer_name`,`price`,`buynow`,`hours`,`timestamp`,`nameid`,`item_name`,`type`,`refine`,`attribute`,`unique_id`", schema_config.auction_db);
 	for( j = 0; j < MAX_SLOTS; j++ )
 		StringBuf_Printf(&buf, ",`card%d`", j);
 	StringBuf_Printf(&buf, ") VALUES ('%d',?,'%d',?,'%d','%d','%d','%lu','%d',?,'%d','%d','%d','%"PRIu64"'",
@@ -135,7 +136,7 @@ static void mapif_Auction_message(int char_id, unsigned char result)
 	WBUFW(buf,0) = 0x3854;
 	WBUFL(buf,2) = char_id;
 	WBUFL(buf,6) = result;
-	mapif_sendall(buf,7);
+	chmapif_sendall(buf,7);
 }
 
 static int auction_end_timer(int tid, unsigned int tick, int id, intptr_t data)
@@ -165,7 +166,7 @@ void auction_delete(struct auction_data *auction)
 {
 	unsigned int auction_id = auction->auction_id;
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `auction_id` = '%d'", auction_db, auction_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `auction_id` = '%d'", schema_config.auction_db, auction_id) )
 		Sql_ShowDebug(sql_handle);
 
 	if( auction->auction_end_timer != INVALID_TIMER )
@@ -187,7 +188,7 @@ void inter_auctions_fromsql(void)
 		"`price`,`buynow`,`hours`,`timestamp`,`nameid`,`item_name`,`type`,`refine`,`attribute`,`unique_id`");
 	for( i = 0; i < MAX_SLOTS; i++ )
 		StringBuf_Printf(&buf, ",`card%d`", i);
-	StringBuf_Printf(&buf, " FROM `%s` ORDER BY `auction_id` DESC", auction_db);
+	StringBuf_Printf(&buf, " FROM `%s` ORDER BY `auction_id` DESC", schema_config.auction_db);
 
 	if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) )
 		Sql_ShowDebug(sql_handle);

+ 14 - 14
src/char/int_elemental.c

@@ -17,12 +17,12 @@
 
 bool mapif_elemental_save(struct s_elemental* ele) {
 	bool flag = true;
-	
+
 	if( ele->elemental_id == 0 ) { // Create new DB entry
 		if( SQL_ERROR == Sql_Query(sql_handle,
 								   "INSERT INTO `%s` (`char_id`,`class`,`mode`,`hp`,`sp`,`max_hp`,`max_sp`,`atk1`,`atk2`,`matk`,`aspd`,`def`,`mdef`,`flee`,`hit`,`life_time`)"
 								   "VALUES ('%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%u')",
-								   elemental_db, ele->char_id, ele->class_, ele->mode, ele->hp, ele->sp, ele->max_hp, ele->max_sp, ele->atk, ele->atk2, ele->matk, ele->amotion, ele->def, ele->mdef, ele->flee, ele->hit, ele->life_time) )
+								   schema_config.elemental_db, ele->char_id, ele->class_, ele->mode, ele->hp, ele->sp, ele->max_hp, ele->max_sp, ele->atk, ele->atk2, ele->matk, ele->amotion, ele->def, ele->mdef, ele->flee, ele->hit, ele->life_time) )
 		{
 			Sql_ShowDebug(sql_handle);
 			flag = false;
@@ -32,7 +32,7 @@ bool mapif_elemental_save(struct s_elemental* ele) {
 	} else if( SQL_ERROR == Sql_Query(sql_handle,
 									"UPDATE `%s` SET `char_id` = '%d', `class` = '%d', `mode` = '%d', `hp` = '%d', `sp` = '%d',"
 									"`max_hp` = '%d', `max_sp` = '%d', `atk1` = '%d', `atk2` = '%d', `matk` = '%d', `aspd` = '%d', `def` = '%d',"
-									"`mdef` = '%d', `flee` = '%d', `hit` = '%d', `life_time` = '%u' WHERE `ele_id` = '%d'",elemental_db,
+									"`mdef` = '%d', `flee` = '%d', `hit` = '%d', `life_time` = '%u' WHERE `ele_id` = '%d'", schema_config.elemental_db,
 									ele->char_id, ele->class_, ele->mode, ele->hp, ele->sp, ele->max_hp, ele->max_sp, ele->atk, ele->atk2,
 									ele->matk, ele->amotion, ele->def, ele->mdef, ele->flee, ele->hit, ele->life_time, ele->elemental_id) )
 	{ // Update DB entry
@@ -44,23 +44,23 @@ bool mapif_elemental_save(struct s_elemental* ele) {
 
 bool mapif_elemental_load(int ele_id, int char_id, struct s_elemental *ele) {
 	char* data;
-	
+
 	memset(ele, 0, sizeof(struct s_elemental));
 	ele->elemental_id = ele_id;
 	ele->char_id = char_id;
-	
+
 	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `class`, `mode`, `hp`, `sp`, `max_hp`, `max_sp`, `atk1`, `atk2`, `matk`, `aspd`,"
 							   "`def`, `mdef`, `flee`, `hit`, `life_time` FROM `%s` WHERE `ele_id` = '%d' AND `char_id` = '%d'",
-							   elemental_db, ele_id, char_id) ) {
+							   schema_config.elemental_db, ele_id, char_id) ) {
 		Sql_ShowDebug(sql_handle);
 		return false;
 	}
-	
+
 	if( SQL_SUCCESS != Sql_NextRow(sql_handle) ) {
 		Sql_FreeResult(sql_handle);
 		return false;
 	}
-	
+
 	Sql_GetData(sql_handle,  0, &data, NULL); ele->class_ = atoi(data);
 	Sql_GetData(sql_handle,  1, &data, NULL); ele->mode = atoi(data);
 	Sql_GetData(sql_handle,  2, &data, NULL); ele->hp = atoi(data);
@@ -77,24 +77,24 @@ bool mapif_elemental_load(int ele_id, int char_id, struct s_elemental *ele) {
 	Sql_GetData(sql_handle, 13, &data, NULL); ele->hit = atoi(data);
 	Sql_GetData(sql_handle, 14, &data, NULL); ele->life_time = atoi(data);
 	Sql_FreeResult(sql_handle);
-	if( save_log )
+	if( charserv_config.save_log )
 		ShowInfo("Elemental loaded (%d - %d).\n", ele->elemental_id, ele->char_id);
-	
+
 	return true;
 }
 
 bool mapif_elemental_delete(int ele_id) {
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `ele_id` = '%d'", elemental_db, ele_id) ) {
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `ele_id` = '%d'", schema_config.elemental_db, ele_id) ) {
 		Sql_ShowDebug(sql_handle);
 		return false;
 	}
-	
+
 	return true;
 }
 
 static void mapif_elemental_send(int fd, struct s_elemental *ele, unsigned char flag) {
 	int size = sizeof(struct s_elemental) + 5;
-	
+
 	WFIFOHEAD(fd,size);
 	WFIFOW(fd,0) = 0x387c;
 	WFIFOW(fd,2) = size;
@@ -150,7 +150,7 @@ void inter_elemental_sql_final(void) {
  *------------------------------------------*/
 int inter_elemental_parse_frommap(int fd) {
 	unsigned short cmd = RFIFOW(fd,0);
-	
+
 	switch( cmd ) {
 		case 0x307c: mapif_parse_elemental_create(fd, (struct s_elemental*)RFIFOP(fd,4)); break;
 		case 0x307d: mapif_parse_elemental_load(fd, (int)RFIFOL(fd,2), (int)RFIFOL(fd,6)); break;

+ 66 - 65
src/char/int_guild.c

@@ -10,6 +10,7 @@
 #include "../common/strlib.h"
 #include "../common/timer.h"
 #include "char.h"
+#include "char_mapif.h"
 #include "inter.h"
 #include "int_guild.h"
 
@@ -72,7 +73,7 @@ static int guild_save_timer(int tid, unsigned int tick, int id, intptr_t data)
 
 		if( g->save_flag == GS_REMOVE )
 		{// Nothing to save, guild is ready for removal.
-			if (save_log)
+			if (charserv_config.save_log)
 				ShowInfo("Guild Unloaded (%d - %s)\n", g->guild_id, g->name);
 			db_remove(guild_db_, key);
 		}
@@ -84,15 +85,15 @@ static int guild_save_timer(int tid, unsigned int tick, int id, intptr_t data)
 
 	state = guild_db_->size(guild_db_);
 	if( state < 1 ) state = 1; //Calculate the time slot for the next save.
-	add_timer(tick  + autosave_interval/state, guild_save_timer, 0, 0);
+	add_timer(tick  + (charserv_config.autosave_interval)/state, guild_save_timer, 0, 0);
 	return 0;
 }
 
 int inter_guild_removemember_tosql(int account_id, int char_id)
 {
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE from `%s` where `account_id` = '%d' and `char_id` = '%d'", guild_member_db, account_id, char_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE from `%s` where `account_id` = '%d' and `char_id` = '%d'", schema_config.guild_member_db, account_id, char_id) )
 		Sql_ShowDebug(sql_handle);
-	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `guild_id` = '0' WHERE `char_id` = '%d'", char_db, char_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `guild_id` = '0' WHERE `char_id` = '%d'", schema_config.char_db, char_id) )
 		Sql_ShowDebug(sql_handle);
 	return 0;
 }
@@ -140,7 +141,7 @@ int inter_guild_tosql(struct guild *g,int flag)
 		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` "
 			"(`name`,`master`,`guild_lv`,`max_member`,`average_lv`,`char_id`) "
 			"VALUES ('%s', '%s', '%d', '%d', '%d', '%d')",
-			guild_db, esc_name, esc_master, g->guild_lv, g->max_member, g->average_lv, g->member[0].char_id) )
+			schema_config.guild_db, esc_name, esc_master, g->guild_lv, g->max_member, g->average_lv, g->member[0].char_id) )
 		{
 			Sql_ShowDebug(sql_handle);
 			if (g->guild_id == -1)
@@ -160,7 +161,7 @@ int inter_guild_tosql(struct guild *g,int flag)
 		bool add_comma = false;
 
 		StringBuf_Init(&buf);
-		StringBuf_Printf(&buf, "UPDATE `%s` SET ", guild_db);
+		StringBuf_Printf(&buf, "UPDATE `%s` SET ", schema_config.guild_db);
 
 		if (flag & GS_EMBLEM)
 		{
@@ -238,14 +239,14 @@ int inter_guild_tosql(struct guild *g,int flag)
 				Sql_EscapeStringLen(sql_handle, esc_name, m->name, strnlen(m->name, NAME_LENGTH));
 				if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`name`) "
 					"VALUES ('%d','%d','%d','%d','%d','%d','%d','%d','%"PRIu64"','%d','%d','%d','%s')",
-					guild_member_db, g->guild_id, m->account_id, m->char_id,
+					schema_config.guild_member_db, g->guild_id, m->account_id, m->char_id,
 					m->hair, m->hair_color, m->gender,
 					m->class_, m->lv, m->exp, m->exp_payper, m->online, m->position, esc_name) )
 					Sql_ShowDebug(sql_handle);
 				if (m->modified&GS_MEMBER_NEW || new_guild == 1)
 				{
 					if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `guild_id` = '%d' WHERE `char_id` = '%d'",
-						char_db, g->guild_id, m->char_id) )
+						schema_config.char_db, g->guild_id, m->char_id) )
 						Sql_ShowDebug(sql_handle);
 				}
 				m->modified = GS_MEMBER_UNMODIFIED;
@@ -262,7 +263,7 @@ int inter_guild_tosql(struct guild *g,int flag)
 				continue;
 			Sql_EscapeStringLen(sql_handle, esc_name, p->name, strnlen(p->name, NAME_LENGTH));
 			if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`guild_id`,`position`,`name`,`mode`,`exp_mode`) VALUES ('%d','%d','%s','%d','%d')",
-				guild_position_db, g->guild_id, i, esc_name, p->mode, p->exp_mode) )
+				schema_config.guild_position_db, g->guild_id, i, esc_name, p->mode, p->exp_mode) )
 				Sql_ShowDebug(sql_handle);
 			p->modified = GS_POSITION_UNMODIFIED;
 		}
@@ -275,7 +276,7 @@ int inter_guild_tosql(struct guild *g,int flag)
 		// their info changed, not to mention this would also mess up oppositions!
 		// [Skotlex]
 		//if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id`='%d' OR `alliance_id`='%d'", guild_alliance_db, g->guild_id, g->guild_id) )
-		if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id`='%d'", guild_alliance_db, g->guild_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id`='%d'", schema_config.guild_alliance_db, g->guild_id) )
 		{
 			Sql_ShowDebug(sql_handle);
 		}
@@ -290,7 +291,7 @@ int inter_guild_tosql(struct guild *g,int flag)
 					Sql_EscapeStringLen(sql_handle, esc_name, a->name, strnlen(a->name, NAME_LENGTH));
 					if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`guild_id`,`opposition`,`alliance_id`,`name`) "
 						"VALUES ('%d','%d','%d','%s')",
-						guild_alliance_db, g->guild_id, a->opposition, a->guild_id, esc_name) )
+						schema_config.guild_alliance_db, g->guild_id, a->opposition, a->guild_id, esc_name) )
 						Sql_ShowDebug(sql_handle);
 				}
 			}
@@ -308,7 +309,7 @@ int inter_guild_tosql(struct guild *g,int flag)
 				Sql_EscapeStringLen(sql_handle, esc_name, e->name, strnlen(e->name, NAME_LENGTH));
 				Sql_EscapeStringLen(sql_handle, esc_mes, e->mes, strnlen(e->mes, sizeof(e->mes)));
 				if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`guild_id`,`account_id`,`name`,`mes`) "
-					"VALUES ('%d','%d','%s','%s')", guild_expulsion_db, g->guild_id, e->account_id, esc_name, esc_mes) )
+					"VALUES ('%d','%d','%s','%s')", schema_config.guild_expulsion_db, g->guild_id, e->account_id, esc_name, esc_mes) )
 					Sql_ShowDebug(sql_handle);
 			}
 		}
@@ -320,13 +321,13 @@ int inter_guild_tosql(struct guild *g,int flag)
 		for(i=0;i<MAX_GUILDSKILL;i++){
 			if (g->skill[i].id>0 && g->skill[i].lv>0){
 				if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`guild_id`,`id`,`lv`) VALUES ('%d','%d','%d')",
-					guild_skill_db, g->guild_id, g->skill[i].id, g->skill[i].lv) )
+					schema_config.guild_skill_db, g->guild_id, g->skill[i].id, g->skill[i].lv) )
 					Sql_ShowDebug(sql_handle);
 			}
 		}
 	}
 
-	if (save_log)
+	if (charserv_config.save_log)
 		ShowInfo("Saved guild (%d - %s):%s\n",g->guild_id,g->name,t_info);
 	return 1;
 }
@@ -352,7 +353,7 @@ struct guild * inter_guild_fromsql(int guild_id)
 #endif
 
 	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT g.`name`,c.`name`,g.`guild_lv`,g.`connect_member`,g.`max_member`,g.`average_lv`,g.`exp`,g.`next_exp`,g.`skill_point`,g.`mes1`,g.`mes2`,g.`emblem_len`,g.`emblem_id`,g.`emblem_data` "
-		"FROM `%s` g LEFT JOIN `%s` c ON c.`char_id` = g.`char_id` WHERE g.`guild_id`='%d'", guild_db, char_db, guild_id) )
+		"FROM `%s` g LEFT JOIN `%s` c ON c.`char_id` = g.`char_id` WHERE g.`guild_id`='%d'", schema_config.guild_db, schema_config.char_db, guild_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return NULL;
@@ -407,7 +408,7 @@ struct guild * inter_guild_fromsql(int guild_id)
 
 	// load guild member info
 	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`name` "
-		"FROM `%s` WHERE `guild_id`='%d' ORDER BY `position`", guild_member_db, guild_id) )
+		"FROM `%s` WHERE `guild_id`='%d' ORDER BY `position`", schema_config.guild_member_db, guild_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		aFree(g);
@@ -435,7 +436,7 @@ struct guild * inter_guild_fromsql(int guild_id)
 	}
 
 	//printf("- Read guild_position %d from sql \n",guild_id);
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `position`,`name`,`mode`,`exp_mode` FROM `%s` WHERE `guild_id`='%d'", guild_position_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `position`,`name`,`mode`,`exp_mode` FROM `%s` WHERE `guild_id`='%d'", schema_config.guild_position_db, guild_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		aFree(g);
@@ -457,7 +458,7 @@ struct guild * inter_guild_fromsql(int guild_id)
 	}
 
 	//printf("- Read guild_alliance %d from sql \n",guild_id);
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `opposition`,`alliance_id`,`name` FROM `%s` WHERE `guild_id`='%d'", guild_alliance_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `opposition`,`alliance_id`,`name` FROM `%s` WHERE `guild_id`='%d'", schema_config.guild_alliance_db, guild_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		aFree(g);
@@ -473,7 +474,7 @@ struct guild * inter_guild_fromsql(int guild_id)
 	}
 
 	//printf("- Read guild_expulsion %d from sql \n",guild_id);
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`name`,`mes` FROM `%s` WHERE `guild_id`='%d'", guild_expulsion_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`name`,`mes` FROM `%s` WHERE `guild_id`='%d'", schema_config.guild_expulsion_db, guild_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		aFree(g);
@@ -489,7 +490,7 @@ struct guild * inter_guild_fromsql(int guild_id)
 	}
 
 	//printf("- Read guild_skill %d from sql \n",guild_id);
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`lv` FROM `%s` WHERE `guild_id`='%d' ORDER BY `id`", guild_skill_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`lv` FROM `%s` WHERE `guild_id`='%d' ORDER BY `id`", schema_config.guild_skill_db, guild_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		aFree(g);
@@ -514,7 +515,7 @@ struct guild * inter_guild_fromsql(int guild_id)
 	idb_put(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 (charserv_config.save_log)
 		ShowInfo("Guild loaded (%d - %s)\n", guild_id, g->name);
 
 	return g;
@@ -528,15 +529,15 @@ int inter_guildcastle_tosql(struct guild_castle *gc)
 
 	StringBuf_Init(&buf);
 	StringBuf_Printf(&buf, "REPLACE INTO `%s` SET `castle_id`='%d', `guild_id`='%d', `economy`='%d', `defense`='%d', "
-	                 "`triggerE`='%d', `triggerD`='%d', `nextTime`='%d', `payTime`='%d', `createTime`='%d', `visibleC`='%d'",
-	                 guild_castle_db, gc->castle_id, gc->guild_id, gc->economy, gc->defense,
-	                 gc->triggerE, gc->triggerD, gc->nextTime, gc->payTime, gc->createTime, gc->visibleC);
+	    "`triggerE`='%d', `triggerD`='%d', `nextTime`='%d', `payTime`='%d', `createTime`='%d', `visibleC`='%d'",
+	    schema_config.guild_castle_db, gc->castle_id, gc->guild_id, gc->economy, gc->defense,
+	    gc->triggerE, gc->triggerD, gc->nextTime, gc->payTime, gc->createTime, gc->visibleC);
 	for (i = 0; i < MAX_GUARDIANS; ++i)
 		StringBuf_Printf(&buf, ", `visibleG%d`='%d'", i, gc->guardian[i].visible);
 
 	if (SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)))
 		Sql_ShowDebug(sql_handle);
-	else if(save_log)
+	else if(charserv_config.save_log)
 		ShowInfo("Saved guild castle (%d)\n", gc->castle_id);
 
 	StringBuf_Destroy(&buf);
@@ -559,7 +560,7 @@ static struct guild_castle* inter_guildcastle_fromsql(int castle_id)
 	                    "`triggerD`, `nextTime`, `payTime`, `createTime`, `visibleC`");
 	for (i = 0; i < MAX_GUARDIANS; ++i)
 		StringBuf_Printf(&buf, ", `visibleG%d`", i);
-	StringBuf_Printf(&buf, " FROM `%s` WHERE `castle_id`='%d'", guild_castle_db, castle_id);
+	StringBuf_Printf(&buf, " FROM `%s` WHERE `castle_id`='%d'", schema_config.guild_castle_db, castle_id);
 	if (SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf))) {
 		Sql_ShowDebug(sql_handle);
 		StringBuf_Destroy(&buf);
@@ -588,7 +589,7 @@ static struct guild_castle* inter_guildcastle_fromsql(int castle_id)
 
 	idb_put(castle_db, castle_id, gc);
 
-	if (save_log)
+	if (charserv_config.save_log)
 		ShowInfo("Loaded guild castle (%d - guild %d)\n", castle_id, gc->guild_id);
 
 	return gc;
@@ -617,7 +618,7 @@ int inter_guild_CharOnline(int char_id, int guild_id)
 
 	if (guild_id == -1) {
 		//Get guild_id from the database
-		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT guild_id FROM `%s` WHERE char_id='%d'", char_db, char_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT guild_id FROM `%s` WHERE char_id='%d'", schema_config.char_db, char_id) )
 		{
 			Sql_ShowDebug(sql_handle);
 			return 0;
@@ -668,7 +669,7 @@ int inter_guild_CharOffline(int char_id, int guild_id)
 	if (guild_id == -1)
 	{
 		//Get guild_id from the database
-		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT guild_id FROM `%s` WHERE char_id='%d'", char_db, char_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT guild_id FROM `%s` WHERE char_id='%d'", schema_config.char_db, char_id) )
 		{
 			Sql_ShowDebug(sql_handle);
 			return 0;
@@ -762,7 +763,7 @@ int search_guildname(char *str)
 
 	Sql_EscapeStringLen(sql_handle, esc_name, str, safestrnlen(str, NAME_LENGTH));
 	//Lookup guilds with the same name
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT guild_id FROM `%s` WHERE name='%s'", guild_db, esc_name) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT guild_id FROM `%s` WHERE name='%s'", schema_config.guild_db, esc_name) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return -1;
@@ -908,9 +909,9 @@ int mapif_guild_noinfo(int fd,int guild_id)
 	WBUFL(buf,4)=guild_id;
 	ShowWarning("int_guild: info not found %d\n",guild_id);
 	if(fd<0)
-		mapif_sendall(buf,8);
+		chmapif_sendall(buf,8);
 	else
-		mapif_send(fd,buf,8);
+		chmapif_send(fd,buf,8);
 	return 0;
 }
 
@@ -922,9 +923,9 @@ int mapif_guild_info(int fd,struct guild *g)
 	WBUFW(buf,2)=4+sizeof(struct guild);
 	memcpy(buf+4,g,sizeof(struct guild));
 	if(fd<0)
-		mapif_sendall(buf,WBUFW(buf,2));
+		chmapif_sendall(buf,WBUFW(buf,2));
 	else
-		mapif_send(fd,buf,WBUFW(buf,2));
+		chmapif_send(fd,buf,WBUFW(buf,2));
 	return 0;
 }
 
@@ -952,7 +953,7 @@ int mapif_guild_withdraw(int guild_id,int account_id,int char_id,int flag, const
 	WBUFB(buf,14)=flag;
 	memcpy(WBUFP(buf,15),mes,40);
 	memcpy(WBUFP(buf,55),name,NAME_LENGTH);
-	mapif_sendall(buf,55+NAME_LENGTH);
+	chmapif_sendall(buf,55+NAME_LENGTH);
 	ShowInfo("int_guild: guild withdraw (%d - %d: %s - %s)\n",guild_id,account_id,name,mes);
 	return 0;
 }
@@ -968,7 +969,7 @@ int mapif_guild_memberinfoshort(struct guild *g,int idx)
 	WBUFB(buf,14)=(unsigned char)g->member[idx].online;
 	WBUFW(buf,15)=g->member[idx].lv;
 	WBUFW(buf,17)=g->member[idx].class_;
-	mapif_sendall(buf,19);
+	chmapif_sendall(buf,19);
 	return 0;
 }
 
@@ -979,7 +980,7 @@ int mapif_guild_broken(int guild_id,int flag)
 	WBUFW(buf,0)=0x3836;
 	WBUFL(buf,2)=guild_id;
 	WBUFB(buf,6)=flag;
-	mapif_sendall(buf,7);
+	chmapif_sendall(buf,7);
 	ShowInfo("int_guild: Guild broken (%d)\n",guild_id);
 	return 0;
 }
@@ -995,7 +996,7 @@ int mapif_guild_message(int guild_id,int account_id,char *mes,int len, int sfd)
 	WBUFL(buf,4)=guild_id;
 	WBUFL(buf,8)=account_id;
 	memcpy(WBUFP(buf,12),mes,len);
-	mapif_sendallwos(sfd, buf,len+12);
+	chmapif_sendallwos(sfd, buf,len+12);
 	return 0;
 }
 
@@ -1010,7 +1011,7 @@ int mapif_guild_basicinfochanged(int guild_id,int type,const void *data,int len)
 	WBUFL(buf, 4)=guild_id;
 	WBUFW(buf, 8)=type;
 	memcpy(WBUFP(buf,10),data,len);
-	mapif_sendall(buf,len+10);
+	chmapif_sendall(buf,len+10);
 	return 0;
 }
 
@@ -1027,7 +1028,7 @@ int mapif_guild_memberinfochanged(int guild_id,int account_id,int char_id, int t
 	WBUFL(buf,12)=char_id;
 	WBUFW(buf,16)=type;
 	memcpy(WBUFP(buf,18),data,len);
-	mapif_sendall(buf,len+18);
+	chmapif_sendall(buf,len+18);
 	return 0;
 }
 
@@ -1039,7 +1040,7 @@ int mapif_guild_skillupack(int guild_id,uint16 skill_id,int account_id)
 	WBUFL(buf, 2)=guild_id;
 	WBUFL(buf, 6)=skill_id;
 	WBUFL(buf,10)=account_id;
-	mapif_sendall(buf,14);
+	chmapif_sendall(buf,14);
 	return 0;
 }
 
@@ -1055,7 +1056,7 @@ int mapif_guild_alliance(int guild_id1,int guild_id2,int account_id1,int account
 	WBUFB(buf,18)=flag;
 	memcpy(WBUFP(buf,19),name1,NAME_LENGTH);
 	memcpy(WBUFP(buf,19+NAME_LENGTH),name2,NAME_LENGTH);
-	mapif_sendall(buf,19+2*NAME_LENGTH);
+	chmapif_sendall(buf,19+2*NAME_LENGTH);
 	return 0;
 }
 
@@ -1068,7 +1069,7 @@ int mapif_guild_position(struct guild *g,int idx)
 	WBUFL(buf,4)=g->guild_id;
 	WBUFL(buf,8)=idx;
 	memcpy(WBUFP(buf,12),&g->position[idx],sizeof(struct guild_position));
-	mapif_sendall(buf,WBUFW(buf,2));
+	chmapif_sendall(buf,WBUFW(buf,2));
 	return 0;
 }
 
@@ -1080,7 +1081,7 @@ int mapif_guild_notice(struct guild *g)
 	WBUFL(buf,2)=g->guild_id;
 	memcpy(WBUFP(buf,6),g->mes1,MAX_GUILDMES1);
 	memcpy(WBUFP(buf,66),g->mes2,MAX_GUILDMES2);
-	mapif_sendall(buf,186);
+	chmapif_sendall(buf,186);
 	return 0;
 }
 
@@ -1093,7 +1094,7 @@ int mapif_guild_emblem(struct guild *g)
 	WBUFL(buf,4)=g->guild_id;
 	WBUFL(buf,8)=g->emblem_id;
 	memcpy(WBUFP(buf,12),g->emblem_data,g->emblem_len);
-	mapif_sendall(buf,WBUFW(buf,2));
+	chmapif_sendall(buf,WBUFW(buf,2));
 	return 0;
 }
 
@@ -1104,7 +1105,7 @@ int mapif_guild_master_changed(struct guild *g, int aid, int cid)
 	WBUFL(buf,2)=g->guild_id;
 	WBUFL(buf,6)=aid;
 	WBUFL(buf,10)=cid;
-	mapif_sendall(buf,14);
+	chmapif_sendall(buf,14);
 	return 0;
 }
 
@@ -1144,15 +1145,15 @@ int mapif_parse_CreateGuild(int fd,int account_id,char *name,struct guild_member
 		return 0;
 	}
 	// Check Authorised letters/symbols in the name of the character
-	if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised
+	if (charserv_config.char_config.char_name_option == 1) { // only letters/symbols in char_name_letters are authorised
 		for (i = 0; i < NAME_LENGTH && name[i]; i++)
-			if (strchr(char_name_letters, name[i]) == NULL) {
+			if (strchr(charserv_config.char_config.char_name_letters, name[i]) == NULL) {
 				mapif_guild_created(fd,account_id,NULL);
 				return 0;
 			}
-	} else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden
+	} else if (charserv_config.char_config.char_name_option == 2) { // letters/symbols in char_name_letters are forbidden
 		for (i = 0; i < NAME_LENGTH && name[i]; i++)
-			if (strchr(char_name_letters, name[i]) != NULL) {
+			if (strchr(charserv_config.char_config.char_name_letters, name[i]) != NULL) {
 				mapif_guild_created(fd,account_id,NULL);
 				return 0;
 			}
@@ -1202,7 +1203,7 @@ int mapif_parse_CreateGuild(int fd,int account_id,char *name,struct guild_member
 	mapif_guild_created(fd,account_id,g);
 	mapif_guild_info(fd,g);
 
-	if(log_inter)
+	if(charserv_config.log_inter)
 		inter_log("guild %s (id=%d) created by master %s (id=%d)\n",
 			name, g->guild_id, master->name, master->account_id );
 
@@ -1268,7 +1269,7 @@ int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, in
 	if( g == NULL )
 	{
 		// Unknown guild, just update the player
-		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `guild_id`='0' WHERE `account_id`='%d' AND `char_id`='%d'", char_db, account_id, char_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `guild_id`='0' WHERE `account_id`='%d' AND `char_id`='%d'", schema_config.char_db, account_id, char_id) )
 			Sql_ShowDebug(sql_handle);
 		// mapif_guild_withdraw(guild_id,account_id,char_id,flag,g->member[i].name,mes);
 		return 0;
@@ -1381,37 +1382,37 @@ int mapif_parse_BreakGuild(int fd,int guild_id)
 
 	// Delete guild from sql
 	//printf("- Delete guild %d from guild\n",guild_id);
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", schema_config.guild_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_member_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", schema_config.guild_member_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_castle_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", schema_config.guild_castle_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_storage_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", schema_config.guild_storage_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d' OR `alliance_id` = '%d'", guild_alliance_db, guild_id, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d' OR `alliance_id` = '%d'", schema_config.guild_alliance_db, guild_id, guild_id) )
 		Sql_ShowDebug(sql_handle);
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_position_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", schema_config.guild_position_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_skill_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", schema_config.guild_skill_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_expulsion_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", schema_config.guild_expulsion_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 
 	//printf("- Update guild %d of char\n",guild_id);
-	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `guild_id`='0' WHERE `guild_id`='%d'", char_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `guild_id`='0' WHERE `guild_id`='%d'", schema_config.char_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 
 	mapif_guild_broken(guild_id,0);
 
-	if(log_inter)
+	if(charserv_config.log_inter)
 		inter_log("guild %s (id=%d) broken\n",g->name,guild_id);
 
 	//Remove the guild from memory. [Skotlex]
@@ -1499,8 +1500,8 @@ int mapif_parse_GuildMemberInfoChange(int fd,int guild_id,int account_id,int cha
 				exp = g->member[i].exp - old_exp;
 
 				// Compute gained exp
-				if (guild_exp_rate != 100)
-					exp = exp*guild_exp_rate/100;
+				if (charserv_config.guild_exp_rate != 100)
+					exp = exp*(charserv_config.guild_exp_rate)/100;
 
 				// Update guild exp
 				if (exp > UINT64_MAX - g->exp)
@@ -1761,7 +1762,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 (charserv_config.log_inter && gc->guild_id != value) {
 				int gid = (value) ? value : gc->guild_id;
 				struct guild *g = idb_get(guild_db_, gid);
 				inter_log("guild %s (id=%d) %s castle id=%d\n",

+ 13 - 12
src/char/int_homun.c

@@ -95,7 +95,7 @@ bool mapif_homunculus_save(struct s_homunculus* hd)
 		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` "
 			"(`char_id`, `class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`, `rename_flag`, `vaporize`) "
 			"VALUES ('%d', '%d', '%d', '%s', '%d', '%u', '%u', '%d', '%d', %d, '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
-			homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
+			schema_config.homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
 			hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize) )
 		{
 			Sql_ShowDebug(sql_handle);
@@ -109,7 +109,7 @@ bool mapif_homunculus_save(struct s_homunculus* hd)
 	else
 	{
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_id`='%d', `class`='%d',`prev_class`='%d',`name`='%s',`level`='%d',`exp`='%u',`intimacy`='%u',`hunger`='%d', `str`='%d', `agi`='%d', `vit`='%d', `int`='%d', `dex`='%d', `luk`='%d', `hp`='%d',`max_hp`='%d',`sp`='%d',`max_sp`='%d',`skill_point`='%d', `rename_flag`='%d', `vaporize`='%d' WHERE `homun_id`='%d'",
-			homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
+			schema_config.homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
 			hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize, hd->hom_id) )
 		{
 			Sql_ShowDebug(sql_handle);
@@ -121,7 +121,7 @@ bool mapif_homunculus_save(struct s_homunculus* hd)
 			int i;
 
 			stmt = SqlStmt_Malloc(sql_handle);
-			if( SQL_ERROR == SqlStmt_Prepare(stmt, "REPLACE INTO `%s` (`homun_id`, `id`, `lv`) VALUES (%d, ?, ?)", skill_homunculus_db, hd->hom_id) )
+			if( SQL_ERROR == SqlStmt_Prepare(stmt, "REPLACE INTO `%s` (`homun_id`, `id`, `lv`) VALUES (%d, ?, ?)", schema_config.skill_homunculus_db, hd->hom_id) )
 				SqlStmt_ShowDebug(stmt);
 			for( i = 0; i < MAX_HOMUNSKILL; ++i )
 			{
@@ -155,7 +155,8 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd)
 
 	memset(hd, 0, sizeof(*hd));
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `homun_id`,`char_id`,`class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`rename_flag`, `vaporize` FROM `%s` WHERE `homun_id`='%u'", homunculus_db, homun_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `homun_id`,`char_id`,`class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`rename_flag`, `vaporize` FROM `%s` WHERE `homun_id`='%u'",
+		schema_config.homunculus_db, homun_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return false;
@@ -201,7 +202,7 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd)
 	hd->hunger = cap_value(hd->hunger, 0, 100);
 
 	// Load Homunculus Skill
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`lv` FROM `%s` WHERE `homun_id`=%d", skill_homunculus_db, homun_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`lv` FROM `%s` WHERE `homun_id`=%d", schema_config.skill_homunculus_db, homun_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return false;
@@ -223,7 +224,7 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd)
 	}
 	Sql_FreeResult(sql_handle);
 
-	if( save_log )
+	if( charserv_config.save_log )
 		ShowInfo("Homunculus loaded (%d - %s).\n", hd->hom_id, hd->name);
 
 	return true;
@@ -231,8 +232,8 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd)
 
 bool mapif_homunculus_delete(int homun_id)
 {
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `homun_id` = '%u'", homunculus_db, homun_id)
-	||	SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `homun_id` = '%u'", skill_homunculus_db, homun_id)
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `homun_id` = '%u'", schema_config.homunculus_db, homun_id)
+	||	SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `homun_id` = '%u'", schema_config.skill_homunculus_db, homun_id)
 	) {
 		Sql_ShowDebug(sql_handle);
 		return false;
@@ -245,16 +246,16 @@ bool mapif_homunculus_rename(char *name)
 	int i;
 
 	// Check Authorised letters/symbols in the name of the homun
-	if( char_name_option == 1 )
+	if( charserv_config.char_config.char_name_option == 1 )
 	{// only letters/symbols in char_name_letters are authorised
 		for( i = 0; i < NAME_LENGTH && name[i]; i++ )
-			if( strchr(char_name_letters, name[i]) == NULL )
+			if( strchr(charserv_config.char_config.char_name_letters, name[i]) == NULL )
 				return false;
 	} else
-	if( char_name_option == 2 )
+	if( charserv_config.char_config.char_name_option == 2 )
 	{// letters/symbols in char_name_letters are forbidden
 		for( i = 0; i < NAME_LENGTH && name[i]; i++ )
-			if( strchr(char_name_letters, name[i]) != NULL )
+			if( strchr(charserv_config.char_config.char_name_letters, name[i]) != NULL )
 				return false;
 	}
 

+ 14 - 13
src/char/int_mail.c

@@ -9,6 +9,7 @@
 #include "../common/sql.h"
 #include "../common/timer.h"
 #include "char.h"
+#include "char_mapif.h"
 #include "inter.h"
 
 #include <stdio.h>
@@ -35,7 +36,7 @@ static int mail_fromsql(int char_id, struct mail_data* md)
 
 	// I keep the `status` < 3 just in case someone forget to apply the sqlfix
 	StringBuf_Printf(&buf, " FROM `%s` WHERE `dest_id`='%d' AND `status` < 3 ORDER BY `id` LIMIT %d",
-		mail_db, char_id, MAIL_MAX_INBOX + 1);
+		schema_config.mail_db, char_id, MAIL_MAX_INBOX + 1);
 
 	if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) )
 		Sql_ShowDebug(sql_handle);
@@ -84,7 +85,7 @@ static int mail_fromsql(int char_id, struct mail_data* md)
 		msg = &md->msg[i];
 		if( msg->status == MAIL_NEW )
 		{
-			if ( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", mail_db, MAIL_UNREAD, msg->id) )
+			if ( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", schema_config.mail_db, MAIL_UNREAD, msg->id) )
 				Sql_ShowDebug(sql_handle);
 
 			msg->status = MAIL_UNREAD;
@@ -108,7 +109,7 @@ int mail_savemessage(struct mail_message* msg)
 
 	// build message save query
 	StringBuf_Init(&buf);
-	StringBuf_Printf(&buf, "INSERT INTO `%s` (`send_name`, `send_id`, `dest_name`, `dest_id`, `title`, `message`, `time`, `status`, `zeny`, `amount`, `nameid`, `refine`, `attribute`, `identify`, `unique_id`", mail_db);
+	StringBuf_Printf(&buf, "INSERT INTO `%s` (`send_name`, `send_id`, `dest_name`, `dest_id`, `title`, `message`, `time`, `status`, `zeny`, `amount`, `nameid`, `refine`, `attribute`, `identify`, `unique_id`", schema_config.mail_db);
 	for (j = 0; j < MAX_SLOTS; j++)
 		StringBuf_Printf(&buf, ", `card%d`", j);
 	StringBuf_Printf(&buf, ") VALUES (?, '%d', ?, '%d', ?, ?, '%lu', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%"PRIu64"'",
@@ -116,7 +117,7 @@ int mail_savemessage(struct mail_message* msg)
 	for (j = 0; j < MAX_SLOTS; j++)
 		StringBuf_Printf(&buf, ", '%d'", msg->item.card[j]);
 	StringBuf_AppendStr(&buf, ")");
-	
+
 	//Unique Non Stackable Item ID
 	updateLastUid(msg->item.unique_id);
 	dbUpdateUid(sql_handle);
@@ -153,7 +154,7 @@ static bool mail_loadmessage(int mail_id, struct mail_message* msg)
 		"`zeny`,`amount`,`nameid`,`refine`,`attribute`,`identify`,`unique_id`");
 	for( j = 0; j < MAX_SLOTS; j++ )
 		StringBuf_Printf(&buf, ",`card%d`", j);
-	StringBuf_Printf(&buf, " FROM `%s` WHERE `id` = '%d'", mail_db, mail_id);
+	StringBuf_Printf(&buf, " FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id);
 
 	if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf))
 	||  SQL_SUCCESS != Sql_NextRow(sql_handle) )
@@ -228,7 +229,7 @@ static void mapif_parse_Mail_requestinbox(int fd)
 static void mapif_parse_Mail_read(int fd)
 {
 	int mail_id = RFIFOL(fd,2);
-	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", mail_db, MAIL_READ, mail_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", schema_config.mail_db, MAIL_READ, mail_id) )
 		Sql_ShowDebug(sql_handle);
 }
 
@@ -241,7 +242,7 @@ static bool mail_DeleteAttach(int mail_id)
 	int i;
 
 	StringBuf_Init(&buf);
-	StringBuf_Printf(&buf, "UPDATE `%s` SET `zeny` = '0', `nameid` = '0', `amount` = '0', `refine` = '0', `attribute` = '0', `identify` = '0'", mail_db);
+	StringBuf_Printf(&buf, "UPDATE `%s` SET `zeny` = '0', `nameid` = '0', `amount` = '0', `refine` = '0', `attribute` = '0', `identify` = '0'", schema_config.mail_db);
 	for (i = 0; i < MAX_SLOTS; i++)
 		StringBuf_Printf(&buf, ", `card%d` = '0'", i);
 	StringBuf_Printf(&buf, " WHERE `id` = '%d'", mail_id);
@@ -297,7 +298,7 @@ static void mapif_parse_Mail_getattach(int fd)
 static void mapif_Mail_delete(int fd, int char_id, int mail_id)
 {
 	bool failed = false;
-	if ( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", mail_db, mail_id) )
+	if ( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		failed = true;
@@ -322,7 +323,7 @@ static void mapif_parse_Mail_delete(int fd)
 void mapif_Mail_new(struct mail_message *msg)
 {
 	unsigned char buf[74];
-	
+
 	if( !msg || !msg->id )
 		return;
 
@@ -331,7 +332,7 @@ void mapif_Mail_new(struct mail_message *msg)
 	WBUFL(buf,6) = msg->id;
 	memcpy(WBUFP(buf,10), msg->send_name, NAME_LENGTH);
 	memcpy(WBUFP(buf,34), msg->title, MAIL_TITLE_LENGTH);
-	mapif_sendall(buf, 74);
+	chmapif_sendall(buf, 74);
 }
 
 /*==========================================
@@ -346,7 +347,7 @@ static void mapif_Mail_return(int fd, int char_id, int mail_id)
 	{
 		if( msg.dest_id != char_id)
 			return;
-		else if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", mail_db, mail_id) )
+		else if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id) )
 			Sql_ShowDebug(sql_handle);
 		else
 		{
@@ -389,7 +390,7 @@ static void mapif_parse_Mail_return(int fd)
 static void mapif_Mail_send(int fd, struct mail_message* msg)
 {
 	int len = sizeof(struct mail_message) + 4;
-	
+
 	WFIFOHEAD(fd,len);
 	WFIFOW(fd,0) = 0x384d;
 	WFIFOW(fd,2) = len;
@@ -411,7 +412,7 @@ static void mapif_parse_Mail_send(int fd)
 
 	// Try to find the Dest Char by Name
 	Sql_EscapeStringLen(sql_handle, esc_name, msg.dest_name, strnlen(msg.dest_name, NAME_LENGTH));
-	if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`, `char_id` FROM `%s` WHERE `name` = '%s'", char_db, esc_name) )
+	if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`, `char_id` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, esc_name) )
 		Sql_ShowDebug(sql_handle);
 	else
 	if ( SQL_SUCCESS == Sql_NextRow(sql_handle) )

+ 10 - 10
src/char/int_mercenary.c

@@ -19,7 +19,7 @@ bool mercenary_owner_fromsql(int char_id, struct mmo_charstatus *status)
 {
 	char* data;
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `merc_id`, `arch_calls`, `arch_faith`, `spear_calls`, `spear_faith`, `sword_calls`, `sword_faith` FROM `%s` WHERE `char_id` = '%d'", mercenary_owner_db, char_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `merc_id`, `arch_calls`, `arch_faith`, `spear_calls`, `spear_faith`, `sword_calls`, `sword_faith` FROM `%s` WHERE `char_id` = '%d'", schema_config.mercenary_owner_db, char_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return false;
@@ -46,7 +46,7 @@ bool mercenary_owner_fromsql(int char_id, struct mmo_charstatus *status)
 bool mercenary_owner_tosql(int char_id, struct mmo_charstatus *status)
 {
 	if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`char_id`, `merc_id`, `arch_calls`, `arch_faith`, `spear_calls`, `spear_faith`, `sword_calls`, `sword_faith`) VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
-		mercenary_owner_db, char_id, status->mer_id, status->arch_calls, status->arch_faith, status->spear_calls, status->spear_faith, status->sword_calls, status->sword_faith) )
+		schema_config.mercenary_owner_db, char_id, status->mer_id, status->arch_calls, status->arch_faith, status->spear_calls, status->spear_faith, status->sword_calls, status->sword_faith) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return false;
@@ -57,10 +57,10 @@ bool mercenary_owner_tosql(int char_id, struct mmo_charstatus *status)
 
 bool mercenary_owner_delete(int char_id)
 {
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d'", mercenary_owner_db, char_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d'", schema_config.mercenary_owner_db, char_id) )
 		Sql_ShowDebug(sql_handle);
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d'", mercenary_db, char_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d'", schema_config.mercenary_db, char_id) )
 		Sql_ShowDebug(sql_handle);
 
 	return true;
@@ -74,7 +74,7 @@ bool mapif_mercenary_save(struct s_mercenary* merc)
 	{ // Create new DB entry
 		if( SQL_ERROR == Sql_Query(sql_handle,
 			"INSERT INTO `%s` (`char_id`,`class`,`hp`,`sp`,`kill_counter`,`life_time`) VALUES ('%d','%d','%d','%d','%u','%u')",
-			mercenary_db, merc->char_id, merc->class_, merc->hp, merc->sp, merc->kill_count, merc->life_time) )
+			schema_config.mercenary_db, merc->char_id, merc->class_, merc->hp, merc->sp, merc->kill_count, merc->life_time) )
 		{
 			Sql_ShowDebug(sql_handle);
 			flag = false;
@@ -84,7 +84,7 @@ bool mapif_mercenary_save(struct s_mercenary* merc)
 	}
 	else if( SQL_ERROR == Sql_Query(sql_handle,
 		"UPDATE `%s` SET `char_id` = '%d', `class` = '%d', `hp` = '%d', `sp` = '%d', `kill_counter` = '%u', `life_time` = '%u' WHERE `mer_id` = '%d'",
-		mercenary_db, merc->char_id, merc->class_, merc->hp, merc->sp, merc->kill_count, merc->life_time, merc->mercenary_id) )
+		schema_config.mercenary_db, merc->char_id, merc->class_, merc->hp, merc->sp, merc->kill_count, merc->life_time, merc->mercenary_id) )
 	{ // Update DB entry
 		Sql_ShowDebug(sql_handle);
 		flag = false;
@@ -101,7 +101,7 @@ bool mapif_mercenary_load(int merc_id, int char_id, struct s_mercenary *merc)
 	merc->mercenary_id = merc_id;
 	merc->char_id = char_id;
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `class`, `hp`, `sp`, `kill_counter`, `life_time` FROM `%s` WHERE `mer_id` = '%d' AND `char_id` = '%d'", mercenary_db, merc_id, char_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `class`, `hp`, `sp`, `kill_counter`, `life_time` FROM `%s` WHERE `mer_id` = '%d' AND `char_id` = '%d'", schema_config.mercenary_db, merc_id, char_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return false;
@@ -119,15 +119,15 @@ bool mapif_mercenary_load(int merc_id, int char_id, struct s_mercenary *merc)
 	Sql_GetData(sql_handle,  3, &data, NULL); merc->kill_count = atoi(data);
 	Sql_GetData(sql_handle,  4, &data, NULL); merc->life_time = atoi(data);
 	Sql_FreeResult(sql_handle);
-	if( save_log )
+	if( charserv_config.save_log )
 		ShowInfo("Mercenary loaded (%d - %d).\n", merc->mercenary_id, merc->char_id);
-	
+
 	return true;
 }
 
 bool mapif_mercenary_delete(int merc_id)
 {
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `mer_id` = '%d'", mercenary_db, merc_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `mer_id` = '%d'", schema_config.mercenary_db, merc_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return false;

+ 37 - 36
src/char/int_party.c

@@ -11,6 +11,7 @@
 #include "../common/mapindex.h"
 #include "../common/sql.h"
 #include "char.h"
+#include "char_mapif.h"
 #include "inter.h"
 #include "int_party.h"
 
@@ -129,9 +130,9 @@ int inter_party_tosql(struct party *p, int flag, int index)
 	if( flag & PS_BREAK )
 	{// Break the party
 		// we'll skip name-checking and just reset everyone with the same party id [celest]
-		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", schema_config.char_db, party_id) )
 			Sql_ShowDebug(sql_handle);
-		if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `party_id`='%d'", party_db, party_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `party_id`='%d'", schema_config.party_db, party_id) )
 			Sql_ShowDebug(sql_handle);
 		//Remove from memory
 		idb_remove(party_db_, party_id);
@@ -143,7 +144,7 @@ int inter_party_tosql(struct party *p, int flag, int index)
 		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` "
 			"(`name`, `exp`, `item`, `leader_id`, `leader_char`) "
 			"VALUES ('%s', '%d', '%d', '%d', '%d')",
-			party_db, esc_name, p->exp, p->item, p->member[index].account_id, p->member[index].char_id) )
+			schema_config.party_db, esc_name, p->exp, p->item, p->member[index].account_id, p->member[index].char_id) )
 		{
 			Sql_ShowDebug(sql_handle);
 			return 0;
@@ -154,32 +155,32 @@ int inter_party_tosql(struct party *p, int flag, int index)
 	if( flag & PS_BASIC )
 	{// Update party info.
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `name`='%s', `exp`='%d', `item`='%d' WHERE `party_id`='%d'",
-			party_db, esc_name, p->exp, p->item, party_id) )
+			schema_config.party_db, esc_name, p->exp, p->item, party_id) )
 			Sql_ShowDebug(sql_handle);
 	}
 
 	if( flag & PS_LEADER )
 	{// Update leader
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s`  SET `leader_id`='%d', `leader_char`='%d' WHERE `party_id`='%d'",
-			party_db, p->member[index].account_id, p->member[index].char_id, party_id) )
+			schema_config.party_db, p->member[index].account_id, p->member[index].char_id, party_id) )
 			Sql_ShowDebug(sql_handle);
 	}
-	
+
 	if( flag & PS_ADDMEMBER )
 	{// Add one party member.
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `party_id`='%d' WHERE `account_id`='%d' AND `char_id`='%d'",
-			char_db, party_id, p->member[index].account_id, p->member[index].char_id) )
+			schema_config.char_db, party_id, p->member[index].account_id, p->member[index].char_id) )
 			Sql_ShowDebug(sql_handle);
 	}
 
 	if( flag & PS_DELMEMBER )
 	{// Remove one party member.
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d' AND `account_id`='%d' AND `char_id`='%d'",
-			char_db, party_id, p->member[index].account_id, p->member[index].char_id) )
+			schema_config.char_db, party_id, p->member[index].account_id, p->member[index].char_id) )
 			Sql_ShowDebug(sql_handle);
 	}
 
-	if( save_log )
+	if( charserv_config.save_log )
 		ShowInfo("Party Saved (%d - %s)\n", party_id, p->name);
 	return 1;
 }
@@ -200,7 +201,7 @@ struct party_data *inter_party_fromsql(int party_id)
 #endif
 	if( party_id <= 0 )
 		return NULL;
-	
+
 	//Load from memory
 	p = (struct party_data*)idb_get(party_db_, party_id);
 	if( p != NULL )
@@ -209,7 +210,7 @@ struct party_data *inter_party_fromsql(int party_id)
 	p = party_pt;
 	memset(p, 0, sizeof(struct party_data));
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `party_id`, `name`,`exp`,`item`, `leader_id`, `leader_char` FROM `%s` WHERE `party_id`='%d'", party_db, party_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `party_id`, `name`,`exp`,`item`, `leader_id`, `leader_char` FROM `%s` WHERE `party_id`='%d'", schema_config.party_db, party_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return NULL;
@@ -227,7 +228,7 @@ struct party_data *inter_party_fromsql(int party_id)
 	Sql_FreeResult(sql_handle);
 
 	// Load members
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`char_id`,`name`,`base_level`,`last_map`,`online`,`class` FROM `%s` WHERE `party_id`='%d'", char_db, party_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`char_id`,`name`,`base_level`,`last_map`,`online`,`class` FROM `%s` WHERE `party_id`='%d'", schema_config.char_db, party_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return NULL;
@@ -246,7 +247,7 @@ struct party_data *inter_party_fromsql(int party_id)
 	}
 	Sql_FreeResult(sql_handle);
 
-	if( save_log )
+	if( charserv_config.save_log )
 		ShowInfo("Party loaded (%d - %s).\n", party_id, p->party.name);
 	//Add party to memory.
 	CREATE(p, struct party_data, 1);
@@ -291,7 +292,7 @@ struct party_data* search_partyname(char* str)
 	struct party_data* p = NULL;
 
 	Sql_EscapeStringLen(sql_handle, esc_name, str, safestrnlen(str, NAME_LENGTH));
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `party_id` FROM `%s` WHERE `name`='%s'", party_db, esc_name) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `party_id` FROM `%s` WHERE `name`='%s'", schema_config.party_db, esc_name) )
 		Sql_ShowDebug(sql_handle);
 	else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
 	{
@@ -370,9 +371,9 @@ static void mapif_party_info(int fd, struct party* p, int char_id)
 	memcpy(WBUFP(buf,8), p, sizeof(struct party));
 
 	if(fd<0)
-		mapif_sendall(buf,WBUFW(buf,2));
+		chmapif_sendall(buf,WBUFW(buf,2));
 	else
-		mapif_send(fd,buf,WBUFW(buf,2));
+		chmapif_send(fd,buf,WBUFW(buf,2));
 }
 
 //Whether or not additional party members
@@ -399,9 +400,9 @@ int mapif_party_optionchanged(int fd,struct party *p,int account_id,int flag)
 	WBUFW(buf,12)=p->item;
 	WBUFB(buf,14)=flag;
 	if(flag==0)
-		mapif_sendall(buf,15);
+		chmapif_sendall(buf,15);
 	else
-		mapif_send(fd,buf,15);
+		chmapif_send(fd,buf,15);
 	return 0;
 }
 
@@ -413,7 +414,7 @@ int mapif_party_withdraw(int party_id,int account_id, int char_id) {
 	WBUFL(buf,2) = party_id;
 	WBUFL(buf,6) = account_id;
 	WBUFL(buf,10) = char_id;
-	mapif_sendall(buf, 14);
+	chmapif_sendall(buf, 14);
 	return 0;
 }
 
@@ -429,7 +430,7 @@ int mapif_party_membermoved(struct party *p,int idx)
 	WBUFW(buf,14) = p->member[idx].map;
 	WBUFB(buf,16) = p->member[idx].online;
 	WBUFW(buf,17) = p->member[idx].lv;
-	mapif_sendall(buf, 19);
+	chmapif_sendall(buf, 19);
 	return 0;
 }
 
@@ -440,7 +441,7 @@ int mapif_party_broken(int party_id,int flag)
 	WBUFW(buf,0)=0x3826;
 	WBUFL(buf,2)=party_id;
 	WBUFB(buf,6)=flag;
-	mapif_sendall(buf,7);
+	chmapif_sendall(buf,7);
 	//printf("int_party: broken %d\n",party_id);
 	return 0;
 }
@@ -454,7 +455,7 @@ int mapif_party_message(int party_id,int account_id,char *mes,int len, int sfd)
 	WBUFL(buf,4)=party_id;
 	WBUFL(buf,8)=account_id;
 	memcpy(WBUFP(buf,12),mes,len);
-	mapif_sendallwos(sfd, buf,len+12);
+	chmapif_sendallwos(sfd, buf,len+12);
 	return 0;
 }
 
@@ -472,9 +473,9 @@ int mapif_parse_CreateParty(int fd, char *name, int item, int item2, struct part
 		return 0;
 	}
 	// Check Authorised letters/symbols in the name of the character
-	if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised
+	if (charserv_config.char_config.char_name_option == 1) { // only letters/symbols in char_name_letters are authorised
 		for (i = 0; i < NAME_LENGTH && name[i]; i++)
-			if (strchr(char_name_letters, name[i]) == NULL) {
+			if (strchr(charserv_config.char_config.char_name_letters, name[i]) == NULL) {
 				if( name[i] == '"' ) { /* client-special-char */
 					normalize_name(name,"\"");
 					mapif_parse_CreateParty(fd,name,item,item2,leader);
@@ -483,16 +484,16 @@ int mapif_parse_CreateParty(int fd, char *name, int item, int item2, struct part
 				mapif_party_created(fd,leader->account_id,leader->char_id,NULL);
 				return 0;
 			}
-	} else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden
+	} else if (charserv_config.char_config.char_name_option == 2) { // letters/symbols in char_name_letters are forbidden
 		for (i = 0; i < NAME_LENGTH && name[i]; i++)
-			if (strchr(char_name_letters, name[i]) != NULL) {
+			if (strchr(charserv_config.char_config.char_name_letters, name[i]) != NULL) {
 				mapif_party_created(fd,leader->account_id,leader->char_id,NULL);
 				return 0;
 			}
 	}
 
 	p = (struct party_data*)aCalloc(1, sizeof(struct party_data));
-	
+
 	memcpy(p->party.name,name,NAME_LENGTH);
 	p->party.exp=0;
 	p->party.item=(item?1:0)|(item2?2:0);
@@ -596,7 +597,7 @@ int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id)
 	p = inter_party_fromsql(party_id);
 	if( p == NULL )
 	{// Party does not exists?
-		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", schema_config.char_db, party_id) )
 			Sql_ShowDebug(sql_handle);
 		return 0;
 	}
@@ -633,7 +634,7 @@ int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id)
 			int_party_check_lv(p);
 		}
 	}
-		
+
 	if (party_check_empty(p) == 0)
 		mapif_party_info(-1, &p->party, 0);
 	return 0;
@@ -648,7 +649,7 @@ int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id
 	if (p == NULL)
 		return 0;
 
-	for(i = 0; i < MAX_PARTY && 
+	for(i = 0; i < MAX_PARTY &&
 		(p->party.member[i].account_id != account_id ||
 		p->party.member[i].char_id != char_id); i++);
 
@@ -662,7 +663,7 @@ int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id
 		else
 			p->party.count--;
 		// Even share check situations: Family state (always breaks)
-		// character logging on/off is max/min level (update level range) 
+		// character logging on/off is max/min level (update level range)
 		// or character logging on/off has a different level (update level range using new level)
 		if (p->family ||
 			(p->party.member[i].lv <= p->min_lv || p->party.member[i].lv >= p->max_lv) ||
@@ -727,7 +728,7 @@ int mapif_parse_PartyLeaderChange(int fd,int party_id,int account_id,int char_id
 
 	for (i = 0; i < MAX_PARTY; i++)
 	{
-		if(p->party.member[i].leader) 
+		if(p->party.member[i].leader)
 			p->party.member[i].leader = 0;
 		if(p->party.member[i].account_id == account_id &&
 			p->party.member[i].char_id == char_id)
@@ -799,7 +800,7 @@ int inter_party_CharOnline(int char_id, int party_id)
 	{// Get party_id from the database
 		char* data;
 
-		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT party_id FROM `%s` WHERE char_id='%d'", char_db, char_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT party_id FROM `%s` WHERE char_id='%d'", schema_config.char_db, char_id) )
 		{
 			Sql_ShowDebug(sql_handle);
 			return 0;
@@ -814,7 +815,7 @@ int inter_party_CharOnline(int char_id, int party_id)
 	}
 	if (party_id == 0)
 		return 0; //No party...
-	
+
 	p = inter_party_fromsql(party_id);
 	if(!p) {
 		ShowError("Character %d's party %d not found!\n", char_id, party_id);
@@ -845,7 +846,7 @@ int inter_party_CharOffline(int char_id, int party_id) {
 	{// Get guild_id from the database
 		char* data;
 
-		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT party_id FROM `%s` WHERE char_id='%d'", char_db, char_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT party_id FROM `%s` WHERE char_id='%d'", schema_config.char_db, char_id) )
 		{
 			Sql_ShowDebug(sql_handle);
 			return 0;
@@ -860,7 +861,7 @@ int inter_party_CharOffline(int char_id, int party_id) {
 	}
 	if (party_id == 0)
 		return 0; //No party...
-	
+
 	//Character has a party, set character offline and check if they were the only member online
 	if ((p = inter_party_fromsql(party_id)) == NULL)
 		return 0;

+ 6 - 6
src/char/int_pet.c

@@ -32,7 +32,7 @@ int inter_pet_tosql(int pet_id, struct s_pet* p)
 		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` "
 			"(`class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`) "
 			"VALUES ('%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
-			pet_db, p->class_, esc_name, p->account_id, p->char_id, p->level, p->egg_id,
+			schema_config.pet_db, p->class_, esc_name, p->account_id, p->char_id, p->level, p->egg_id,
 			p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate) )
 		{
 			Sql_ShowDebug(sql_handle);
@@ -43,7 +43,7 @@ int inter_pet_tosql(int pet_id, struct s_pet* p)
 	else
 	{// Update pet.
 		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `class`='%d',`name`='%s',`account_id`='%d',`char_id`='%d',`level`='%d',`egg_id`='%d',`equip`='%d',`intimate`='%d',`hungry`='%d',`rename_flag`='%d',`incuvate`='%d' WHERE `pet_id`='%d'",
-			pet_db, p->class_, esc_name, p->account_id, p->char_id, p->level, p->egg_id,
+			schema_config.pet_db, p->class_, esc_name, p->account_id, p->char_id, p->level, p->egg_id,
 			p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate, p->pet_id) )
 		{
 			Sql_ShowDebug(sql_handle);
@@ -51,7 +51,7 @@ int inter_pet_tosql(int pet_id, struct s_pet* p)
 		}
 	}
 
-	if (save_log)
+	if (charserv_config.save_log)
 		ShowInfo("Pet saved %d - %s.\n", pet_id, p->name);
 	return 1;
 }
@@ -68,7 +68,7 @@ int inter_pet_fromsql(int pet_id, struct s_pet* p)
 
 	//`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`)
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate` FROM `%s` WHERE `pet_id`='%d'", pet_db, pet_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate` FROM `%s` WHERE `pet_id`='%d'", schema_config.pet_db, pet_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return 0;
@@ -94,7 +94,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( charserv_config.save_log )
 			ShowInfo("Pet loaded (%d - %s).\n", pet_id, p->name);
 	}
 	return 0;
@@ -114,7 +114,7 @@ void inter_pet_sql_final(void){
 int inter_pet_delete(int pet_id){
 	ShowInfo("delete pet request: %d...\n",pet_id);
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `pet_id`='%d'", pet_db, pet_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `pet_id`='%d'", schema_config.pet_db, pet_id) )
 		Sql_ShowDebug(sql_handle);
 	return 0;
 }

+ 4 - 4
src/char/int_quest.c

@@ -43,7 +43,7 @@ struct quest *mapif_quests_fromsql(int char_id, int *count) {
 
 	memset(&tmp_quest, 0, sizeof(struct quest));
 
-	if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `quest_id`, `state`, `time`, `count1`, `count2`, `count3` FROM `%s` WHERE `char_id`=?", quest_db)
+	if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `quest_id`, `state`, `time`, `count1`, `count2`, `count3` FROM `%s` WHERE `char_id`=? ", schema_config.quest_db)
 	||	SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
 	||	SQL_ERROR == SqlStmt_Execute(stmt)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT,  &tmp_quest.quest_id, 0, NULL, NULL)
@@ -88,7 +88,7 @@ struct quest *mapif_quests_fromsql(int char_id, int *count) {
  * @return false in case of errors, true otherwise
  */
 bool mapif_quest_delete(int char_id, int quest_id) {
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `quest_id` = '%d' AND `char_id` = '%d'", quest_db, quest_id, char_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `quest_id` = '%d' AND `char_id` = '%d'", schema_config.quest_db, quest_id, char_id) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return false;
@@ -105,7 +105,7 @@ bool mapif_quest_delete(int char_id, int quest_id) {
  * @return false in case of errors, true otherwise
  */
 bool mapif_quest_add(int char_id, struct quest qd) {
-	if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`quest_id`, `char_id`, `state`, `time`, `count1`, `count2`, `count3`) VALUES ('%d', '%d', '%d','%d', '%d', '%d', '%d')", quest_db, qd.quest_id, char_id, qd.state, qd.time, qd.count[0], qd.count[1], qd.count[2]) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`quest_id`, `char_id`, `state`, `time`, `count1`, `count2`, `count3`) VALUES ('%d', '%d', '%d','%d', '%d', '%d', '%d')", schema_config.quest_db, qd.quest_id, char_id, qd.state, qd.time, qd.count[0], qd.count[1], qd.count[2]) )
 	{
 		Sql_ShowDebug(sql_handle);
 		return false;
@@ -122,7 +122,7 @@ bool mapif_quest_add(int char_id, struct quest qd) {
  * @return false in case of errors, true otherwise
  */
 bool mapif_quest_update(int char_id, struct quest qd) {
-	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `state`='%d', `count1`='%d', `count2`='%d', `count3`='%d' WHERE `quest_id` = '%d' AND `char_id` = '%d'", quest_db, qd.state, qd.count[0], qd.count[1], qd.count[2], qd.quest_id, char_id) ) 
+	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `state`='%d', `count1`='%d', `count2`='%d', `count3`='%d' WHERE `quest_id` = '%d' AND `char_id` = '%d'", schema_config.quest_db, qd.state, qd.count[0], qd.count[1], qd.count[2], qd.quest_id, char_id) ) 
 	{
 		Sql_ShowDebug(sql_handle);
 		return false;

+ 20 - 14
src/char/int_storage.c

@@ -20,7 +20,7 @@
 /// Save storage data to sql
 int storage_tosql(int account_id, struct storage_data* p)
 {
-	memitemdata_to_sql(p->items, MAX_STORAGE, account_id, TABLE_STORAGE);
+	char_memitemdata_to_sql(p->items, MAX_STORAGE, account_id, TABLE_STORAGE);
 	return 0;
 }
 
@@ -38,7 +38,7 @@ int storage_fromsql(int account_id, struct storage_data* p)
 	StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`bound`,`unique_id`");
 	for( j = 0; j < MAX_SLOTS; ++j )
 		StringBuf_Printf(&buf, ",`card%d`", j);
-	StringBuf_Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id);
+	StringBuf_Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", schema_config.storage_db, account_id);
 
 	if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) )
 		Sql_ShowDebug(sql_handle);
@@ -74,7 +74,7 @@ int storage_fromsql(int account_id, struct storage_data* p)
 /// Save guild_storage data to sql
 int guild_storage_tosql(int guild_id, struct guild_storage* p)
 {
-	memitemdata_to_sql(p->items, MAX_GUILD_STORAGE, guild_id, TABLE_GUILD_STORAGE);
+	char_memitemdata_to_sql(p->items, MAX_GUILD_STORAGE, guild_id, TABLE_GUILD_STORAGE);
 	ShowInfo ("guild storage save to DB - guild: %d\n", guild_id);
 	return 0;
 }
@@ -94,7 +94,7 @@ int guild_storage_fromsql(int guild_id, struct guild_storage* p)
 	StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`bound`,`unique_id`");
 	for( j = 0; j < MAX_SLOTS; ++j )
 		StringBuf_Printf(&buf, ",`card%d`", j);
-	StringBuf_Printf(&buf, " FROM `%s` WHERE `guild_id`='%d' ORDER BY `nameid`", guild_storage_db, guild_id);
+	StringBuf_Printf(&buf, " FROM `%s` WHERE `guild_id`='%d' ORDER BY `nameid`", schema_config.guild_storage_db, guild_id);
 
 	if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) )
 		Sql_ShowDebug(sql_handle);
@@ -142,13 +142,13 @@ void inter_storage_sql_final(void)
 // Delete char storage
 int inter_storage_delete(int account_id)
 {
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id`='%d'", storage_db, account_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id`='%d'", schema_config.storage_db, account_id) )
 		Sql_ShowDebug(sql_handle);
 	return 0;
 }
 int inter_guild_storage_delete(int guild_id)
 {
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id`='%d'", guild_storage_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id`='%d'", schema_config.guild_storage_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 	return 0;
 }
@@ -158,7 +158,7 @@ int inter_guild_storage_delete(int guild_id)
 
 int mapif_load_guild_storage(int fd,int account_id,int guild_id, char flag)
 {
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", guild_db, guild_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", schema_config.guild_db, guild_id) )
 		Sql_ShowDebug(sql_handle);
 	else if( Sql_NumRows(sql_handle) > 0 )
 	{// guild exists
@@ -218,7 +218,7 @@ int mapif_parse_SaveGuildStorage(int fd)
 	}
 	else
 	{
-		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", guild_db, guild_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", schema_config.guild_db, guild_id) )
 			Sql_ShowDebug(sql_handle);
 		else if( Sql_NumRows(sql_handle) > 0 )
 		{// guild exists
@@ -263,7 +263,7 @@ int mapif_parse_itembound_retrieve(int fd)
 	StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`");
 	for( j = 0; j < MAX_SLOTS; ++j )
 		StringBuf_Printf(&buf, ", `card%d`", j);
-	StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d'",inventory_db,char_id);
+	StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d'",schema_config.inventory_db,char_id);
 
 	stmt = SqlStmt_Malloc(sql_handle);
 	if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
@@ -305,7 +305,7 @@ int mapif_parse_itembound_retrieve(int fd)
 
 	//First we delete the character's items
 	StringBuf_Clear(&buf);
-	StringBuf_Printf(&buf, "DELETE FROM `%s` WHERE",inventory_db);
+	StringBuf_Printf(&buf, "DELETE FROM `%s` WHERE",schema_config.inventory_db);
 	for(j=0; j<i; j++) {
 		if( found )
 			StringBuf_AppendStr(&buf, " OR");
@@ -354,7 +354,7 @@ int mapif_parse_itembound_retrieve(int fd)
 } while( 0 )
 
 		StringBuf_Clear(&buf);
-		StringBuf_Printf(&buf, "UPDATE `%s` SET ", char_db);
+		StringBuf_Printf(&buf, "UPDATE `%s` SET ", schema_config.char_db);
 		for( j = 0; j < bound_qt; j++ ) {
 			//Equips can be at more than one slot at the same time
 			CHECK_REMOVE(bound_item[j],EQP_HAND_R,weapon);
@@ -387,7 +387,7 @@ int mapif_parse_itembound_retrieve(int fd)
 	found = false;
 	StringBuf_Clear(&buf);
 	StringBuf_Printf(&buf, "INSERT INTO `%s` (`guild_id`, `nameid`, `amount`, `equip`, `identify`, `refine`,"
-		"`attribute`, `expire_time`, `bound`", guild_storage_db);
+		"`attribute`, `expire_time`, `bound`", schema_config.guild_storage_db);
 	for( s = 0; s < MAX_SLOTS; ++s )
 		StringBuf_Printf(&buf, ", `card%d`", s);
 	StringBuf_AppendStr(&buf, ") VALUES ");
@@ -422,8 +422,14 @@ int mapif_parse_itembound_retrieve(int fd)
 	//Finally reload storage and tell map we're done
 	mapif_load_guild_storage(fd,aid,guild_id,0);
 
-	//If character is logged in char, disconnect
-	disconnect_player(aid);
+	//If character is logged in char, disconnect, 
+	/* @CHECKME [lighta]
+	 * I suppose this was an attempt to avoid item duplication if the expelled user reconnect during the operation.
+	 * well it's kinda ugly to expel someone like this, so I consider this as a hack.
+	 * we better flag them so that they not allowed to reconnect during operation or flag it so we will flush those item on ram with the map ack.
+	 * both way seem nicer for player.
+	 */
+	char_disconnect_player(aid);
 
 	//Tell map-server the operation is over and it can unlock the storage
 	mapif_itembound_ack(fd,aid,guild_id);

+ 25 - 24
src/char/inter.c

@@ -9,6 +9,8 @@
 #include "../common/socket.h"
 #include "../common/timer.h"
 #include "char.h"
+#include "char_logif.h"
+#include "char_mapif.h"
 #include "inter.h"
 #include "int_party.h"
 #include "int_guild.h"
@@ -389,7 +391,7 @@ void mapif_parse_accinfo(int fd) {
 	account_id = atoi(query);
 
 	if (account_id < START_ACCOUNT_NUM) {	// is string
-		if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`name`,`class`,`base_level`,`job_level`,`online` FROM `%s` WHERE `name` LIKE '%s' LIMIT 10", char_db, query_esq)
+		if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`name`,`class`,`base_level`,`job_level`,`online` FROM `%s` WHERE `name` LIKE '%s' LIMIT 10", schema_config.char_db, query_esq)
 				|| Sql_NumRows(sql_handle) == 0 ) {
 			if( Sql_NumRows(sql_handle) == 0 ) {
 				inter_to_fd(fd, u_fd, aid, "No matches were found for your criteria, '%s'",query);
@@ -523,12 +525,12 @@ int inter_accreg_tosql(int account_id, int char_id, struct accreg* reg, int type
 	switch( type )
 	{
 	case 3: //Char Reg
-		if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", schema_config.reg_db, char_id) )
 			Sql_ShowDebug(sql_handle);
 		account_id = 0;
 		break;
 	case 2: //Account Reg
-		if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'", reg_db, account_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'", schema_config.reg_db, account_id) )
 			Sql_ShowDebug(sql_handle);
 		char_id = 0;
 		break;
@@ -544,7 +546,7 @@ int inter_accreg_tosql(int account_id, int char_id, struct accreg* reg, int type
 		return 0;
 
 	StringBuf_Init(&buf);
-	StringBuf_Printf(&buf, "INSERT INTO `%s` (`type`,`account_id`,`char_id`,`str`,`value`) VALUES ", reg_db);
+	StringBuf_Printf(&buf, "INSERT INTO `%s` (`type`,`account_id`,`char_id`,`str`,`value`) VALUES ", schema_config.reg_db);
 
 	for( i = 0; i < reg->reg_num; ++i ) {
 		r = &reg->reg[i];
@@ -590,11 +592,11 @@ int inter_accreg_fromsql(int account_id,int char_id, struct accreg *reg, int typ
 	switch( type )
 	{
 	case 3: //char reg
-		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'", schema_config.reg_db, char_id) )
 			Sql_ShowDebug(sql_handle);
 		break;
 	case 2: //account reg
-		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'", reg_db, account_id) )
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'", schema_config.reg_db, account_id) )
 			Sql_ShowDebug(sql_handle);
 		break;
 	case 1: //account2 reg
@@ -669,7 +671,7 @@ static int inter_config_read(const char* cfgName)
 		else if(!strcmpi(w1,"party_share_level"))
 			party_share_level = (unsigned int)atof(w2);
 		else if(!strcmpi(w1,"log_inter"))
-			log_inter = atoi(w2);
+			charserv_config.log_inter = atoi(w2);
 		else if(!strcmpi(w1,"import"))
 			inter_config_read(w2);
 	}
@@ -692,7 +694,7 @@ int inter_log(char* fmt, ...)
 	va_end(ap);
 
 	Sql_EscapeStringLen(sql_handle, esc_str, str, strnlen(str, sizeof(str)));
-	if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`time`, `log`) VALUES (NOW(),  '%s')", interlog_db, esc_str) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`time`, `log`) VALUES (NOW(),  '%s')", schema_config.interlog_db, esc_str) )
 		Sql_ShowDebug(sql_handle);
 
 	return 0;
@@ -769,8 +771,7 @@ int inter_mapif_init(int fd)
 int mapif_broadcast(unsigned char *mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY, int sfd)
 {
 	unsigned char *buf = (unsigned char*)aMalloc((len)*sizeof(unsigned char));
-	if (buf == NULL) return 1;
-		
+
 	WBUFW(buf,0) = 0x3800;
 	WBUFW(buf,2) = len;
 	WBUFL(buf,4) = fontColor;
@@ -779,7 +780,7 @@ int mapif_broadcast(unsigned char *mes, int len, unsigned long fontColor, short
 	WBUFW(buf,12) = fontAlign;
 	WBUFW(buf,14) = fontY;
 	memcpy(WBUFP(buf,16), mes, len - 16);
-	mapif_sendallwos(sfd, buf, len);
+	chmapif_sendallwos(sfd, buf, len);
 
 	aFree(buf);
 	return 0;
@@ -797,7 +798,7 @@ int mapif_wis_message(struct WisData *wd)
 	memcpy(WBUFP(buf, 8), wd->src, NAME_LENGTH);
 	memcpy(WBUFP(buf,32), wd->dst, NAME_LENGTH);
 	memcpy(WBUFP(buf,56), wd->msg, wd->len);
-	wd->count = mapif_sendall(buf,WBUFW(buf,2));
+	wd->count = chmapif_sendall(buf,WBUFW(buf,2));
 
 	return 0;
 }
@@ -810,7 +811,7 @@ int mapif_wis_end(struct WisData *wd, int flag)
 	WBUFW(buf, 0)=0x3802;
 	memcpy(WBUFP(buf, 2),wd->src,24);
 	WBUFB(buf,26)=flag;
-	mapif_send(wd->fd,buf,27);
+	chmapif_send(wd->fd,buf,27);
 	return 0;
 }
 
@@ -818,7 +819,7 @@ int mapif_wis_end(struct WisData *wd, int flag)
 static void mapif_account_reg(int fd, unsigned char *src)
 {
 	WBUFW(src,0)=0x3804; //NOTE: writing to RFIFO
-	mapif_sendallwos(fd, src, WBUFW(src,2));
+	chmapif_sendallwos(fd, src, WBUFW(src,2));
 }
 
 // Send the requested account_reg
@@ -934,7 +935,7 @@ int mapif_parse_WisRequest(int fd)
 	safestrncpy(name, (char*)RFIFOP(fd,28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex]
 
 	Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `name` FROM `%s` WHERE `name`='%s'", char_db, esc_name) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `name` FROM `%s` WHERE `name`='%s'", schema_config.char_db, esc_name) )
 		Sql_ShowDebug(sql_handle);
 
 	// search if character exists before to ask all map-servers
@@ -944,7 +945,7 @@ int mapif_parse_WisRequest(int fd)
 		WBUFW(buf, 0) = 0x3802;
 		memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH);
 		WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
-		mapif_send(fd, buf, 27);
+		chmapif_send(fd, buf, 27);
 	}
 	else
 	{// Character exists. So, ask all map-servers
@@ -959,7 +960,7 @@ int mapif_parse_WisRequest(int fd)
 			WBUFW(buf, 0) = 0x3802;
 			memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH);
 			WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
-			mapif_send(fd, buf, 27);
+			chmapif_send(fd, buf, 27);
 		}
 		else
 		{
@@ -1014,7 +1015,7 @@ int mapif_parse_WisToGM(int fd)
 
 	memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2));
 	WBUFW(buf, 0) = 0x3803;
-	mapif_sendall(buf, RFIFOW(fd,2));
+	chmapif_sendall(buf, RFIFOW(fd,2));
 
 	return 0;
 }
@@ -1034,7 +1035,7 @@ int mapif_parse_Registry(int fd)
 		max = ACCOUNT_REG_NUM;
 	break;
 	case 1: //Account2 registry, must be sent over to login server.
-		return save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4);
+		return chlogif_save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4);
 	default:
 		return 1;
 	}
@@ -1061,7 +1062,7 @@ int mapif_parse_RegistryRequest(int fd)
 	//Load Account Registry
 	if (RFIFOB(fd,11)) mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),2);
 	//Ask Login Server for Account2 values.
-	if (RFIFOB(fd,10)) request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6));
+	if (RFIFOB(fd,10)) chlogif_request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6));
 	return 1;
 }
 
@@ -1089,15 +1090,15 @@ int mapif_parse_NameChangeRequest(int fd)
 	name = (char*)RFIFOP(fd,11);
 
 	// Check Authorised letters/symbols in the name
-	if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised
+	if (charserv_config.char_config.char_name_option == 1) { // only letters/symbols in char_name_letters are authorised
 		for (i = 0; i < NAME_LENGTH && name[i]; i++)
-		if (strchr(char_name_letters, name[i]) == NULL) {
+		if (strchr(charserv_config.char_config.char_name_letters, name[i]) == NULL) {
 			mapif_namechange_ack(fd, account_id, char_id, type, 0, name);
 			return 0;
 		}
-	} else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden
+	} else if (charserv_config.char_config.char_name_option == 2) { // letters/symbols in char_name_letters are forbidden
 		for (i = 0; i < NAME_LENGTH && name[i]; i++)
-		if (strchr(char_name_letters, name[i]) != NULL) {
+		if (strchr(charserv_config.char_config.char_name_letters, name[i]) != NULL) {
 			mapif_namechange_ack(fd, account_id, char_id, type, 0, name);
 			return 0;
 		}

+ 46 - 20
src/common/cli.c

@@ -1,3 +1,12 @@
+/**
+ * @file cli.c
+ * Module purpose is to handle the console (cli=console line input) while the servers launch and run.
+ *  This contains functions common to all servers, but then dispatches them to a specific parser on each server.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author rAthena Dev Team
+ */
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -29,14 +38,16 @@ char* LOGIN_CONF_NAME;
 char* LAN_CONF_NAME; //char-login
 char* MSG_CONF_NAME_EN; //all
 
-/*
- * Function to check if specified option have an argument follow it
- * return
+/**
+ * Function to check if the specified option has an argument following it.
+ * @param option: actual args string
+ * @param i: index of current args
+ * @param argc: arguments count
+ * @return
  *   false : no other args found, and throw a warning
  *   true : something following us
  */
-bool opt_has_next_value(const char* option, int i, int argc)
-{
+bool opt_has_next_value(const char* option, int i, int argc){
 	if (i >= argc - 1) {
 		ShowWarning("Missing value for option '%s'.\n", option);
 		return false;
@@ -45,11 +56,12 @@ bool opt_has_next_value(const char* option, int i, int argc)
 	return true;
 }
 
-/*
- * Display some info about emulator such as
+/**
+ * Display some information about the emulator, such as:
  *   svn version
- *   website/forum adresse
+ *   website/forum address
  *   irc hangout
+ * @param do_exit: terminate execution ?
  */
 void display_versionscreen(bool do_exit)
 {
@@ -68,16 +80,18 @@ void display_versionscreen(bool do_exit)
 		exit(EXIT_SUCCESS);
 }
 
-/*
- * Read the option specify in command line
- * and assign the confs used by the different server
- * exit on failure or return true
+/**
+ * Read the option specified in the command line
+ * and assign the confs used by the different servers.
+ * @TODO remove and place into csnlif of different serv
+ * @param argc: arguments count (from main)
+ * @param argv: arguments values (from main)
+ * @return true or exit on failure
  */
-int cli_get_options(int argc, char ** argv)
-{
-    int i = 0;
-    for (i = 1; i < argc; i++) {
-	const char* arg = argv[i];
+int cli_get_options(int argc, char ** argv) {
+	int i = 0;
+	for (i = 1; i < argc; i++) {
+		const char* arg = argv[i];
 
 	if (arg[0] != '-' && (arg[0] != '/' || arg[1] == '-')) {// -, -- and /
 	    ShowError("Unknown option '%s'.\n", argv[i]);
@@ -163,17 +177,29 @@ int cli_get_options(int argc, char ** argv)
     return 1;
 }
 
-int cli_hasevent(){
+/**
+ * Detect if the console has some input to be read.
+ * @return true if event, else false
+ */
+bool cli_hasevent(){
 #ifdef WIN32
-	return _kbhit();
+	return (_kbhit()!=0);
 #else
 	struct pollfd fds;
 	fds.fd = 0; /* this is STDIN */
 	fds.events = POLLIN;
-	return poll(&fds, 1, 0);
+	return (poll(&fds, 1, 0)>0);
 #endif
 }
 
+/**
+ * Timered function to check if the console has a new event to be read.
+ * @param tid: timer id
+ * @param tick: tick of execution
+ * @param id: user account id
+ * @param data: unused
+ * @return 0
+ */
 int parse_console_timer(int tid, unsigned int tick, int id, intptr_t data) {
 	char buf[MAX_CONSOLE_IN]; //max cmd atm is 63+63+63+3+3
 

+ 10 - 5
src/common/cli.h

@@ -1,8 +1,10 @@
-/*
- * File:   cli.h
- * Author: lighta
- *
- * Created on February 21, 2013, 6:15 PM
+/**
+ * @file cli.h
+ * Module purpose is to handle the console (cli=console line input) while the servers launch and run.
+ *  This contains functions common to all servers, but then dispatches them to a specific parser on each server.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author rAthena Dev Team
  */
 
 #ifndef CLI_H
@@ -32,6 +34,9 @@ extern "C" {
  extern char* MSG_CONF_NAME_EN; //all
 
 extern void display_helpscreen(bool exit);
+bool cli_hasevent();
+void display_versionscreen(bool do_exit);
+bool opt_has_next_value(const char* option, int i, int argc);
 int cli_get_options(int argc, char ** argv);
 int parse_console_timer(int tid, unsigned int tick, int id, intptr_t data);
 extern int parse_console(const char* buf); //particular for each serv

+ 1 - 1
src/common/evdp.h

@@ -136,7 +136,7 @@ bool evdp_outgoingconnection_established(int32 fd, EVDP_DATA *ep);
  *	the connection must be already added (as client or listener)
  * 
  *
- * @return sucess indicator
+ * @return success indicator
  */
 bool evdp_writable_add(int32 fd, EVDP_DATA *ep);
 

+ 2 - 0
src/common/malloc.c

@@ -115,6 +115,8 @@ void aFree_(void *p, const char *file, int line, const char *func)
 	// ShowMessage("%s:%d: in func %s: aFree %p\n",file,line,func,p);
 	if (p)
 		FREE(p, file, line, func);
+
+	p = NULL;
 }
 
 

+ 1 - 0
src/common/mmo.h

@@ -89,6 +89,7 @@
 //For character names, title names, guilds, maps, etc.
 //Includes null-terminator as it is the length of the array.
 #define NAME_LENGTH (23 + 1)
+#define PASSWD_LENGTH (32+1)
 //For item names, which tend to have much longer names.
 #define ITEM_NAME_LENGTH 50
 //For Map Names, which the client considers to be 16 in length including the .gat extension

+ 1 - 0
src/common/timer.h

@@ -51,6 +51,7 @@ int add_timer_func_list(TimerFunc func, char* name);
 
 unsigned long get_uptime(void);
 
+//transform a timestamp to string
 const char* timestamp2string(char* str, size_t size, time_t timestamp, const char* format);
 void split_time(int time, int* year, int* month, int* day, int* hour, int* minute, int* second);
 double solve_time(char* modif_p);

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio