root/src/char_sql/char.c @ 23

Revision 1, 137.3 kB (checked in by jinshiro, 17 years ago)
Line 
1// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
2// For more information, see LICENCE in the main folder
3
4#include "../common/cbasetypes.h"
5#include "../common/strlib.h"
6#include "../common/core.h"
7#include "../common/timer.h"
8#include "../common/mmo.h"
9#include "../common/db.h"
10#include "../common/malloc.h"
11#include "../common/mapindex.h"
12#include "../common/showmsg.h"
13#include "../common/socket.h"
14#include "../common/version.h"
15#include "../common/utils.h"
16#include "inter.h"
17#include "int_guild.h"
18#include "int_homun.h"
19#include "int_party.h"
20#include "char.h"
21
22#include <sys/types.h>
23#include <time.h>
24#include <signal.h>
25#include <string.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29
30// private declarations
31#define CHAR_CONF_NAME  "conf/char_athena.conf"
32#define LAN_CONF_NAME   "conf/subnet_athena.conf"
33#define SQL_CONF_NAME   "conf/inter_athena.conf"
34
35char char_db[256] = "char";
36char scdata_db[256] = "sc_data";
37char cart_db[256] = "cart_inventory";
38char inventory_db[256] = "inventory";
39char charlog_db[256] = "charlog";
40char storage_db[256] = "storage";
41char interlog_db[256] = "interlog";
42char reg_db[256] = "global_reg_value";
43char skill_db[256] = "skill";
44char memo_db[256] = "memo";
45char guild_db[256] = "guild";
46char guild_alliance_db[256] = "guild_alliance";
47char guild_castle_db[256] = "guild_castle";
48char guild_expulsion_db[256] = "guild_expulsion";
49char guild_member_db[256] = "guild_member";
50char guild_position_db[256] = "guild_position";
51char guild_skill_db[256] = "guild_skill";
52char guild_storage_db[256] = "guild_storage";
53char party_db[256] = "party";
54char pet_db[256] = "pet";
55char mail_db[256] = "mail"; // MAIL SYSTEM
56char auction_db[256] = "auction"; // Auctions System
57char friend_db[256] = "friends";
58char hotkey_db[256] = "hotkey";
59char quest_db[256] = "quest";
60char quest_obj_db[256] = "quest_objective";
61
62//If your code editor is having problems syntax highlighting this file, uncomment this and RECOMMENT IT BEFORE COMPILING
63//#undef TXT_SQL_CONVERT
64#ifndef TXT_SQL_CONVERT
65static DBMap* char_db_; // int char_id -> struct mmo_charstatus*
66
67char db_path[1024] = "db";
68
69int db_use_sqldbs;
70
71char login_db[256] = "login";
72char login_db_account_id[32] = "account_id";
73char login_db_level[32] = "level";
74
75int lowest_gm_level = 1;
76
77struct mmo_map_server {
78        int fd;
79        uint32 ip;
80        uint16 port;
81        int users;
82        unsigned short map[MAX_MAP_PER_SERVER];
83} server[MAX_MAP_SERVERS];
84
85int login_fd=-1, char_fd=-1;
86char userid[24];
87char passwd[24];
88char server_name[20];
89char wisp_server_name[NAME_LENGTH] = "Server";
90char login_ip_str[128];
91uint32 login_ip = 0;
92uint16 login_port = 6900;
93char char_ip_str[128];
94uint32 char_ip = 0;
95char bind_ip_str[128];
96uint32 bind_ip = INADDR_ANY;
97uint16 char_port = 6121;
98int char_maintenance = 0;
99bool char_new = true;
100int char_new_display = 0;
101
102int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor]
103int char_name_option = 0; // 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]
104char unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the requested name cannot be determined
105#define TRIM_CHARS "\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex]
106char char_name_letters[1024] = ""; // list of letters/symbols used to authorise or not a name of a character. by [Yor]
107bool char_rename = true;
108
109int char_per_account = 0; //Maximum charas per account (default unlimited) [Sirius]
110int char_del_level = 0; //From which level u can delete character [Lupus]
111
112int log_char = 1;       // loggin char or not [devil]
113int log_inter = 1;      // loggin inter or not [devil]
114
115#ifdef TXT_SQL_CONVERT
116int save_log = 0; //Have the logs be off by default when converting
117#else
118int save_log = 1;
119#endif
120
121static int online_check = 1; //If one, it won't let players connect when their account is already registered online and will send the relevant map server a kick user request. [Skotlex]
122
123// Advanced subnet check [LuzZza]
124struct s_subnet {
125        uint32 mask;
126        uint32 char_ip;
127        uint32 map_ip;
128} subnet[16];
129int subnet_count = 0;
130
131struct char_session_data {
132        bool auth; // whether the session is authed or not
133        int account_id, login_id1, login_id2, sex;
134        int found_char[MAX_CHARS]; // ids of chars on this account
135        char email[40]; // e-mail (default: a@a.com) by [Yor]
136        time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
137};
138
139int char_num, char_max;
140int max_connect_user = 0;
141int gm_allow_level = 99;
142int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
143int start_zeny = 0;
144int start_weapon = 1201;
145int start_armor = 2301;
146int guild_exp_rate = 100;
147
148//Custom limits for the fame lists. [Skotlex]
149int fame_list_size_chemist = MAX_FAME_LIST;
150int fame_list_size_smith = MAX_FAME_LIST;
151int fame_list_size_taekwon = MAX_FAME_LIST;
152
153// Char-server-side stored fame lists [DracoRPG]
154struct fame_list smith_fame_list[MAX_FAME_LIST];
155struct fame_list chemist_fame_list[MAX_FAME_LIST];
156struct fame_list taekwon_fame_list[MAX_FAME_LIST];
157
158// check for exit signal
159// 0 is saving complete
160// other is char_id
161unsigned int save_flag = 0;
162
163// Initial position (it's possible to set it in conf file)
164struct point start_point = { 0, 53, 111 };
165
166bool char_gm_read = false;
167struct gm_account *gm_account = NULL;
168int GM_num = 0;
169
170int console = 0;
171
172//-----------------------------------------------------
173// Auth database
174//-----------------------------------------------------
175#define AUTH_TIMEOUT 30000
176
177struct auth_node {
178        int account_id;
179        int char_id;
180        uint32 login_id1;
181        uint32 login_id2;
182        uint32 ip;
183        int sex;
184        time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
185};
186
187static DBMap* auth_db; // int account_id -> struct auth_node*
188
189//-----------------------------------------------------
190// Online User Database
191//-----------------------------------------------------
192
193struct online_char_data {
194        int account_id;
195        int char_id;
196        int fd;
197        int waiting_disconnect;
198        short server; // -2: unknown server, -1: not connected, 0+: id of server
199};
200
201static DBMap* online_char_db; // int account_id -> struct online_char_data*
202static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, intptr data);
203
204static void* create_online_char_data(DBKey key, va_list args)
205{
206        struct online_char_data* character;
207        CREATE(character, struct online_char_data, 1);
208        character->account_id = key.i;
209        character->char_id = -1;
210        character->server = -1;
211        character->fd = -1;
212        character->waiting_disconnect = -1;
213        return character;
214}
215
216void set_char_charselect(int account_id)
217{
218        struct online_char_data* character;
219
220        character = (struct online_char_data*)idb_ensure(online_char_db, account_id, create_online_char_data);
221
222        if( character->server > -1 )
223                server[character->server].users--;
224
225        character->char_id = -1;
226        character->server = -1;
227
228        if(character->waiting_disconnect != -1) {
229                delete_timer(character->waiting_disconnect, chardb_waiting_disconnect);
230                character->waiting_disconnect = -1;
231        }
232
233        if (login_fd > 0 && !session[login_fd]->flag.eof)
234        {
235                WFIFOHEAD(login_fd,6);
236                WFIFOW(login_fd,0) = 0x272c;
237                WFIFOL(login_fd,2) = account_id;
238                WFIFOSET(login_fd,6);
239        }
240
241}
242
243void set_char_online(int map_id, int char_id, int account_id)
244{
245        struct online_char_data* character;
246        struct mmo_charstatus *cp;
247       
248        //Update DB
249        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `online`='1' WHERE `char_id`='%d'", char_db, char_id) )
250                Sql_ShowDebug(sql_handle);
251
252        //Check to see for online conflicts
253        character = (struct online_char_data*)idb_ensure(online_char_db, account_id, create_online_char_data);
254        if (online_check && character->char_id != -1 && character->server > -1 && character->server != map_id)
255        {
256                ShowNotice("set_char_online: Character %d:%d marked in map server %d, but map server %d claims to have (%d:%d) online!\n",
257                        character->account_id, character->char_id, character->server, map_id, account_id, char_id);
258                mapif_disconnectplayer(server[character->server].fd, character->account_id, character->char_id, 2);
259        }
260
261        //Update state data
262        character->char_id = char_id;
263        character->server = map_id;
264
265        if( character->server > -1 )
266                server[character->server].users++;
267
268        //Get rid of disconnect timer
269        if(character->waiting_disconnect != -1) {
270                delete_timer(character->waiting_disconnect, chardb_waiting_disconnect);
271                character->waiting_disconnect = -1;
272        }
273
274        //Set char online in guild cache. If char is in memory, use the guild id on it, otherwise seek it.
275        cp = (struct mmo_charstatus*)idb_get(char_db_,char_id);
276        inter_guild_CharOnline(char_id, cp?cp->guild_id:-1);
277
278        //Notify login server
279        if (login_fd > 0 && !session[login_fd]->flag.eof)
280        {       
281                WFIFOHEAD(login_fd,6);
282                WFIFOW(login_fd,0) = 0x272b;
283                WFIFOL(login_fd,2) = account_id;
284                WFIFOSET(login_fd,6);
285        }
286}
287
288void set_char_offline(int char_id, int account_id)
289{
290        struct online_char_data* character;
291
292        if ( char_id == -1 )
293        {
294                if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `online`='0' WHERE `account_id`='%d'", char_db, account_id) )
295                        Sql_ShowDebug(sql_handle);
296        }
297        else
298        {
299                struct mmo_charstatus* cp = (struct mmo_charstatus*)idb_get(char_db_,char_id);
300                inter_guild_CharOffline(char_id, cp?cp->guild_id:-1);
301                if (cp)
302                        idb_remove(char_db_,char_id);
303
304                if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `online`='0' WHERE `char_id`='%d'", char_db, char_id) )
305                        Sql_ShowDebug(sql_handle);
306        }
307
308        if ((character = (struct online_char_data*)idb_get(online_char_db, account_id)) != NULL)
309        {       //We don't free yet to avoid aCalloc/aFree spamming during char change. [Skotlex]
310                if( character->server > -1 )
311                        server[character->server].users--;
312
313                if(character->waiting_disconnect != -1){
314                        delete_timer(character->waiting_disconnect, chardb_waiting_disconnect);
315                        character->waiting_disconnect = -1;
316                }
317
318                //If user is NOT at char screen, delete entry [Kevin]
319                if(character->char_id != -1)
320                {
321                        idb_remove(online_char_db, account_id);
322                }
323        }
324       
325        if (login_fd > 0 && !session[login_fd]->flag.eof && (char_id == -1 || character == NULL || character->char_id != -1))
326        {
327                WFIFOHEAD(login_fd,6);
328                WFIFOW(login_fd,0) = 0x272c;
329                WFIFOL(login_fd,2) = account_id;
330                WFIFOSET(login_fd,6);
331        }
332}
333
334static int char_db_setoffline(DBKey key, void* data, va_list ap)
335{
336        struct online_char_data* character = (struct online_char_data*)data;
337        int server = va_arg(ap, int);
338        if (server == -1) {
339                character->char_id = -1;
340                character->server = -1;
341                if(character->waiting_disconnect != -1){
342                        delete_timer(character->waiting_disconnect, chardb_waiting_disconnect);
343                        character->waiting_disconnect = -1;
344                }
345        } else if (character->server == server)
346                character->server = -2; //In some map server that we aren't connected to.
347        return 0;
348}
349
350static int char_db_kickoffline(DBKey key, void* data, va_list ap)
351{
352        struct online_char_data* character = (struct online_char_data*)data;
353        int server_id = va_arg(ap, int);
354
355        if (server_id > -1 && character->server != server_id)
356                return 0;
357
358        //Kick out any connected characters, and set them offline as appropiate.
359        if (character->server > -1)
360                mapif_disconnectplayer(server[character->server].fd, character->account_id, character->char_id, 1);
361        else if (character->waiting_disconnect == -1)
362                set_char_offline(character->char_id, character->account_id);
363        else
364                return 0; // fail
365
366        return 1;
367}
368
369void set_all_offline(int id)
370{
371        if (id < 0)
372                ShowNotice("Sending all users offline.\n");
373        else
374                ShowNotice("Sending users of map-server %d offline.\n",id);
375        online_char_db->foreach(online_char_db,char_db_kickoffline,id);
376
377        if (id >= 0 || login_fd <= 0 || session[login_fd]->flag.eof)
378                return;
379        //Tell login-server to also mark all our characters as offline.
380        WFIFOHEAD(login_fd,2);
381        WFIFOW(login_fd,0) = 0x2737;
382        WFIFOSET(login_fd,2);
383}
384
385void set_all_offline_sql(void)
386{
387        //Set all players to 'OFFLINE'
388        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `online` = '0'", char_db) )
389                Sql_ShowDebug(sql_handle);
390        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `online` = '0'", guild_member_db) )
391                Sql_ShowDebug(sql_handle);
392        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `connect_member` = '0'", guild_db) )
393                Sql_ShowDebug(sql_handle);
394}
395
396//----------------------------------------------------------------------
397// Determine if an account (id) is a GM account
398// and returns its level (or 0 if it isn't a GM account or if not found)
399//----------------------------------------------------------------------
400int isGM(int account_id)
401{
402        int i;
403
404        for(i = 0; i < GM_num; i++)
405                if (gm_account[i].account_id == account_id)
406                        return gm_account[i].level;
407        return 0;
408}
409
410void read_gm_account(void)
411{
412        if(!char_gm_read)
413                return;
414       
415        if (gm_account != NULL)
416                aFree(gm_account);
417        GM_num = 0;
418
419        if( SQL_ERROR == Sql_Query(lsql_handle, "SELECT `%s`,`%s` FROM `%s` WHERE `%s`>='%d'", login_db_account_id, login_db_level, login_db, login_db_level, lowest_gm_level) )
420                Sql_ShowDebug(lsql_handle);
421
422        if( Sql_NumRows(lsql_handle) > 0 )
423        {
424                char* data;
425
426                CREATE(gm_account, struct gm_account, (size_t)Sql_NumRows(lsql_handle));
427                while( SQL_SUCCESS == Sql_NextRow(lsql_handle) )
428                {
429                        // account_id
430                        Sql_GetData(lsql_handle, 0, &data, NULL);
431                        gm_account[GM_num].account_id = atoi(data);
432                        // account_id
433                        Sql_GetData(lsql_handle, 1, &data, NULL);
434                        gm_account[GM_num].level = atoi(data);
435                        ++GM_num;
436                }
437        }
438        Sql_FreeResult(lsql_handle);
439
440        mapif_send_gmaccounts();
441}
442
443static void* create_charstatus(DBKey key, va_list args)
444{
445        struct mmo_charstatus *cp;
446        cp = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus));
447        cp->char_id = key.i;
448        return cp;
449}
450#endif //TXT_SQL_CONVERT
451
452int mmo_char_tosql(int char_id, struct mmo_charstatus* p)
453{
454        int i = 0;
455        int count = 0;
456        int diff = 0;
457        char save_status[128]; //For displaying save information. [Skotlex]
458        struct mmo_charstatus *cp;
459        StringBuf buf;
460
461        if (char_id!=p->char_id) return 0;
462
463#ifndef TXT_SQL_CONVERT
464        cp = (struct mmo_charstatus*)idb_ensure(char_db_, char_id, create_charstatus);
465#else
466        cp = (struct mmo_charstatus*)aCalloc(1, sizeof(struct mmo_charstatus));
467#endif
468
469        StringBuf_Init(&buf);
470        memset(save_status, 0, sizeof(save_status));
471
472        //map inventory data
473        if( memcmp(p->inventory, cp->inventory, sizeof(p->inventory)) )
474        {
475                memitemdata_to_sql(p->inventory, MAX_INVENTORY, p->char_id, TABLE_INVENTORY);
476                strcat(save_status, " inventory");
477        }
478
479        //map cart data
480        if( memcmp(p->cart, cp->cart, sizeof(p->cart)) )
481        {
482                memitemdata_to_sql(p->cart, MAX_CART, p->char_id, TABLE_CART);
483                strcat(save_status, " cart");
484        }
485
486#ifdef TXT_SQL_CONVERT
487{       //Insert the barebones to then update the rest.
488        char esc_name[NAME_LENGTH*2+1];
489
490        Sql_EscapeStringLen(sql_handle, esc_name, p->name, strnlen(p->name, NAME_LENGTH));
491        if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`char_id`, `account_id`, `char_num`, `name`)  VALUES ('%d', '%d', '%d', '%s')",
492                char_db, p->char_id, p->account_id, p->slot, esc_name) )
493        {
494                Sql_ShowDebug(sql_handle);
495        }
496
497        strcat(save_status, " creation");
498}
499#endif
500
501        if (
502                (p->base_exp != cp->base_exp) || (p->base_level != cp->base_level) ||
503                (p->job_level != cp->job_level) || (p->job_exp != cp->job_exp) ||
504                (p->zeny != cp->zeny) ||
505                (p->last_point.x != cp->last_point.x) || (p->last_point.y != cp->last_point.y) ||
506                (p->max_hp != cp->max_hp) || (p->hp != cp->hp) ||
507                (p->max_sp != cp->max_sp) || (p->sp != cp->sp) ||
508                (p->status_point != cp->status_point) || (p->skill_point != cp->skill_point) ||
509                (p->str != cp->str) || (p->agi != cp->agi) || (p->vit != cp->vit) ||
510                (p->int_ != cp->int_) || (p->dex != cp->dex) || (p->luk != cp->luk) ||
511                (p->option != cp->option) ||
512                (p->party_id != cp->party_id) || (p->guild_id != cp->guild_id) ||
513                (p->pet_id != cp->pet_id) || (p->weapon != cp->weapon) || (p->hom_id != cp->hom_id) ||
514                (p->shield != cp->shield) || (p->head_top != cp->head_top) ||
515                (p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom)
516        )
517        {       //Save status
518                if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d',"
519                        "`base_exp`='%u', `job_exp`='%u', `zeny`='%d',"
520                        "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
521                        "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
522                        "`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',"
523                        "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
524                        "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d'"
525                        " WHERE  `account_id`='%d' AND `char_id` = '%d'",
526                        char_db, p->base_level, p->job_level,
527                        p->base_exp, p->job_exp, p->zeny,
528                        p->max_hp, p->hp, p->max_sp, p->sp, p->status_point, p->skill_point,
529                        p->str, p->agi, p->vit, p->int_, p->dex, p->luk,
530                        p->option, p->party_id, p->guild_id, p->pet_id, p->hom_id,
531                        p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom,
532                        mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
533                        mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y,
534                        p->account_id, p->char_id) )
535                {
536                        Sql_ShowDebug(sql_handle);
537                }
538                strcat(save_status, " status");
539        }
540
541        //Values that will seldom change (to speed up saving)
542        if (
543                (p->hair != cp->hair) || (p->hair_color != cp->hair_color) || (p->clothes_color != cp->clothes_color) ||
544                (p->class_ != cp->class_) ||
545                (p->partner_id != cp->partner_id) || (p->father != cp->father) ||
546                (p->mother != cp->mother) || (p->child != cp->child) ||
547                (p->karma != cp->karma) || (p->manner != cp->manner) ||
548                (p->fame != cp->fame)
549        )
550        {
551                if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `class`='%d',"
552                        "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',"
553                        "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d',"
554                        "`karma`='%d',`manner`='%d', `fame`='%d'"
555                        " WHERE  `account_id`='%d' AND `char_id` = '%d'",
556                        char_db, p->class_,
557                        p->hair, p->hair_color, p->clothes_color,
558                        p->partner_id, p->father, p->mother, p->child,
559                        p->karma, p->manner, p->fame,
560                        p->account_id, p->char_id) )
561                {
562                        Sql_ShowDebug(sql_handle);
563                }
564
565                strcat(save_status, " status2");
566        }
567
568        //memo points
569        if( memcmp(p->memo_point, cp->memo_point, sizeof(p->memo_point)) )
570        {
571                char esc_mapname[NAME_LENGTH*2+1];
572
573                //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`)
574                if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", memo_db, p->char_id) )
575                        Sql_ShowDebug(sql_handle);
576
577                //insert here.
578                StringBuf_Clear(&buf);
579                StringBuf_Printf(&buf, "INSERT INTO `%s`(`char_id`,`map`,`x`,`y`) VALUES ", memo_db);
580                for( i = 0, count = 0; i < MAX_MEMOPOINTS; ++i )
581                {
582                        if( p->memo_point[i].map )
583                        {
584                                if( count )
585                                        StringBuf_AppendStr(&buf, ",");
586                                Sql_EscapeString(sql_handle, esc_mapname, mapindex_id2name(p->memo_point[i].map));
587                                StringBuf_Printf(&buf, "('%d', '%s', '%d', '%d')", char_id, esc_mapname, p->memo_point[i].x, p->memo_point[i].y);
588                                ++count;
589                        }
590                }
591                if( count )
592                {
593                        if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
594                                Sql_ShowDebug(sql_handle);
595                }
596               
597                strcat(save_status, " memo");
598        }
599
600        //FIXME: is this neccessary? [ultramage]
601        for(i=0;i<MAX_SKILL;i++)
602                if ((p->skill[i].lv != 0) && (p->skill[i].id == 0))
603                        p->skill[i].id = i; // Fix skill tree
604
605
606        //skills
607        if( memcmp(p->skill, cp->skill, sizeof(p->skill)) )
608        {
609                //`skill` (`char_id`, `id`, `lv`)
610                if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", skill_db, p->char_id) )
611                        Sql_ShowDebug(sql_handle);
612
613                StringBuf_Clear(&buf);
614                StringBuf_Printf(&buf, "INSERT INTO `%s`(`char_id`,`id`,`lv`) VALUES ", skill_db);
615                //insert here.
616                for( i = 0, count = 0; i < MAX_SKILL; ++i )
617                {
618                        if(p->skill[i].id && p->skill[i].flag!=1)
619                        {
620                                if( count )
621                                        StringBuf_AppendStr(&buf, ",");
622                                StringBuf_Printf(&buf, "('%d','%d','%d')", char_id, p->skill[i].id, (p->skill[i].flag == 0 ? p->skill[i].lv : p->skill[i].flag - 2));
623                                ++count;
624                        }
625                }
626                if( count )
627                {
628                        if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
629                                Sql_ShowDebug(sql_handle);
630                }
631
632                strcat(save_status, " skills");
633        }
634
635        diff = 0;
636        for(i = 0; i < MAX_FRIENDS; i++){
637                if(p->friends[i].char_id != cp->friends[i].char_id ||
638                        p->friends[i].account_id != cp->friends[i].account_id){
639                        diff = 1;
640                        break;
641                }
642        }
643
644        if(diff == 1)
645        {       //Save friends
646                if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", friend_db, char_id) )
647                        Sql_ShowDebug(sql_handle);
648
649                StringBuf_Clear(&buf);
650                StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `friend_account`, `friend_id`) VALUES ", friend_db);
651                for( i = 0, count = 0; i < MAX_FRIENDS; ++i )
652                {
653                        if( p->friends[i].char_id > 0 )
654                        {
655                                if( count )
656                                        StringBuf_AppendStr(&buf, ",");
657                                StringBuf_Printf(&buf, "('%d','%d','%d')", char_id, p->friends[i].account_id, p->friends[i].char_id);
658                                count++;
659                        }
660                }
661                if( count )
662                {
663                        if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
664                                Sql_ShowDebug(sql_handle);
665                        else
666                                strcat(save_status, " friends");
667                }
668                else //Friend list cleared.
669                        strcat(save_status, " friends");
670        }
671
672#ifdef HOTKEY_SAVING
673        // hotkeys
674        StringBuf_Clear(&buf);
675        StringBuf_Printf(&buf, "REPLACE INTO `%s` (`char_id`, `hotkey`, `type`, `itemskill_id`, `skill_lvl`) VALUES ", hotkey_db);
676        diff = 0;
677        for(i = 0; i < ARRAYLENGTH(p->hotkeys); i++){
678                if(memcmp(&p->hotkeys[i], &cp->hotkeys[i], sizeof(struct hotkey)))
679                {
680                        if( diff )
681                                StringBuf_AppendStr(&buf, ",");// not the first hotkey
682                        StringBuf_Printf(&buf, "('%d','%u','%u','%u','%u')", char_id, (unsigned int)i, (unsigned int)p->hotkeys[i].type, p->hotkeys[i].id , (unsigned int)p->hotkeys[i].lv);
683                        diff = 1;
684                }
685        }
686        if(diff) {
687                if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
688                        Sql_ShowDebug(sql_handle);
689                else
690                        strcat(save_status, " hotkeys");
691        }
692#endif
693        StringBuf_Destroy(&buf);
694        if (save_status[0]!='\0' && save_log)
695                ShowInfo("Saved char %d - %s:%s.\n", char_id, p->name, save_status);
696#ifndef TXT_SQL_CONVERT
697        memcpy(cp, p, sizeof(struct mmo_charstatus));
698#else
699        aFree(cp);
700#endif
701        return 0;
702}
703
704/// Saves an array of 'item' entries into the specified table.
705int memitemdata_to_sql(const struct item items[], int max, int id, int tableswitch)
706{
707        StringBuf buf;
708        SqlStmt* stmt;
709        int i;
710        int j;
711        const char* tablename;
712        const char* selectoption;
713        struct item item; // temp storage variable
714        bool* flag; // bit array for inventory matching
715        bool found;
716
717        switch (tableswitch) {
718        case TABLE_INVENTORY:     tablename = inventory_db;     selectoption = "char_id";    break;
719        case TABLE_CART:          tablename = cart_db;          selectoption = "char_id";    break;
720        case TABLE_STORAGE:       tablename = storage_db;       selectoption = "account_id"; break;
721        case TABLE_GUILD_STORAGE: tablename = guild_storage_db; selectoption = "guild_id";   break;
722        default:
723                ShowError("Invalid table name!\n");
724                return 1;
725        }
726
727
728        // The following code compares inventory with current database values
729        // and performs modification/deletion/insertion only on relevant rows.
730        // This approach is more complicated than a trivial delete&insert, but
731        // it significantly reduces cpu load on the database server.
732
733        StringBuf_Init(&buf);
734        StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
735        for( j = 0; j < MAX_SLOTS; ++j )
736                StringBuf_Printf(&buf, ", `card%d`", j);
737        StringBuf_Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id);
738
739        stmt = SqlStmt_Malloc(sql_handle);
740        if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
741        ||  SQL_ERROR == SqlStmt_Execute(stmt) )
742        {
743                SqlStmt_ShowDebug(stmt);
744                SqlStmt_Free(stmt);
745                StringBuf_Destroy(&buf);
746                return 1;
747        }
748
749        SqlStmt_BindColumn(stmt, 0, SQLDT_INT,    &item.id,        0, NULL, NULL);
750        SqlStmt_BindColumn(stmt, 1, SQLDT_SHORT,  &item.nameid,    0, NULL, NULL);
751        SqlStmt_BindColumn(stmt, 2, SQLDT_SHORT,  &item.amount,    0, NULL, NULL);
752        SqlStmt_BindColumn(stmt, 3, SQLDT_USHORT, &item.equip,     0, NULL, NULL);
753        SqlStmt_BindColumn(stmt, 4, SQLDT_CHAR,   &item.identify,  0, NULL, NULL);
754        SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR,   &item.refine,    0, NULL, NULL);
755        SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR,   &item.attribute, 0, NULL, NULL);
756        for( j = 0; j < MAX_SLOTS; ++j )
757                SqlStmt_BindColumn(stmt, 7+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
758
759        // bit array indicating which inventory items have already been matched
760        flag = (bool*) aCallocA(max, sizeof(bool));
761
762        while( SQL_SUCCESS == SqlStmt_NextRow(stmt) )
763        {
764                found = false;
765                // search for the presence of the item in the char's inventory
766                for( i = 0; i < max; ++i )
767                {
768                        // skip empty and already matched entries
769                        if( items[i].nameid == 0 || flag[i] )
770                                continue;
771
772                        if( items[i].nameid == item.nameid
773                        &&  items[i].card[0] == item.card[0]
774                        &&  items[i].card[2] == item.card[2]
775                        &&  items[i].card[3] == item.card[3]
776                        ) {     //They are the same item.
777                                ARR_FIND( 0, MAX_SLOTS, j, items[i].card[j] != item.card[j] );
778                                if( j == MAX_SLOTS &&
779                                    items[i].amount == item.amount &&
780                                    items[i].equip == item.equip &&
781                                    items[i].identify == item.identify &&
782                                    items[i].refine == item.refine &&
783                                    items[i].attribute == item.attribute )
784                                ;       //Do nothing.
785                                else
786                                {
787                                        // update all fields.
788                                        StringBuf_Clear(&buf);
789                                        StringBuf_Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d'",
790                                                tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute);
791                                        for( j = 0; j < MAX_SLOTS; ++j )
792                                                StringBuf_Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
793                                        StringBuf_Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);
794                                       
795                                        if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
796                                                Sql_ShowDebug(sql_handle);
797                                }
798
799                                found = flag[i] = true; //Item dealt with,
800                                break; //skip to next item in the db.
801                        }
802                }
803                if( !found )
804                {// Item not present in inventory, remove it.
805                        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE from `%s` where `id`='%d'", tablename, item.id) )
806                                Sql_ShowDebug(sql_handle);
807                }
808        }
809        SqlStmt_Free(stmt);
810
811        StringBuf_Clear(&buf);
812        StringBuf_Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`", tablename, selectoption);
813        for( j = 0; j < MAX_SLOTS; ++j )
814                StringBuf_Printf(&buf, ", `card%d`", j);
815        StringBuf_AppendStr(&buf, ") VALUES ");
816
817        found = false;
818        // insert non-matched items into the db as new items
819        for( i = 0; i < max; ++i )
820        {
821                // skip empty and already matched entries
822                if( items[i].nameid == 0 || flag[i] )
823                        continue;
824
825                if( found )
826                        StringBuf_AppendStr(&buf, ",");
827                else
828                        found = true;
829
830                StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
831                        id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute);
832                for( j = 0; j < MAX_SLOTS; ++j )
833                        StringBuf_Printf(&buf, ", '%d'", items[i].card[j]);
834                StringBuf_AppendStr(&buf, ")");
835        }
836
837        if( found && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
838                Sql_ShowDebug(sql_handle);
839
840        StringBuf_Destroy(&buf);
841        aFree(flag);
842
843        return 0;
844}
845
846int mmo_char_tobuf(uint8* buf, struct mmo_charstatus* p);
847
848#ifndef TXT_SQL_CONVERT
849//=====================================================================================================
850// Loads the basic character rooster for the given account. Returns total buffer used.
851int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
852{
853        SqlStmt* stmt;
854        struct mmo_charstatus p;
855        int j = 0, i;
856
857        stmt = SqlStmt_Malloc(sql_handle);
858        if( stmt == NULL )
859        {
860                SqlStmt_ShowDebug(stmt);
861                return 0;
862        }
863        memset(&p, 0, sizeof(p));
864
865        // read char data
866        if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT "
867                "`char_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
868                "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`,"
869                "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`,"
870                "`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`"
871                " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS)
872        ||      SQL_ERROR == SqlStmt_Execute(stmt)
873        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 0,  SQLDT_INT,    &p.char_id, 0, NULL, NULL)
874        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 1,  SQLDT_UCHAR,  &p.slot, 0, NULL, NULL)
875        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 2,  SQLDT_STRING, &p.name, sizeof(p.name), NULL, NULL)
876        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 3,  SQLDT_SHORT,  &p.class_, 0, NULL, NULL)
877        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 4,  SQLDT_UINT,   &p.base_level, 0, NULL, NULL)
878        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 5,  SQLDT_UINT,   &p.job_level, 0, NULL, NULL)
879        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 6,  SQLDT_UINT,   &p.base_exp, 0, NULL, NULL)
880        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 7,  SQLDT_UINT,   &p.job_exp, 0, NULL, NULL)
881        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 8,  SQLDT_INT,    &p.zeny, 0, NULL, NULL)
882        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 9,  SQLDT_SHORT,  &p.str, 0, NULL, NULL)
883        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 10, SQLDT_SHORT,  &p.agi, 0, NULL, NULL)
884        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 11, SQLDT_SHORT,  &p.vit, 0, NULL, NULL)
885        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 12, SQLDT_SHORT,  &p.int_, 0, NULL, NULL)
886        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 13, SQLDT_SHORT,  &p.dex, 0, NULL, NULL)
887        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 14, SQLDT_SHORT,  &p.luk, 0, NULL, NULL)
888        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 15, SQLDT_INT,    &p.max_hp, 0, NULL, NULL)
889        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 16, SQLDT_INT,    &p.hp, 0, NULL, NULL)
890        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 17, SQLDT_INT,    &p.max_sp, 0, NULL, NULL)
891        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 18, SQLDT_INT,    &p.sp, 0, NULL, NULL)
892        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 19, SQLDT_USHORT, &p.status_point, 0, NULL, NULL)
893        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 20, SQLDT_USHORT, &p.skill_point, 0, NULL, NULL)
894        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 21, SQLDT_UINT,   &p.option, 0, NULL, NULL)
895        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 22, SQLDT_UCHAR,  &p.karma, 0, NULL, NULL)
896        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 23, SQLDT_SHORT,  &p.manner, 0, NULL, NULL)
897        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 24, SQLDT_SHORT,  &p.hair, 0, NULL, NULL)
898        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 25, SQLDT_SHORT,  &p.hair_color, 0, NULL, NULL)
899        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 26, SQLDT_SHORT,  &p.clothes_color, 0, NULL, NULL)
900        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 27, SQLDT_SHORT,  &p.weapon, 0, NULL, NULL)
901        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 28, SQLDT_SHORT,  &p.shield, 0, NULL, NULL)
902        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 29, SQLDT_SHORT,  &p.head_top, 0, NULL, NULL)
903        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 30, SQLDT_SHORT,  &p.head_mid, 0, NULL, NULL)
904        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 31, SQLDT_SHORT,  &p.head_bottom, 0, NULL, NULL)
905        )
906        {
907                SqlStmt_ShowDebug(stmt);
908                SqlStmt_Free(stmt);
909                return 0;
910        }
911        for( i = 0; i < MAX_CHARS && SQL_SUCCESS == SqlStmt_NextRow(stmt); i++ )
912        {
913                sd->found_char[i] = p.char_id;
914                j += mmo_char_tobuf(WBUFP(buf, j), &p);
915        }
916        for( ; i < MAX_CHARS; i++ )
917                sd->found_char[i] = -1;
918
919        SqlStmt_Free(stmt);
920        return j;
921}
922
923//=====================================================================================================
924int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything)
925{
926        int i,j;
927        char t_msg[128] = "";
928        struct mmo_charstatus* cp;
929        StringBuf buf;
930        SqlStmt* stmt;
931        char last_map[MAP_NAME_LENGTH_EXT];
932        char save_map[MAP_NAME_LENGTH_EXT];
933        char point_map[MAP_NAME_LENGTH_EXT];
934        struct point tmp_point;
935        struct item tmp_item;
936        struct skill tmp_skill;
937        struct s_friend tmp_friend;
938#ifdef HOTKEY_SAVING
939        struct hotkey tmp_hotkey;
940        int hotkey_num;
941#endif
942
943        memset(p, 0, sizeof(struct mmo_charstatus));
944       
945        if (save_log) ShowInfo("Char load request (%d)\n", char_id);
946
947        stmt = SqlStmt_Malloc(sql_handle);
948        if( stmt == NULL )
949        {
950                SqlStmt_ShowDebug(stmt);
951                return 0;
952        }
953
954        // read char data
955        if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT "
956                "`char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
957                "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`,"
958                "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`hair`,"
959                "`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`,"
960                "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`"
961                " FROM `%s` WHERE `char_id`=? LIMIT 1", char_db)
962        ||      SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
963        ||      SQL_ERROR == SqlStmt_Execute(stmt)
964        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 0,  SQLDT_INT,    &p->char_id, 0, NULL, NULL)
965        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 1,  SQLDT_INT,    &p->account_id, 0, NULL, NULL)
966        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 2,  SQLDT_UCHAR,  &p->slot, 0, NULL, NULL)
967        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 3,  SQLDT_STRING, &p->name, sizeof(p->name), NULL, NULL)
968        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 4,  SQLDT_SHORT,  &p->class_, 0, NULL, NULL)
969        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 5,  SQLDT_UINT,   &p->base_level, 0, NULL, NULL)
970        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 6,  SQLDT_UINT,   &p->job_level, 0, NULL, NULL)
971        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 7,  SQLDT_UINT,   &p->base_exp, 0, NULL, NULL)
972        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 8,  SQLDT_UINT,   &p->job_exp, 0, NULL, NULL)
973        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 9,  SQLDT_INT,    &p->zeny, 0, NULL, NULL)
974        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 10, SQLDT_SHORT,  &p->str, 0, NULL, NULL)
975        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 11, SQLDT_SHORT,  &p->agi, 0, NULL, NULL)
976        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 12, SQLDT_SHORT,  &p->vit, 0, NULL, NULL)
977        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 13, SQLDT_SHORT,  &p->int_, 0, NULL, NULL)
978        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 14, SQLDT_SHORT,  &p->dex, 0, NULL, NULL)
979        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 15, SQLDT_SHORT,  &p->luk, 0, NULL, NULL)
980        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 16, SQLDT_INT,    &p->max_hp, 0, NULL, NULL)
981        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 17, SQLDT_INT,    &p->hp, 0, NULL, NULL)
982        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 18, SQLDT_INT,    &p->max_sp, 0, NULL, NULL)
983        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 19, SQLDT_INT,    &p->sp, 0, NULL, NULL)
984        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 20, SQLDT_USHORT, &p->status_point, 0, NULL, NULL)
985        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 21, SQLDT_USHORT, &p->skill_point, 0, NULL, NULL)
986        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 22, SQLDT_UINT,   &p->option, 0, NULL, NULL)
987        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 23, SQLDT_UCHAR,  &p->karma, 0, NULL, NULL)
988        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 24, SQLDT_SHORT,  &p->manner, 0, NULL, NULL)
989        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 25, SQLDT_INT,    &p->party_id, 0, NULL, NULL)
990        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 26, SQLDT_INT,    &p->guild_id, 0, NULL, NULL)
991        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 27, SQLDT_INT,    &p->pet_id, 0, NULL, NULL)
992        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 28, SQLDT_INT,    &p->hom_id, 0, NULL, NULL)
993        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 29, SQLDT_SHORT,  &p->hair, 0, NULL, NULL)
994        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 30, SQLDT_SHORT,  &p->hair_color, 0, NULL, NULL)
995        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 31, SQLDT_SHORT,  &p->clothes_color, 0, NULL, NULL)
996        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 32, SQLDT_SHORT,  &p->weapon, 0, NULL, NULL)
997        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 33, SQLDT_SHORT,  &p->shield, 0, NULL, NULL)
998        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 34, SQLDT_SHORT,  &p->head_top, 0, NULL, NULL)
999        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 35, SQLDT_SHORT,  &p->head_mid, 0, NULL, NULL)
1000        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 36, SQLDT_SHORT,  &p->head_bottom, 0, NULL, NULL)
1001        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 37, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL)
1002        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 38, SQLDT_SHORT,  &p->last_point.x, 0, NULL, NULL)
1003        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 39, SQLDT_SHORT,  &p->last_point.y, 0, NULL, NULL)
1004        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 40, SQLDT_STRING, &save_map, sizeof(save_map), NULL, NULL)
1005        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 41, SQLDT_SHORT,  &p->save_point.x, 0, NULL, NULL)
1006        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 42, SQLDT_SHORT,  &p->save_point.y, 0, NULL, NULL)
1007        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 43, SQLDT_INT,    &p->partner_id, 0, NULL, NULL)
1008        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 44, SQLDT_INT,    &p->father, 0, NULL, NULL)
1009        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 45, SQLDT_INT,    &p->mother, 0, NULL, NULL)
1010        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 46, SQLDT_INT,    &p->child, 0, NULL, NULL)
1011        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 47, SQLDT_INT,    &p->fame, 0, NULL, NULL) )
1012        {
1013                SqlStmt_ShowDebug(stmt);
1014                SqlStmt_Free(stmt);
1015                return 0;
1016        }
1017        if( SQL_ERROR == SqlStmt_NextRow(stmt) )
1018        {
1019                ShowError("Requested non-existant character id: %d!\n", char_id);
1020                SqlStmt_Free(stmt);
1021                return 0;       
1022        }
1023        p->last_point.map = mapindex_name2id(last_map);
1024        p->save_point.map = mapindex_name2id(save_map);
1025
1026        strcat(t_msg, " status");
1027
1028        if (!load_everything) // For quick selection of data when displaying the char menu
1029        {
1030                SqlStmt_Free(stmt);
1031                return 1;
1032        }
1033
1034        //read memo data
1035        //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`)
1036        if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `map`,`x`,`y` FROM `%s` WHERE `char_id`=? ORDER by `memo_id` LIMIT %d", memo_db, MAX_MEMOPOINTS)
1037        ||      SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
1038        ||      SQL_ERROR == SqlStmt_Execute(stmt)
1039        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &point_map, sizeof(point_map), NULL, NULL)
1040        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_SHORT,  &tmp_point.x, 0, NULL, NULL)
1041        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_SHORT,  &tmp_point.y, 0, NULL, NULL) )
1042                SqlStmt_ShowDebug(stmt);
1043
1044        for( i = 0; i < MAX_MEMOPOINTS && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
1045        {
1046                tmp_point.map = mapindex_name2id(point_map);
1047                memcpy(&p->memo_point[i], &tmp_point, sizeof(tmp_point));
1048        }
1049        strcat(t_msg, " memo");
1050
1051        //read inventory
1052        //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`)
1053        StringBuf_Init(&buf);
1054        StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
1055        for( i = 0; i < MAX_SLOTS; ++i )
1056                StringBuf_Printf(&buf, ", `card%d`", i);
1057        StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY);
1058
1059        if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
1060        ||      SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
1061        ||      SQL_ERROR == SqlStmt_Execute(stmt)
1062        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT,    &tmp_item.id, 0, NULL, NULL)
1063        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_SHORT,  &tmp_item.nameid, 0, NULL, NULL)
1064        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_SHORT,  &tmp_item.amount, 0, NULL, NULL)
1065        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 3, SQLDT_USHORT, &tmp_item.equip, 0, NULL, NULL)
1066        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 4, SQLDT_CHAR,   &tmp_item.identify, 0, NULL, NULL)
1067        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR,   &tmp_item.refine, 0, NULL, NULL)
1068        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR,   &tmp_item.attribute, 0, NULL, NULL) )
1069                SqlStmt_ShowDebug(stmt);
1070        for( i = 0; i < MAX_SLOTS; ++i )
1071                if( SQL_ERROR == SqlStmt_BindColumn(stmt, 7+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
1072                        SqlStmt_ShowDebug(stmt);
1073
1074        for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
1075                memcpy(&p->inventory[i], &tmp_item, sizeof(tmp_item));
1076        strcat(t_msg, " inventory");
1077
1078        //read cart
1079        //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`)
1080        StringBuf_Clear(&buf);
1081        StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
1082        for( j = 0; j < MAX_SLOTS; ++j )
1083                StringBuf_Printf(&buf, ", `card%d`", j);
1084        StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART);
1085
1086        if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
1087        ||      SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
1088        ||      SQL_ERROR == SqlStmt_Execute(stmt)
1089        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT,    &tmp_item.id, 0, NULL, NULL)
1090        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_SHORT,  &tmp_item.nameid, 0, NULL, NULL)
1091        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_SHORT,  &tmp_item.amount, 0, NULL, NULL)
1092        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 3, SQLDT_USHORT, &tmp_item.equip, 0, NULL, NULL)
1093        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 4, SQLDT_CHAR,   &tmp_item.identify, 0, NULL, NULL)
1094        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR,   &tmp_item.refine, 0, NULL, NULL)
1095        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR,   &tmp_item.attribute, 0, NULL, NULL) )
1096                SqlStmt_ShowDebug(stmt);
1097        for( i = 0; i < MAX_SLOTS; ++i )
1098                if( SQL_ERROR == SqlStmt_BindColumn(stmt, 7+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
1099                        SqlStmt_ShowDebug(stmt);
1100
1101        for( i = 0; i < MAX_CART && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
1102                memcpy(&p->cart[i], &tmp_item, sizeof(tmp_item));
1103        strcat(t_msg, " cart");
1104
1105        //read skill
1106        //`skill` (`char_id`, `id`, `lv`)
1107        if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `id`, `lv` FROM `%s` WHERE `char_id`=? LIMIT %d", skill_db, MAX_SKILL)
1108        ||      SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
1109        ||      SQL_ERROR == SqlStmt_Execute(stmt)
1110        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_USHORT, &tmp_skill.id, 0, NULL, NULL)
1111        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_USHORT, &tmp_skill.lv, 0, NULL, NULL) )
1112                SqlStmt_ShowDebug(stmt);
1113        tmp_skill.flag = 0;
1114
1115        for( i = 0; i < MAX_SKILL && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
1116        {
1117                if( tmp_skill.id < ARRAYLENGTH(p->skill) )
1118                        memcpy(&p->skill[tmp_skill.id], &tmp_skill, sizeof(tmp_skill));
1119                else
1120                        ShowWarning("mmo_char_fromsql: ignoring invalid skill (id=%u,lv=%u) of character %s (AID=%d,CID=%d)\n", tmp_skill.id, tmp_skill.lv, p->name, p->account_id, p->char_id);
1121        }
1122        strcat(t_msg, " skills");
1123
1124        //read friends
1125        //`friends` (`char_id`, `friend_account`, `friend_id`)
1126        if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT c.`account_id`, c.`char_id`, c.`name` FROM `%s` c LEFT JOIN `%s` f ON f.`friend_account` = c.`account_id` AND f.`friend_id` = c.`char_id` WHERE f.`char_id`=? LIMIT %d", char_db, friend_db, MAX_FRIENDS)
1127        ||      SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
1128        ||      SQL_ERROR == SqlStmt_Execute(stmt)
1129        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT,    &tmp_friend.account_id, 0, NULL, NULL)
1130        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_INT,    &tmp_friend.char_id, 0, NULL, NULL)
1131        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_STRING, &tmp_friend.name, sizeof(tmp_friend.name), NULL, NULL) )
1132                SqlStmt_ShowDebug(stmt);
1133
1134        for( i = 0; i < MAX_FRIENDS && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
1135                memcpy(&p->friends[i], &tmp_friend, sizeof(tmp_friend));
1136        strcat(t_msg, " friends");
1137
1138#ifdef HOTKEY_SAVING
1139        //read hotkeys
1140        //`hotkey` (`char_id`, `hotkey`, `type`, `itemskill_id`, `skill_lvl`
1141        if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `hotkey`, `type`, `itemskill_id`, `skill_lvl` FROM `%s` WHERE `char_id`=?", hotkey_db)
1142        ||      SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
1143        ||      SQL_ERROR == SqlStmt_Execute(stmt)
1144        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT,    &hotkey_num, 0, NULL, NULL)
1145        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_UCHAR,  &tmp_hotkey.type, 0, NULL, NULL)
1146        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_UINT,   &tmp_hotkey.id, 0, NULL, NULL)
1147        ||      SQL_ERROR == SqlStmt_BindColumn(stmt, 3, SQLDT_USHORT, &tmp_hotkey.lv, 0, NULL, NULL) )
1148                SqlStmt_ShowDebug(stmt);
1149
1150        while( SQL_SUCCESS == SqlStmt_NextRow(stmt) )
1151        {
1152                if( hotkey_num >= 0 && hotkey_num < MAX_HOTKEYS )
1153                        memcpy(&p->hotkeys[hotkey_num], &tmp_hotkey, sizeof(tmp_hotkey));
1154                else
1155                        ShowWarning("mmo_char_fromsql: ignoring invalid hotkey (hotkey=%d,type=%u,id=%u,lv=%u) of character %s (AID=%d,CID=%d)\n", hotkey_num, tmp_hotkey.type, tmp_hotkey.id, tmp_hotkey.lv, p->name, p->account_id, p->char_id);
1156        }
1157        strcat(t_msg, " hotkeys");
1158#endif
1159
1160        if (save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfuly!
1161        SqlStmt_Free(stmt);
1162        StringBuf_Destroy(&buf);
1163
1164        cp = (struct mmo_charstatus*)idb_ensure(char_db_, char_id, create_charstatus);
1165        memcpy(cp, p, sizeof(struct mmo_charstatus));
1166        return 1;
1167}
1168
1169//==========================================================================================================
1170int mmo_char_sql_init(void)
1171{
1172        ShowInfo("Begin Initializing.......\n");
1173        char_db_= idb_alloc(DB_OPT_RELEASE_DATA);
1174
1175        if(char_per_account == 0){
1176          ShowStatus("Chars per Account: 'Unlimited'.......\n");
1177        }else{
1178                ShowStatus("Chars per Account: '%d'.......\n", char_per_account);
1179        }
1180
1181        //the 'set offline' part is now in check_login_conn ...
1182        //if the server connects to loginserver
1183        //it will dc all off players
1184        //and send the loginserver the new state....
1185
1186        // Force all users offline in sql when starting char-server
1187        // (useful when servers crashs and don't clean the database)
1188        set_all_offline_sql();
1189
1190        ShowInfo("Finished initilizing.......\n");
1191
1192        return 0;
1193}
1194
1195//-----------------------------------
1196// Function to create a new character
1197//-----------------------------------
1198int 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)
1199{
1200        char name[NAME_LENGTH];
1201        char esc_name[NAME_LENGTH*2+1];
1202        unsigned int i;
1203        int char_id;
1204
1205        safestrncpy(name, name_, NAME_LENGTH);
1206        normalize_name(name,TRIM_CHARS);
1207        Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
1208
1209        // check length of character name
1210        if( name[0] == '\0' )
1211                return -2; // empty character name
1212
1213        // check content of character name
1214        if( remove_control_chars(name) )
1215                return -2; // control chars in name
1216
1217        // check for reserved names
1218        if( strcmpi(name, main_chat_nick) == 0 || strcmpi(name, wisp_server_name) == 0 )
1219                return -1; // nick reserved for internal server messages
1220
1221        // Check Authorised letters/symbols in the name of the character
1222        if( char_name_option == 1 ) { // only letters/symbols in char_name_letters are authorised
1223                for( i = 0; i < NAME_LENGTH && name[i]; i++ )
1224                        if( strchr(char_name_letters, name[i]) == NULL )
1225                                return -2;
1226        } else
1227        if( char_name_option == 2 ) { // letters/symbols in char_name_letters are forbidden
1228                for( i = 0; i < NAME_LENGTH && name[i]; i++ )
1229                        if( strchr(char_name_letters, name[i]) != NULL )
1230                                return -2;
1231        } // else, all letters/symbols are authorised (except control char removed before)
1232
1233        // check name (already in use?)
1234        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `name` = '%s'", char_db, esc_name) ) {
1235                Sql_ShowDebug(sql_handle);
1236                return -2;
1237        }
1238        if( Sql_NumRows(sql_handle) > 0 )
1239                return -1; //  name already exists
1240
1241        //check other inputs
1242        if((slot >= MAX_CHARS) // slots
1243        || (hair_style >= 24) // hair style
1244        || (hair_color >= 9) // hair color
1245        || (str + agi + vit + int_ + dex + luk != 6*5 ) // stats
1246        || (str < 1 || str > 9 || agi < 1 || agi > 9 || vit < 1 || vit > 9 || int_ < 1 || int_ > 9 || dex < 1 || dex > 9 || luk < 1 || luk > 9) // individual stat values
1247        || (str + int_ != 10 || agi + luk != 10 || vit + dex != 10) ) // pairs
1248                return -2; // invalid input
1249
1250        // check the number of already existing chars in this account
1251        if( char_per_account != 0 ) {
1252                if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `account_id` = '%d'", char_db, sd->account_id) )
1253                        Sql_ShowDebug(sql_handle);
1254                if( Sql_NumRows(sql_handle) >= char_per_account )
1255                        return -2; // character account limit exceeded
1256        }
1257
1258        // check char slot
1259        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d'", char_db, sd->account_id, slot) )
1260                Sql_ShowDebug(sql_handle);
1261        if( Sql_NumRows(sql_handle) > 0 )
1262                return -2; // slot already in use
1263
1264        // validation success, log result
1265        if (log_char) {
1266                if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
1267                        "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
1268                        charlog_db, "make new char", sd->account_id, slot, esc_name, str, agi, vit, int_, dex, luk, hair_style, hair_color) )
1269                        Sql_ShowDebug(sql_handle);
1270        }
1271
1272        //Insert the new char entry to the database
1273        if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`,"
1274                "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES ("
1275                "'%d', '%d', '%s', '%d',  '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')",
1276                char_db, sd->account_id , slot, esc_name, start_zeny, str, agi, vit, int_, dex, luk,
1277                (40 * (100 + vit)/100) , (40 * (100 + vit)/100 ),  (11 * (100 + int_)/100), (11 * (100 + int_)/100), hair_style, hair_color,
1278                mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y) )
1279        {
1280                Sql_ShowDebug(sql_handle);
1281                return -2; //No, stop the procedure!
1282        }
1283        //Retrieve the newly auto-generated char id
1284        char_id = (int)Sql_LastInsertId(sql_handle);
1285        //Give the char the default items
1286        if (start_weapon > 0) { //add Start Weapon (Knife?)
1287                if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')", inventory_db, char_id, start_weapon, 1, 1) )
1288                        Sql_ShowDebug(sql_handle);
1289        }
1290        if (start_armor > 0) { //Add default armor (cotton shirt?)
1291                if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')", inventory_db, char_id, start_armor, 1, 1) )
1292                        Sql_ShowDebug(sql_handle);
1293        }
1294
1295        ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, slot, name);
1296        return char_id;
1297}
1298
1299/*----------------------------------------------------------------------------------------------------------*/
1300/* Divorce Players */
1301/*----------------------------------------------------------------------------------------------------------*/
1302int divorce_char_sql(int partner_id1, int partner_id2)
1303{
1304        unsigned char buf[64];
1305
1306        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `partner_id`='0' WHERE `char_id`='%d' OR `char_id`='%d'", char_db, partner_id1, partner_id2) )
1307                Sql_ShowDebug(sql_handle);
1308        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE (`nameid`='%d' OR `nameid`='%d') AND (`char_id`='%d' OR `char_id`='%d')", inventory_db, WEDDING_RING_M, WEDDING_RING_F, partner_id1, partner_id2) )
1309                Sql_ShowDebug(sql_handle);
1310
1311        WBUFW(buf,0) = 0x2b12;
1312        WBUFL(buf,2) = partner_id1;
1313        WBUFL(buf,6) = partner_id2;
1314        mapif_sendall(buf,10);
1315
1316        return 0;
1317}
1318
1319/*----------------------------------------------------------------------------------------------------------*/
1320/* Delete char - davidsiaw */
1321/*----------------------------------------------------------------------------------------------------------*/
1322/* Returns 0 if successful
1323 * Returns < 0 for error
1324 */
1325int delete_char_sql(int char_id)
1326{
1327        char name[NAME_LENGTH];
1328        char esc_name[NAME_LENGTH*2+1]; //Name needs be escaped.
1329        int account_id, party_id, guild_id, hom_id, base_level, partner_id, father_id, mother_id;
1330        char* data;
1331        size_t len;
1332
1333        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `name`,`account_id`,`party_id`,`guild_id`,`base_level`,`homun_id`,`partner_id`,`father`,`mother` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) )
1334                Sql_ShowDebug(sql_handle);
1335
1336        if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
1337        {
1338                ShowError("delete_char_sql: Unable to fetch character data, deletion aborted.\n");
1339                Sql_FreeResult(sql_handle);
1340                return -1;
1341        }
1342
1343        Sql_GetData(sql_handle, 0, &data, &len); safestrncpy(name, data, NAME_LENGTH);
1344        Sql_GetData(sql_handle, 1, &data, NULL); account_id = atoi(data);
1345        Sql_GetData(sql_handle, 2, &data, NULL); party_id = atoi(data);
1346        Sql_GetData(sql_handle, 3, &data, NULL); guild_id = atoi(data);
1347        Sql_GetData(sql_handle, 4, &data, NULL); base_level = atoi(data);
1348        Sql_GetData(sql_handle, 5, &data, NULL); hom_id = atoi(data);
1349        Sql_GetData(sql_handle, 6, &data, NULL); partner_id = atoi(data);
1350        Sql_GetData(sql_handle, 7, &data, NULL); father_id = atoi(data);
1351        Sql_GetData(sql_handle, 8, &data, NULL); mother_id = atoi(data);
1352
1353        Sql_EscapeStringLen(sql_handle, esc_name, name, min(len, NAME_LENGTH));
1354        Sql_FreeResult(sql_handle);
1355
1356        //check for config char del condition [Lupus]
1357        if( ( char_del_level > 0 && base_level >= char_del_level )
1358         || ( char_del_level < 0 && base_level <= -char_del_level )
1359        ) {
1360                        ShowInfo("Char deletion aborted: %s, BaseLevel: %i\n", name, base_level);
1361                        return -1;
1362        }
1363
1364        /* Divorce [Wizputer] */
1365        if( partner_id )
1366                divorce_char_sql(char_id, partner_id);
1367
1368        /* De-addopt [Zephyrus] */
1369        if( father_id || mother_id )
1370        { // Char is Baby
1371                unsigned char buf[64];
1372
1373                if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `child`='0' WHERE `char_id`='%d' OR `char_id`='%d'", char_db, father_id, mother_id) )
1374                        Sql_ShowDebug(sql_handle);
1375                if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '410'AND (`char_id`='%d' OR `char_id`='%d')", skill_db, father_id, mother_id) )
1376                        Sql_ShowDebug(sql_handle);
1377
1378                WBUFW(buf,0) = 0x2b25;
1379                WBUFL(buf,2) = father_id;
1380                WBUFL(buf,6) = mother_id;
1381                WBUFL(buf,10) = char_id; // Baby
1382                mapif_sendall(buf,14);
1383        }
1384
1385        //Make the character leave the party [Skotlex]
1386        if (party_id)
1387                inter_party_leave(party_id, account_id, char_id);
1388
1389        /* delete char's pet */
1390        //Delete the hatched pet if you have one...
1391        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d' AND `incuvate` = '0'", pet_db, char_id) )
1392                Sql_ShowDebug(sql_handle);
1393
1394        //Delete all pets that are stored in eggs (inventory + cart)
1395        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE pet_id IN "
1396                "(SELECT card1|card2<<2 FROM `%s` WHERE char_id = '%d' AND card0 = -256"
1397                " UNION"
1398                " SELECT card1|card2<<2 FROM `%s` WHERE char_id = '%d' AND card0 = -256)",
1399                pet_db, inventory_db, char_id, cart_db, char_id) )
1400                Sql_ShowDebug(sql_handle);
1401
1402        /* remove homunculus */ 
1403        if (hom_id)
1404                mapif_homunculus_delete(hom_id);
1405
1406        /* delete char's friends list */
1407        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d'", friend_db, char_id) )
1408                Sql_ShowDebug(sql_handle);
1409
1410        /* delete char from other's friend list */
1411        //NOTE: Won't this cause problems for people who are already online? [Skotlex]
1412        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `friend_id` = '%d'", friend_db, char_id) )
1413                Sql_ShowDebug(sql_handle);
1414
1415#ifdef HOTKEY_SAVING
1416        /* delete hotkeys */
1417        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", hotkey_db, char_id) )
1418                Sql_ShowDebug(sql_handle);
1419#endif
1420
1421        /* delete inventory */
1422        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", inventory_db, char_id) )
1423                Sql_ShowDebug(sql_handle);
1424
1425        /* delete cart inventory */
1426        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", cart_db, char_id) )
1427                Sql_ShowDebug(sql_handle);
1428
1429        /* delete memo areas */
1430        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", memo_db, char_id) )
1431                Sql_ShowDebug(sql_handle);
1432
1433        /* delete character registry */
1434        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id) )
1435                Sql_ShowDebug(sql_handle);
1436       
1437        /* delete skills */
1438        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", skill_db, char_id) )
1439                Sql_ShowDebug(sql_handle);
1440       
1441#ifdef ENABLE_SC_SAVING
1442        /* status changes */
1443        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, account_id, char_id) )
1444                Sql_ShowDebug(sql_handle);
1445#endif
1446
1447        if (log_char) {
1448                if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`char_msg`,`name`) VALUES (NOW(), '%d', '%d', 'Deleted char (CID %d)', '%s')",
1449                        charlog_db, account_id, 0, char_id, esc_name) )
1450                        Sql_ShowDebug(sql_handle);
1451        }
1452
1453        /* delete character */
1454        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_db, char_id) )
1455                Sql_ShowDebug(sql_handle);
1456
1457        /* No need as we used inter_guild_leave [Skotlex]
1458        // Also delete info from guildtables.
1459        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", guild_member_db, char_id) )
1460                Sql_ShowDebug(sql_handle);
1461        */
1462
1463        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `guild_id` FROM `%s` WHERE `master` = '%s'", guild_db, esc_name) )
1464                Sql_ShowDebug(sql_handle);
1465        else if( Sql_NumRows(sql_handle) > 0 )
1466                mapif_parse_BreakGuild(0,guild_id);
1467        else if( guild_id )
1468                inter_guild_leave(guild_id, account_id, char_id);// Leave your guild.
1469        return 0;
1470}
1471
1472//==========================================================================================================
1473
1474int count_users(void)
1475{
1476        int i, users;
1477
1478        if (login_fd > 0 && session[login_fd]){
1479                users = 0;
1480                for(i = 0; i < MAX_MAP_SERVERS; i++) {
1481                        if (server[i].fd > 0) {
1482                                users += server[i].users;
1483                        }
1484                }
1485                return users;
1486        }
1487        return 0;
1488}
1489
1490/// Writes char data to the buffer in the format used by the client.
1491/// Used in packets 0x6b (chars info) and 0x6d (new char info)
1492/// Returns the size (106 or 108)
1493int mmo_char_tobuf(uint8* buf, struct mmo_charstatus* p)
1494{
1495        if( buf == NULL || p == NULL )
1496                return 0;
1497
1498        WBUFL(buf,0) = p->char_id;
1499        WBUFL(buf,4) = min(p->base_exp, LONG_MAX);
1500        WBUFL(buf,8) = p->zeny;
1501        WBUFL(buf,12) = min(p->job_exp, LONG_MAX);
1502        WBUFL(buf,16) = p->job_level;
1503        WBUFL(buf,20) = 0; // probably opt1
1504        WBUFL(buf,24) = 0; // probably opt2
1505        WBUFL(buf,28) = p->option;
1506        WBUFL(buf,32) = p->karma;
1507        WBUFL(buf,36) = p->manner;
1508        WBUFW(buf,40) = min(p->status_point, SHRT_MAX);
1509        WBUFW(buf,42) = min(p->hp, SHRT_MAX);
1510        WBUFW(buf,44) = min(p->max_hp, SHRT_MAX);
1511        WBUFW(buf,46) = min(p->sp, SHRT_MAX);
1512        WBUFW(buf,48) = min(p->max_sp, SHRT_MAX);
1513        WBUFW(buf,50) = DEFAULT_WALK_SPEED; // p->speed;
1514        WBUFW(buf,52) = p->class_;
1515        WBUFW(buf,54) = p->hair;
1516        WBUFW(buf,56) = p->option&0x20 ? 0 : p->weapon; //When the weapon is sent and your option is riding, the client crashes on login!?
1517        WBUFW(buf,58) = p->base_level;
1518        WBUFW(buf,60) = min(p->skill_point, SHRT_MAX);
1519        WBUFW(buf,62) = p->head_bottom;
1520        WBUFW(buf,64) = p->shield;
1521        WBUFW(buf,66) = p->head_top;
1522        WBUFW(buf,68) = p->head_mid;
1523        WBUFW(buf,70) = p->hair_color;
1524        WBUFW(buf,72) = p->clothes_color;
1525        memcpy(WBUFP(buf,74), p->name, NAME_LENGTH);
1526        WBUFB(buf,98) = min(p->str, UCHAR_MAX);
1527        WBUFB(buf,99) = min(p->agi, UCHAR_MAX);
1528        WBUFB(buf,100) = min(p->vit, UCHAR_MAX);
1529        WBUFB(buf,101) = min(p->int_, UCHAR_MAX);
1530        WBUFB(buf,102) = min(p->dex, UCHAR_MAX);
1531        WBUFB(buf,103) = min(p->luk, UCHAR_MAX);
1532        WBUFW(buf,104) = p->slot;
1533        if (char_rename) {
1534                WBUFW(buf,106) = 1;// Rename bit (0=rename,1=no rename)
1535                return 108;
1536        } else {
1537                return 106;
1538        }
1539}
1540
1541int mmo_char_send006b(int fd, struct char_session_data* sd)
1542{
1543        int j;
1544
1545        if (save_log)
1546                ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id);
1547
1548        j = 24; // offset
1549        WFIFOHEAD(fd,j + MAX_CHARS*108); // or 106(!)
1550        WFIFOW(fd,0) = 0x6b;
1551        memset(WFIFOP(fd,4), 0, 20); // unknown bytes
1552        j+=mmo_chars_fromsql(sd, WFIFOP(fd,j));
1553        WFIFOW(fd,2) = j; // packet len
1554        WFIFOSET(fd,j);
1555
1556        return 0;
1557}
1558
1559int char_married(int pl1, int pl2)
1560{
1561        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `partner_id` FROM `%s` WHERE `char_id` = '%d'", char_db, pl1) )
1562                Sql_ShowDebug(sql_handle);
1563        else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
1564        {
1565                char* data;
1566
1567                Sql_GetData(sql_handle, 0, &data, NULL);
1568                if( pl2 == atoi(data) )
1569                {
1570                        Sql_FreeResult(sql_handle);
1571                        return 1;
1572                }
1573        }
1574        Sql_FreeResult(sql_handle);
1575        return 0;
1576}
1577
1578int char_child(int parent_id, int child_id)
1579{
1580        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `child` FROM `%s` WHERE `char_id` = '%d'", char_db, parent_id) )
1581                Sql_ShowDebug(sql_handle);
1582        else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
1583        {
1584                char* data;
1585
1586                Sql_GetData(sql_handle, 0, &data, NULL);
1587                if( child_id == atoi(data) )
1588                {
1589                        Sql_FreeResult(sql_handle);
1590                        return 1;
1591                }
1592        }
1593        Sql_FreeResult(sql_handle);
1594        return 0;
1595}
1596
1597int char_family(int pl1, int pl2, int pl3)
1598{
1599        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`partner_id`,`child` FROM `%s` WHERE `char_id` IN ('%d','%d','%d')", char_db, pl1, pl2, pl3) )
1600                Sql_ShowDebug(sql_handle);
1601        else while( SQL_SUCCESS == Sql_NextRow(sql_handle) )
1602        {
1603                int charid;
1604                int partnerid;
1605                int childid;
1606                char* data;
1607
1608                Sql_GetData(sql_handle, 0, &data, NULL); charid = atoi(data);
1609                Sql_GetData(sql_handle, 1, &data, NULL); partnerid = atoi(data);
1610                Sql_GetData(sql_handle, 2, &data, NULL); childid = atoi(data);
1611
1612                if( (pl1 == charid    && ((pl2 == partnerid && pl3 == childid  ) || (pl2 == childid   && pl3 == partnerid))) ||
1613                        (pl1 == partnerid && ((pl2 == charid    && pl3 == childid  ) || (pl2 == childid   && pl3 == charid   ))) ||
1614                        (pl1 == childid   && ((pl2 == charid    && pl3 == partnerid) || (pl2 == partnerid && pl3 == charid   ))) )
1615                {
1616                        Sql_FreeResult(sql_handle);
1617                        return childid;
1618                }
1619        }
1620        Sql_FreeResult(sql_handle);
1621        return 0;
1622}
1623
1624static void char_auth_ok(int fd, struct char_session_data *sd)
1625{
1626        struct online_char_data* character;
1627        if (max_connect_user && count_users() >= max_connect_user && isGM(sd->account_id) < gm_allow_level)
1628        {
1629                // refuse connection (over populated)
1630                WFIFOW(fd,0) = 0x6c;
1631                WFIFOW(fd,2) = 0;
1632                WFIFOSET(fd,3);
1633                return;
1634        }
1635
1636        if( online_check && (character = (struct online_char_data*)idb_get(online_char_db, sd->account_id)) != NULL )
1637        {       // check if character is not online already. [Skotlex]
1638                if (character->server > -1)
1639                {       //Character already online. KICK KICK KICK
1640                        mapif_disconnectplayer(server[character->server].fd, character->account_id, character->char_id, 2);
1641                        if (character->waiting_disconnect == -1)
1642                                character->waiting_disconnect = add_timer(gettick()+20000, chardb_waiting_disconnect, character->account_id, 0);
1643                        WFIFOW(fd,0) = 0x81;
1644                        WFIFOB(fd,2) = 8;
1645                        WFIFOSET(fd,3);
1646                        return;
1647                }
1648                if (character->fd >= 0 && character->fd != fd)
1649                {       //There's already a connection from this account that hasn't picked a char yet.
1650                        WFIFOW(fd,0) = 0x81;
1651                        WFIFOB(fd,2) = 8;
1652                        WFIFOSET(fd,3);
1653                        return;
1654                }
1655                character->fd = fd;
1656        }
1657
1658        if (login_fd > 0) {
1659                // request to login-server to obtain e-mail/time limit
1660                WFIFOHEAD(login_fd,6);
1661                WFIFOW(login_fd,0) = 0x2716;
1662                WFIFOL(login_fd,2) = sd->account_id;
1663                WFIFOSET(login_fd,6);
1664        }
1665
1666        // mark session as 'authed'
1667        sd->auth = true;
1668
1669        // set char online on charserver
1670        set_char_charselect(sd->account_id);
1671
1672        // send characters to player
1673        mmo_char_send006b(fd, sd);
1674}
1675
1676int send_accounts_tologin(int tid, unsigned int tick, int id, intptr data);
1677
1678int parse_fromlogin(int fd)
1679{
1680        int i;
1681        struct char_session_data *sd;
1682
1683        // only login-server can have an access to here.
1684        // so, if it isn't the login-server, we disconnect the session.
1685        if( fd != login_fd )
1686                set_eof(fd);
1687
1688        if(session[fd]->flag.eof) {
1689                if (fd == login_fd) {
1690                        ShowWarning("Connection to login-server lost (connection #%d).\n", fd);
1691                        login_fd = -1;
1692                }
1693                do_close(fd);
1694                return 0;
1695        }
1696
1697        sd = (struct char_session_data*)session[fd]->session_data;
1698
1699        while(RFIFOREST(fd) >= 2)
1700        {
1701                uint16 command = RFIFOW(fd,0);
1702
1703                switch( command )
1704                {
1705
1706                // acknowledgement of connect-to-loginserver request
1707                case 0x2711:
1708                        if (RFIFOREST(fd) < 3)
1709                                return 0;
1710
1711                        if (RFIFOB(fd,2)) {
1712                                //printf("connect login server error : %d\n", RFIFOB(fd, 2));
1713                                ShowError("Can not connect to login-server.\n");
1714                                ShowError("The server communication passwords (default s1/p1) are probably invalid.\n");
1715                                ShowError("Also, please make sure your login db has the correct communication username/passwords and the gender of the account is S.\n");
1716                                ShowError("The communication passwords are set in map_athena.conf and char_athena.conf\n");
1717                        }else {
1718                                ShowStatus("Connected to login-server (connection #%d).\n", fd);
1719                               
1720                                //Send online accounts to login server.
1721                                send_accounts_tologin(-1, gettick(), 0, 0);
1722                       
1723                                // if no map-server already connected, display a message...
1724                                ARR_FIND( 0, MAX_MAP_SERVERS, i, server[i].fd > 0 && server[i].map[0] );
1725                                if( i == MAX_MAP_SERVERS )
1726                                        ShowStatus("Awaiting maps from map-server.\n");
1727                        }
1728                        RFIFOSKIP(fd,3);
1729                break;
1730
1731                // acknowledgement of account authentication request
1732                case 0x2713:
1733                        if (RFIFOREST(fd) < 59)
1734                                return 0;
1735                {
1736                        int account_id = RFIFOL(fd,2);
1737                        int login_id1 = RFIFOL(fd,6);
1738                        int login_id2 = RFIFOL(fd,10);
1739                        bool result = RFIFOB(fd,14);
1740                        const char* email = (const char*)RFIFOP(fd,15);
1741                        time_t expiration_time = (time_t)RFIFOL(fd,55);
1742
1743                        // find the session with this account id
1744                        ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) &&
1745                                sd->account_id == account_id && sd->login_id1 == login_id1 && sd->login_id2 == login_id2 );
1746                        if( i < fd_max )
1747                        {
1748                                if( result ) { // failure
1749                                        WFIFOHEAD(i,3);
1750                                        WFIFOW(i,0) = 0x6c;
1751                                        WFIFOB(i,2) = 0x42;
1752                                        WFIFOSET(i,3);
1753                                } else { // success
1754                                        memcpy(sd->email, email, 40);
1755                                        sd->expiration_time = expiration_time;
1756                                        char_auth_ok(i, sd);
1757                                }
1758                        }
1759                }
1760                        RFIFOSKIP(fd,59);
1761                break;
1762
1763                // acknowledgement of e-mail/limited time request
1764                case 0x2717:
1765                        if (RFIFOREST(fd) < 50)
1766                                return 0;
1767
1768                        // find the session with this account id
1769                        ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2) );
1770                        if( i < fd_max )
1771                        {
1772                                memcpy(sd->email, RFIFOP(fd,6), 40);
1773                                sd->expiration_time = (time_t)RFIFOL(fd,46);
1774                        }
1775                        RFIFOSKIP(fd,50);
1776                break;
1777
1778                // login-server alive packet
1779                case 0x2718:
1780                        if (RFIFOREST(fd) < 2)
1781                                return 0;
1782                        RFIFOSKIP(fd,2);
1783                break;
1784
1785                // changesex reply
1786                case 0x2723:
1787                        if (RFIFOREST(fd) < 7)
1788                                return 0;
1789                {
1790                        unsigned char buf[16];
1791
1792                        int acc = RFIFOL(fd,2);
1793                        int sex = RFIFOB(fd,6);
1794                        RFIFOSKIP(fd,7);
1795
1796                        if( acc > 0 )
1797                        {
1798                                int char_id[MAX_CHARS];
1799                                int class_[MAX_CHARS];
1800                                int guild_id[MAX_CHARS];
1801                                int num;
1802                                int i;
1803                                char* data;
1804
1805                                struct auth_node* node = (struct auth_node*)idb_get(auth_db, acc);
1806                                if( node != NULL )
1807                                        node->sex = sex;
1808
1809                                // get characters
1810                                if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`class`,`guild_id` FROM `%s` WHERE `account_id` = '%d'", char_db, acc) )
1811                                        Sql_ShowDebug(sql_handle);
1812                                for( i = 0; i < MAX_CHARS && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i )
1813                                {
1814                                        Sql_GetData(sql_handle, 0, &data, NULL); char_id[i] = atoi(data);
1815                                        Sql_GetData(sql_handle, 1, &data, NULL); class_[i] = atoi(data);
1816                                        Sql_GetData(sql_handle, 2, &data, NULL); guild_id[i] = atoi(data);
1817                                }
1818                                num = i;
1819                                for( i = 0; i < num; ++i )
1820                                {
1821                                        if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER ||
1822                                                class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY ||
1823                                                class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER )
1824                                        {
1825                                                // job modification
1826                                                if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER )
1827                                                        class_[i] = (sex ? JOB_BARD : JOB_DANCER);
1828                                                else if( class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY )
1829                                                        class_[i] = (sex ? JOB_CLOWN : JOB_GYPSY);
1830                                                else if( class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER )
1831                                                        class_[i] = (sex ? JOB_BABY_BARD : JOB_BABY_DANCER);
1832                                                // remove specifical skills of classes 19,20 4020,4021 and 4042,4043
1833                                                if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `skill_point` = `skill_point` +"
1834                                                        " (SELECT SUM(lv) FROM `%s` WHERE `char_id` = '%d' AND `id` >= '315' AND `id` <= '330' AND `lv` > '0')"
1835                                                        " WHERE `char_id` = '%d'",
1836                                                        char_db, skill_db, char_id[i], char_id[i]) )
1837                                                        Sql_ShowDebug(sql_handle);
1838                                                if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `id` >= '315' AND `id` <= '330'", skill_db, char_id[i]) )
1839                                                        Sql_ShowDebug(sql_handle);
1840                                        }
1841                                        // to avoid any problem with equipment and invalid sex, equipment is unequiped.
1842                                        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `equip` = '0' WHERE `char_id` = '%d'", inventory_db, char_id[i]) )
1843                                                Sql_ShowDebug(sql_handle);
1844                                        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'", char_db, class_[i], char_id[i]) )
1845                                                Sql_ShowDebug(sql_handle);
1846
1847                                        if( guild_id[i] )// If there is a guild, update the guild_member data [Skotlex]
1848                                                inter_guild_sex_changed(guild_id[i], acc, char_id[i], sex);
1849                                }
1850                                Sql_FreeResult(sql_handle);
1851                        }
1852
1853                        // disconnect player if online on char-server
1854                        ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == acc );
1855                        if( i < fd_max )
1856                                set_eof(i);
1857
1858                        // notify all mapservers about this change
1859                        WBUFW(buf,0) = 0x2b0d;
1860                        WBUFL(buf,2) = acc;
1861                        WBUFB(buf,6) = sex;
1862                        mapif_sendall(buf, 7);
1863                }
1864                break;
1865
1866                // reply to an account_reg2 registry request
1867                case 0x2729:
1868                        if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
1869                                return 0;
1870
1871                {       //Receive account_reg2 registry, forward to map servers.
1872                        unsigned char buf[ACCOUNT_REG2_NUM*(256+32+2)+16];
1873                        memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2));
1874                        WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex]
1875                        mapif_sendall(buf, WBUFW(buf,2));
1876                }
1877                        RFIFOSKIP(fd, RFIFOW(fd,2));
1878                break;
1879
1880                // State change of account/ban notification (from login-server)
1881                case 0x2731:
1882                        if (RFIFOREST(fd) < 11)
1883                                return 0;
1884
1885                {       // send to all map-servers to disconnect the player
1886                        unsigned char buf[11];
1887                        WBUFW(buf,0) = 0x2b14;
1888                        WBUFL(buf,2) = RFIFOL(fd,2);
1889                        WBUFB(buf,6) = RFIFOB(fd,6); // 0: change of statut, 1: ban
1890                        WBUFL(buf,7) = RFIFOL(fd,7); // status or final date of a banishment
1891                        mapif_sendall(buf, 11);
1892                }
1893                        // disconnect player if online on char-server
1894                        ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2) );
1895                        if( i < fd_max )
1896                                set_eof(i);
1897
1898                        RFIFOSKIP(fd,11);
1899                break;
1900
1901                // gm account information from login server
1902                case 0x2732:
1903                        if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
1904                                return 0;
1905
1906                        if(!char_gm_read) {
1907                                unsigned char buf[32000]; //FIXME: this will crash
1908                                if (gm_account != NULL)
1909                                        aFree(gm_account);
1910                                gm_account = (struct gm_account*)aCalloc(sizeof(struct gm_account) * ((RFIFOW(fd,2) - 4) / 5), 1);
1911                                GM_num = 0;
1912                                for (i = 4; i < RFIFOW(fd,2); i = i + 5) {
1913                                        gm_account[GM_num].account_id = RFIFOL(fd,i);
1914                                        gm_account[GM_num].level = (int)RFIFOB(fd,i+4);
1915                                        //printf("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level);
1916                                        GM_num++;
1917                                }
1918                                ShowStatus("From login-server: receiving information of %d GM accounts.\n", GM_num);
1919                                // send new gm acccounts level to map-servers
1920                                memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2));
1921                                WBUFW(buf,0) = 0x2b15;
1922                                mapif_sendall(buf, RFIFOW(fd,2));
1923                        }
1924
1925                        RFIFOSKIP(fd,RFIFOW(fd,2));
1926                break;
1927
1928                // Login server request to kick a character out. [Skotlex]
1929                case 0x2734:
1930                        if (RFIFOREST(fd) < 6)
1931                                return 0;
1932                {
1933                        int aid = RFIFOL(fd,2);
1934                        struct online_char_data* character = (struct online_char_data*)idb_get(online_char_db, aid);
1935                        RFIFOSKIP(fd,6);
1936                        if( character != NULL )
1937                        {// account is already marked as online!
1938                                if( character->server > -1 )
1939                                {       //Kick it from the map server it is on.
1940                                        mapif_disconnectplayer(server[character->server].fd, character->account_id, character->char_id, 2);
1941                                        if (character->waiting_disconnect == -1)
1942                                                character->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, chardb_waiting_disconnect, character->account_id, 0);
1943                                }
1944                                else
1945                                {// Manual kick from char server.
1946                                        struct char_session_data *tsd;
1947                                        int i;
1948                                        ARR_FIND( 0, fd_max, i, session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid );
1949                                        if( i < fd_max )
1950                                        {
1951                                                WFIFOHEAD(i,3);
1952                                                WFIFOW(i,0) = 0x81;
1953                                                WFIFOB(i,2) = 2; // "Someone has already logged in with this id"
1954                                                WFIFOSET(i,3);
1955                                                set_eof(i);
1956                                        }
1957                                        else //Shouldn't happen, but just in case.
1958                                                set_char_offline(-1, aid);
1959                                }
1960                        }
1961                }
1962                break;
1963
1964                // ip address update signal from login server
1965                case 0x2735:
1966                {
1967                        unsigned char buf[2];
1968                        uint32 new_ip = 0;
1969
1970                        WBUFW(buf,0) = 0x2b1e;
1971                        mapif_sendall(buf, 2);
1972
1973                        new_ip = host2ip(login_ip_str);
1974                        if (new_ip && new_ip != login_ip) //Update login ip, too.
1975                                login_ip = new_ip;
1976
1977                        new_ip = host2ip(char_ip_str);
1978                        if (new_ip && new_ip != char_ip)
1979                        {       //Update ip.
1980                                char_ip = new_ip;
1981                                ShowInfo("Updating IP for [%s].\n", char_ip_str);
1982                                // notify login server about the change
1983                                WFIFOHEAD(fd,6);
1984                                WFIFOW(fd,0) = 0x2736;
1985                                WFIFOL(fd,2) = htonl(char_ip);
1986                                WFIFOSET(fd,6);
1987                        }
1988                }
1989
1990                        RFIFOSKIP(fd,2);
1991                break;
1992
1993                default:
1994                        ShowError("Unknown packet 0x%04x received from login server, disconnecting.\n", command);
1995                        set_eof(fd);
1996                        return 0;
1997                }
1998        }
1999
2000        RFIFOFLUSH(fd);
2001        return 0;
2002}
2003
2004int request_accreg2(int account_id, int char_id)
2005{
2006        if (login_fd > 0) {
2007                WFIFOHEAD(login_fd,10);
2008                WFIFOW(login_fd,0) = 0x272e;
2009                WFIFOL(login_fd,2) = account_id;
2010                WFIFOL(login_fd,6) = char_id;
2011                WFIFOSET(login_fd,10);
2012                return 1;
2013        }
2014        return 0;
2015}
2016
2017//Send packet forward to login-server for account saving
2018int save_accreg2(unsigned char* buf, int len)
2019{
2020        if (login_fd > 0) {
2021                WFIFOHEAD(login_fd,len+4);
2022                memcpy(WFIFOP(login_fd,4), buf, len);
2023                WFIFOW(login_fd,0) = 0x2728;
2024                WFIFOW(login_fd,2) = len+4;
2025                WFIFOSET(login_fd,len+4);
2026                return 1;
2027        }
2028        return 0;
2029}
2030
2031void char_read_fame_list(void)
2032{
2033        int i;
2034        char* data;
2035        size_t len;
2036
2037        // Empty ranking lists
2038        memset(smith_fame_list, 0, sizeof(smith_fame_list));
2039        memset(chemist_fame_list, 0, sizeof(chemist_fame_list));
2040        memset(taekwon_fame_list, 0, sizeof(taekwon_fame_list));
2041        // Build Blacksmith ranking list
2042        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`fame`,`name` FROM `%s` WHERE `fame`>0 AND (`class`='%d' OR `class`='%d' OR `class`='%d') ORDER BY `fame` DESC LIMIT 0,%d", char_db, JOB_BLACKSMITH, JOB_WHITESMITH, JOB_BABY_BLACKSMITH, fame_list_size_smith) )
2043                Sql_ShowDebug(sql_handle);
2044        for( i = 0; i < fame_list_size_smith && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i )
2045        {
2046                // char_id
2047                Sql_GetData(sql_handle, 0, &data, NULL);
2048                smith_fame_list[i].id = atoi(data);
2049                // fame
2050                Sql_GetData(sql_handle, 1, &data, &len);
2051                smith_fame_list[i].fame = atoi(data);
2052                // name
2053                Sql_GetData(sql_handle, 2, &data, &len);
2054                memcpy(smith_fame_list[i].name, data, min(len, NAME_LENGTH));
2055        }
2056        // Build Alchemist ranking list
2057        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`fame`,`name` FROM `%s` WHERE `fame`>0 AND (`class`='%d' OR `class`='%d' OR `class`='%d') ORDER BY `fame` DESC LIMIT 0,%d", char_db, JOB_ALCHEMIST, JOB_CREATOR, JOB_BABY_ALCHEMIST, fame_list_size_chemist) )
2058                Sql_ShowDebug(sql_handle);
2059        for( i = 0; i < fame_list_size_chemist && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i )
2060        {
2061                // char_id
2062                Sql_GetData(sql_handle, 0, &data, NULL);
2063                chemist_fame_list[i].id = atoi(data);
2064                // fame
2065                Sql_GetData(sql_handle, 1, &data, &len);
2066                chemist_fame_list[i].fame = atoi(data);
2067                // name
2068                Sql_GetData(sql_handle, 2, &data, &len);
2069                memcpy(chemist_fame_list[i].name, data, min(len, NAME_LENGTH));
2070        }
2071        // Build Taekwon ranking list
2072        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`fame`,`name` FROM `%s` WHERE `fame`>0 AND (`class`='%d') ORDER BY `fame` DESC LIMIT 0,%d", char_db, JOB_TAEKWON, fame_list_size_taekwon) )
2073                Sql_ShowDebug(sql_handle);
2074        for( i = 0; i < fame_list_size_taekwon && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i )
2075        {
2076                // char_id
2077                Sql_GetData(sql_handle, 0, &data, NULL);
2078                taekwon_fame_list[i].id = atoi(data);
2079                // fame
2080                Sql_GetData(sql_handle, 1, &data, &len);
2081                taekwon_fame_list[i].fame = atoi(data);
2082                // name
2083                Sql_GetData(sql_handle, 2, &data, &len);
2084                memcpy(taekwon_fame_list[i].name, data, min(len, NAME_LENGTH));
2085        }
2086        Sql_FreeResult(sql_handle);
2087}
2088
2089// Send map-servers the fame ranking lists
2090int char_send_fame_list(int fd)
2091{
2092        int i, len = 8;
2093        unsigned char buf[32000];
2094       
2095        WBUFW(buf,0) = 0x2b1b;
2096
2097        for(i = 0; i < fame_list_size_smith && smith_fame_list[i].id; i++) {
2098                memcpy(WBUFP(buf, len), &smith_fame_list[i], sizeof(struct fame_list));
2099                len += sizeof(struct fame_list);
2100        }
2101        // add blacksmith's block length
2102        WBUFW(buf, 6) = len;
2103
2104        for(i = 0; i < fame_list_size_chemist && chemist_fame_list[i].id; i++) {
2105                memcpy(WBUFP(buf, len), &chemist_fame_list[i], sizeof(struct fame_list));
2106                len += sizeof(struct fame_list);
2107        }
2108        // add alchemist's block length
2109        WBUFW(buf, 4) = len;
2110
2111        for(i = 0; i < fame_list_size_taekwon && taekwon_fame_list[i].id; i++) {
2112                memcpy(WBUFP(buf, len), &taekwon_fame_list[i], sizeof(struct fame_list));
2113                len += sizeof(struct fame_list);
2114        }
2115        // add total packet length
2116        WBUFW(buf, 2) = len;
2117
2118        if (fd != -1)
2119                mapif_send(fd, buf, len);
2120        else
2121                mapif_sendall(buf, len);
2122        return 0;
2123}
2124
2125void char_update_fame_list(int type, int index, int fame)
2126{
2127        unsigned char buf[8];
2128        WBUFW(buf,0) = 0x2b22;
2129        WBUFB(buf,2) = type;
2130        WBUFB(buf,3) = index;
2131        WBUFL(buf,4) = fame;
2132        mapif_sendall(buf, 8);
2133}
2134
2135//Loads a character's name and stores it in the buffer given (must be NAME_LENGTH in size)
2136//Returns 1 on found, 0 on not found (buffer is filled with Unknown char name)
2137int char_loadName(int char_id, char* name)
2138{
2139        char* data;
2140        size_t len;
2141
2142        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `name` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) )
2143                Sql_ShowDebug(sql_handle);
2144        else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
2145        {
2146                Sql_GetData(sql_handle, 0, &data, &len);
2147                memset(name, 0, NAME_LENGTH);
2148                memcpy(name, data, min(len, NAME_LENGTH));
2149                return 1;
2150        }
2151        else
2152        {
2153                memcpy(name, unknown_char_name, NAME_LENGTH);
2154        }
2155        return 0;
2156}
2157
2158int search_mapserver(unsigned short map, uint32 ip, uint16 port);
2159
2160int parse_frommap(int fd)
2161{
2162        int i = 0, j = 0;
2163        int id;
2164
2165        // Sometimes fd=0, and it will cause server crash. Don't know why. :(
2166        if (fd <= 0) {
2167                ShowError("parse_frommap error fd=%d\n", fd);
2168                return 0;
2169        }
2170
2171        ARR_FIND( 0, MAX_MAP_SERVERS, id, server[id].fd == fd );
2172        if(id == MAX_MAP_SERVERS)
2173                set_eof(fd);
2174        if(session[fd]->flag.eof) {
2175                if (id < MAX_MAP_SERVERS) {
2176                        unsigned char buf[16384];
2177                        ShowStatus("Map-server %d (session #%d) has disconnected.\n", id, fd);
2178                        //Notify other map servers that this one is gone. [Skotlex]
2179                        WBUFW(buf,0) = 0x2b20;
2180                        WBUFL(buf,4) = htonl(server[id].ip);
2181                        WBUFW(buf,8) = htons(server[id].port);
2182                        j = 0;
2183                        for(i = 0; i < MAX_MAP_PER_SERVER; i++)
2184                                if (server[id].map[i])
2185                                        WBUFW(buf,10+(j++)*4) = server[id].map[i];
2186                        if (j > 0) {
2187                                WBUFW(buf,2) = j * 4 + 10;
2188                                mapif_sendallwos(fd, buf, WBUFW(buf,2));
2189                        }
2190                        memset(&server[id], 0, sizeof(struct mmo_map_server));
2191                        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ragsrvinfo` WHERE `index`='%d'", server[id].fd) )
2192                                Sql_ShowDebug(sql_handle);
2193                        server[id].fd = -1;
2194                        online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server.
2195                }
2196                do_close(fd);
2197                return 0;
2198        }
2199
2200        while(RFIFOREST(fd) >= 2)
2201        {
2202                switch(RFIFOW(fd, 0))
2203                {
2204
2205                case 0x2af7: // request from map-server to reload GM accounts. Transmission to login-server
2206                        if(char_gm_read) //Re-read gm accounts.
2207                                read_gm_account();
2208                        //Send to login request to reload gm accounts.
2209                        else if (login_fd > 0) { // don't send request if no login-server
2210                                WFIFOHEAD(login_fd,2);
2211                                WFIFOW(login_fd,0) = 0x2709;
2212                                WFIFOSET(login_fd,2);
2213                        }
2214                        RFIFOSKIP(fd,2);
2215                break;
2216
2217                case 0x2afa: // Receiving map names list from the map-server
2218                        if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
2219                                return 0;
2220
2221                        memset(server[id].map, 0, sizeof(server[id].map));
2222                        j = 0;
2223                        for(i = 4; i < RFIFOW(fd,2); i += 4) {
2224                                server[id].map[j] = RFIFOW(fd,i);
2225                                j++;
2226                        }
2227
2228                        ShowStatus("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n",
2229                                                id, j, CONVIP(server[id].ip), server[id].port);
2230                        ShowStatus("Map-server %d loading complete.\n", id);
2231                       
2232                        // send name for wisp to player
2233                        WFIFOHEAD(fd, 3 + NAME_LENGTH);
2234                        WFIFOW(fd,0) = 0x2afb;
2235                        WFIFOB(fd,2) = 0;
2236                        memcpy(WFIFOP(fd,3), wisp_server_name, NAME_LENGTH);
2237                        WFIFOSET(fd,3+NAME_LENGTH);
2238
2239                        char_send_fame_list(fd); //Send fame list.
2240
2241                        {
2242                        unsigned char buf[16384];
2243                        int x;
2244                        if (j == 0) {
2245                                ShowWarning("Map-server %d has NO maps.\n", id);
2246                        } else {
2247                                // Transmitting maps information to the other map-servers
2248                                WBUFW(buf,0) = 0x2b04;
2249                                WBUFW(buf,2) = j * 4 + 10;
2250                                WBUFL(buf,4) = htonl(server[id].ip);
2251                                WBUFW(buf,8) = htons(server[id].port);
2252                                memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 4);
2253                                mapif_sendallwos(fd, buf, WBUFW(buf,2));
2254                        }
2255                        // Transmitting the maps of the other map-servers to the new map-server
2256                        for(x = 0; x < MAX_MAP_SERVERS; x++) {
2257                                if (server[x].fd > 0 && x != id) {
2258                                        WFIFOHEAD(fd,10 +4*MAX_MAP_PER_SERVER);
2259                                        WFIFOW(fd,0) = 0x2b04;
2260                                        WFIFOL(fd,4) = htonl(server[x].ip);
2261                                        WFIFOW(fd,8) = htons(server[x].port);
2262                                        j = 0;
2263                                        for(i = 0; i < MAX_MAP_PER_SERVER; i++)
2264                                                if (server[x].map[i])
2265                                                        WFIFOW(fd,10+(j++)*4) = server[x].map[i];
2266                                        if (j > 0) {
2267                                                WFIFOW(fd,2) = j * 4 + 10;
2268                                                WFIFOSET(fd,WFIFOW(fd,2));
2269                                        }
2270                                }
2271                        }
2272                        }
2273                        RFIFOSKIP(fd,RFIFOW(fd,2));
2274                break;
2275
2276                case 0x2afc: //Packet command is now used for sc_data request. [Skotlex]
2277                        if (RFIFOREST(fd) < 10)
2278                                return 0;
2279                {
2280#ifdef ENABLE_SC_SAVING
2281                        int aid, cid;
2282                        aid = RFIFOL(fd,2);
2283                        cid = RFIFOL(fd,6);
2284                        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT type, tick, val1, val2, val3, val4 from `%s` WHERE `account_id` = '%d' AND `char_id`='%d'",
2285                                scdata_db, aid, cid) )
2286                        {
2287                                Sql_ShowDebug(sql_handle);
2288                                break;
2289                        }
2290                        if( Sql_NumRows(sql_handle) > 0 )
2291                        {
2292                                struct status_change_data scdata;
2293                                int count;
2294                                char* data;
2295
2296                                WFIFOHEAD(fd,14+50*sizeof(struct status_change_data));
2297                                WFIFOW(fd,0) = 0x2b1d;
2298                                WFIFOL(fd,4) = aid;
2299                                WFIFOL(fd,8) = cid;
2300                                for( count = 0; count < 50 && SQL_SUCCESS == Sql_NextRow(sql_handle); ++count )
2301                                {
2302                                        Sql_GetData(sql_handle, 0, &data, NULL); scdata.type = atoi(data);
2303                                        Sql_GetData(sql_handle, 1, &data, NULL); scdata.tick = atoi(data);
2304                                        Sql_GetData(sql_handle, 2, &data, NULL); scdata.val1 = atoi(data);
2305                                        Sql_GetData(sql_handle, 3, &data, NULL); scdata.val2 = atoi(data);
2306                                        Sql_GetData(sql_handle, 4, &data, NULL); scdata.val3 = atoi(data);
2307                                        Sql_GetData(sql_handle, 5, &data, NULL); scdata.val4 = atoi(data);
2308                                        memcpy(WFIFOP(fd, 14+count*sizeof(struct status_change_data)), &scdata, sizeof(struct status_change_data));
2309                                }
2310                                if (count >= 50)
2311                                        ShowWarning("Too many status changes for %d:%d, some of them were not loaded.\n", aid, cid);
2312                                if (count > 0)
2313                                {
2314                                        WFIFOW(fd,2) = 14 + count*sizeof(struct status_change_data);
2315                                        WFIFOW(fd,12) = count;
2316                                        WFIFOSET(fd,WFIFOW(fd,2));
2317
2318                                        //Clear the data once loaded.
2319                                        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid) )
2320                                                Sql_ShowDebug(sql_handle);
2321                                }
2322                        }
2323                        Sql_FreeResult(sql_handle);
2324#endif
2325                        RFIFOSKIP(fd, 10);
2326                }
2327                break;
2328
2329                case 0x2afe: //set MAP user count
2330                        if (RFIFOREST(fd) < 4)
2331                                return 0;
2332                        if (RFIFOW(fd,2) != server[id].users) {
2333                                server[id].users = RFIFOW(fd,2);
2334                                ShowInfo("User Count: %d (Server: %d)\n", server[id].users, id);
2335                        }
2336                        RFIFOSKIP(fd, 4);
2337                        break;
2338
2339                case 0x2aff: //set MAP users
2340                        if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2))
2341                                return 0;
2342                {
2343                        //TODO: When data mismatches memory, update guild/party online/offline states.
2344                        int i, aid, cid;
2345                        struct online_char_data* character;
2346
2347                        online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown'
2348                        server[id].users = RFIFOW(fd,4);
2349                        for(i = 0; i < server[id].users; i++) {
2350                                aid = RFIFOL(fd,6+i*8);
2351                                cid = RFIFOL(fd,6+i*8+4);
2352                                character = (struct online_char_data*)idb_ensure(online_char_db, aid, create_online_char_data);
2353                                if (character->server > -1 && character->server != id)
2354                                {
2355                                        ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n",
2356                                                character->account_id, character->char_id, character->server, id, aid, cid);
2357                                        mapif_disconnectplayer(server[character->server].fd, character->account_id, character->char_id, 2);
2358                                }
2359                                character->server = id;
2360                                character->char_id = cid;
2361                        }
2362                        //If any chars remain in -2, they will be cleaned in the cleanup timer.
2363                        RFIFOSKIP(fd,RFIFOW(fd,2));
2364                }
2365                break;
2366
2367                case 0x2b01: // Receive character data from map-server for saving
2368                        if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
2369                                return 0;
2370                {
2371                        int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2);
2372                        struct online_char_data* character;
2373                        if (size - 13 != sizeof(struct mmo_charstatus))
2374                        {
2375                                ShowError("parse_from_map (save-char): Size mismatch! %d != %d\n", size-13, sizeof(struct mmo_charstatus));
2376                                RFIFOSKIP(fd,size);
2377                                break;
2378                        }
2379                        //Check account only if this ain't final save. Final-save goes through because of the char-map reconnect
2380                        if (RFIFOB(fd,12) || (
2381                                (character = (struct online_char_data*)idb_get(online_char_db, aid)) != NULL &&
2382                                character->char_id == cid))
2383                        {
2384                                struct mmo_charstatus char_dat;
2385                                memcpy(&char_dat, RFIFOP(fd,13), sizeof(struct mmo_charstatus));
2386                                mmo_char_tosql(cid, &char_dat);
2387                        } else {        //This may be valid on char-server reconnection, when re-sending characters that already logged off.
2388                                ShowError("parse_from_map (save-char): Received data for non-existant/offline character (%d:%d).\n", aid, cid);
2389                                set_char_online(id, cid, aid);
2390                        }
2391
2392                        if (RFIFOB(fd,12))
2393                        { //Flag? Set character offline after saving [Skotlex]
2394                                set_char_offline(cid, aid);
2395                                WFIFOHEAD(fd,10);
2396                                WFIFOW(fd,0) = 0x2b21; //Save ack only needed on final save.
2397                                WFIFOL(fd,2) = aid;
2398                                WFIFOL(fd,6) = cid;
2399                                WFIFOSET(fd,10);
2400                        }
2401                        RFIFOSKIP(fd,size);
2402                }
2403                break;
2404
2405                case 0x2b02: // req char selection
2406                        if( RFIFOREST(fd) < 18 )
2407                                return 0;
2408                {
2409                        struct auth_node* node;
2410
2411                        int account_id = RFIFOL(fd,2);
2412                        uint32 login_id1 = RFIFOL(fd,6);
2413                        uint32 login_id2 = RFIFOL(fd,10);
2414                        uint32 ip = RFIFOL(fd,14);
2415                        RFIFOSKIP(fd,18);
2416
2417                        // create temporary auth entry
2418                        CREATE(node, struct auth_node, 1);
2419                        node->account_id = account_id;
2420                        node->char_id = 0;
2421                        node->login_id1 = login_id1;
2422                        node->login_id2 = login_id2;
2423                        //node->sex = 0;
2424                        node->ip = ntohl(ip);
2425                        node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server)
2426                        idb_put(auth_db, account_id, node);
2427
2428                        //Set char to "@ char select" in online db [Kevin]
2429                        set_char_charselect(account_id);
2430
2431                        WFIFOHEAD(fd,7);
2432                        WFIFOW(fd,0) = 0x2b03;
2433                        WFIFOL(fd,2) = account_id;
2434                        WFIFOB(fd,6) = 0;
2435                        WFIFOSET(fd,7);
2436                }
2437                break;
2438
2439                case 0x2b05: // request "change map server"
2440                        if (RFIFOREST(fd) < 35)
2441                                return 0;
2442                {
2443                        int map_id, map_fd = -1;
2444                        struct online_char_data* data;
2445                        struct mmo_charstatus* char_data;
2446                        struct mmo_charstatus char_dat;
2447
2448                        map_id = search_mapserver(RFIFOW(fd,18), ntohl(RFIFOL(fd,24)), ntohs(RFIFOW(fd,28))); //Locate mapserver by ip and port.
2449                        if (map_id >= 0)
2450                                map_fd = server[map_id].fd;
2451                        //Char should just had been saved before this packet, so this should be safe. [Skotlex]
2452                        char_data = (struct mmo_charstatus*)uidb_get(char_db_,RFIFOL(fd,14));
2453                        if (char_data == NULL) 
2454                        {       //Really shouldn't happen.
2455                                mmo_char_fromsql(RFIFOL(fd,14), &char_dat, true);
2456                                char_data = (struct mmo_charstatus*)uidb_get(char_db_,RFIFOL(fd,14));
2457                        }
2458                        //Tell the new map server about this player using Kevin's new auth packet. [Skotlex]
2459                        if (map_fd >= 0 && session[map_fd] && char_data) 
2460                        {       //Send the map server the auth of this player.
2461                                struct auth_node* node;
2462
2463                                //Update the "last map" as this is where the player must be spawned on the new map server.
2464                                char_data->last_point.map = RFIFOW(fd,18);
2465                                char_data->last_point.x = RFIFOW(fd,20);
2466                                char_data->last_point.y = RFIFOW(fd,22);
2467                                char_data->sex = RFIFOB(fd,30);
2468
2469#if 0
2470                                // the map-server must request it [FlavioJS]
2471                                WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus));
2472                                WFIFOW(map_fd,0) = 0x2afd;
2473                                WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus);
2474                                WFIFOL(map_fd,4) = RFIFOL(fd,2); //Account ID
2475                                WFIFOL(map_fd,8) = RFIFOL(fd,6); //Login1
2476                                WFIFOL(map_fd,16) = RFIFOL(fd,10); //Login2
2477                                WFIFOL(map_fd,12) = (unsigned long)0; //TODO: expiration_time, how do I figure it out right now?
2478                                memcpy(WFIFOP(map_fd,20), char_data, sizeof(struct mmo_charstatus));
2479                                WFIFOSET(map_fd, WFIFOW(map_fd,2));
2480#endif
2481
2482                                // create temporary auth entry
2483                                CREATE(node, struct auth_node, 1);
2484                                node->account_id = RFIFOL(fd,2);
2485                                node->char_id = RFIFOL(fd,14);
2486                                node->login_id1 = RFIFOL(fd,6);
2487                                node->login_id2 = RFIFOL(fd,10);
2488                                node->sex = RFIFOB(fd,30);
2489                                node->expiration_time = 0; // FIXME
2490                                node->ip = ntohl(RFIFOL(fd,31));
2491                                idb_put(auth_db, RFIFOL(fd,2), node);
2492
2493                                data = (struct online_char_data*)idb_ensure(online_char_db, RFIFOL(fd,2), create_online_char_data);
2494                                data->char_id = char_data->char_id;
2495                                data->server = map_id; //Update server where char is.
2496                               
2497                                //Reply with an ack.
2498                                WFIFOHEAD(fd,30);
2499                                WFIFOW(fd,0) = 0x2b06;
2500                                memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28);
2501                                WFIFOSET(fd,30);
2502                        } else { //Reply with nak
2503                                WFIFOHEAD(fd,30);
2504                                WFIFOW(fd,0) = 0x2b06;
2505                                memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28);
2506                                WFIFOL(fd,6) = 0; //Set login1 to 0.
2507                                WFIFOSET(fd,30);
2508                        }
2509                        RFIFOSKIP(fd,35);
2510                }
2511                break;
2512
2513                case 0x2b08: // char name request
2514                        if (RFIFOREST(fd) < 6)
2515                                return 0;
2516
2517                        WFIFOHEAD(fd,30);
2518                        WFIFOW(fd,0) = 0x2b09;
2519                        WFIFOL(fd,2) = RFIFOL(fd,2);
2520                        char_loadName((int)RFIFOL(fd,2), (char*)WFIFOP(fd,6));
2521                        WFIFOSET(fd,30);
2522
2523                        RFIFOSKIP(fd,6);
2524                break;
2525
2526                case 0x2b0c: // Map server send information to change an email of an account -> login-server
2527                        if (RFIFOREST(fd) < 86)
2528                                return 0;
2529                        if (login_fd > 0) { // don't send request if no login-server
2530                                WFIFOHEAD(login_fd,86);
2531                                memcpy(WFIFOP(login_fd,0), RFIFOP(fd,0),86); // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B
2532                                WFIFOW(login_fd,0) = 0x2722;
2533                                WFIFOSET(login_fd,86);
2534                        }
2535                        RFIFOSKIP(fd, 86);
2536                break;
2537
2538                case 0x2b0e: // Request from map-server to change an account's status (will just be forwarded to login server)
2539                        if (RFIFOREST(fd) < 44)
2540                                return 0;
2541                {
2542                        int result = 0; // 0-login-server request done, 1-player not found, 2-gm level too low, 3-login-server offline
2543                        char esc_name[NAME_LENGTH*2+1];
2544
2545                        int acc = RFIFOL(fd,2); // account_id of who ask (-1 if server itself made this request)
2546                        const char* name = (char*)RFIFOP(fd,6); // name of the target character
2547                        int type = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban
2548                        short year = RFIFOW(fd,32);
2549                        short month = RFIFOW(fd,34);
2550                        short day = RFIFOW(fd,36);
2551                        short hour = RFIFOW(fd,38);
2552                        short minute = RFIFOW(fd,40);
2553                        short second = RFIFOW(fd,42);
2554                        RFIFOSKIP(fd,44);
2555
2556                        Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
2557                        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`name` FROM `%s` WHERE `name` = '%s'", char_db, esc_name) )
2558                                Sql_ShowDebug(sql_handle);
2559                        else
2560                        if( Sql_NumRows(sql_handle) == 0 )
2561                        {
2562                                result = 1; // 1-player not found
2563                        }
2564                        else
2565                        if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
2566                                Sql_ShowDebug(sql_handle);
2567                                //FIXME: set proper result value?
2568                        else
2569                        {
2570                                char name[NAME_LENGTH];
2571                                int account_id;
2572                                char* data;
2573
2574                                Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data);
2575                                Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(name, data, sizeof(name));
2576
2577                                if( login_fd <= 0 )
2578                                        result = 3; // 3-login-server offline
2579                                else
2580                                if( acc != -1 && isGM(acc) < isGM(account_id) )
2581                                        result = 2; // 2-gm level too low
2582                                else
2583                                switch( type ) {
2584                                case 1: // block
2585                                                WFIFOHEAD(login_fd,10);
2586                                                WFIFOW(login_fd,0) = 0x2724;
2587                                                WFIFOL(login_fd,2) = account_id;
2588                                                WFIFOL(login_fd,6) = 5; // new account status
2589                                                WFIFOSET(login_fd,10);
2590                                break;
2591                                case 2: // ban
2592                                                WFIFOHEAD(login_fd,18);
2593                                                WFIFOW(login_fd, 0) = 0x2725;
2594                                                WFIFOL(login_fd, 2) = account_id;
2595                                                WFIFOW(login_fd, 6) = year;
2596                                                WFIFOW(login_fd, 8) = month;
2597                                                WFIFOW(login_fd,10) = day;
2598                                                WFIFOW(login_fd,12) = hour;
2599                                                WFIFOW(login_fd,14) = minute;
2600                                                WFIFOW(login_fd,16) = second;
2601                                                WFIFOSET(login_fd,18);
2602                                break;
2603                                case 3: // unblock
2604                                                WFIFOHEAD(login_fd,10);
2605                                                WFIFOW(login_fd,0) = 0x2724;
2606                                                WFIFOL(login_fd,2) = account_id;
2607                                                WFIFOL(login_fd,6) = 0; // new account status
2608                                                WFIFOSET(login_fd,10);
2609                                break;
2610                                case 4: // unban
2611                                                WFIFOHEAD(login_fd,6);
2612                                                WFIFOW(login_fd,0) = 0x272a;
2613                                                WFIFOL(login_fd,2) = account_id;
2614                                                WFIFOSET(login_fd,6);
2615                                break;
2616                                case 5: // changesex
2617                                                WFIFOHEAD(login_fd,6);
2618                                                WFIFOW(login_fd,0) = 0x2727;
2619                                                WFIFOL(login_fd,2) = account_id;
2620                                                WFIFOSET(login_fd,6);
2621                                break;
2622                                }
2623                        }
2624
2625                        Sql_FreeResult(sql_handle);
2626
2627                        // send answer if a player ask, not if the server ask
2628                        if( acc != -1 && type != 5) { // Don't send answer for changesex
2629                                WFIFOHEAD(fd,34);
2630                                WFIFOW(fd, 0) = 0x2b0f;
2631                                WFIFOL(fd, 2) = acc;
2632                                safestrncpy((char*)WFIFOP(fd,6), name, NAME_LENGTH);
2633                                WFIFOW(fd,30) = type;
2634                                WFIFOW(fd,32) = result;
2635                                WFIFOSET(fd,34);
2636                        }
2637                }
2638                break;
2639
2640                case 0x2b10: // Update and send fame ranking list
2641                        if (RFIFOREST(fd) < 11)
2642                                return 0;
2643                {
2644                        int cid = RFIFOL(fd, 2);
2645                        int fame = RFIFOL(fd, 6);
2646                        char type = RFIFOB(fd, 10);
2647                        int size;
2648                        struct fame_list* list;
2649                        int player_pos;
2650                        int fame_pos;
2651
2652                        switch(type)
2653                        {
2654                                case 1:  size = fame_list_size_smith;   list = smith_fame_list;   break;
2655                                case 2:  size = fame_list_size_chemist; list = chemist_fame_list; break;
2656                                case 3:  size = fame_list_size_taekwon; list = taekwon_fame_list; break;
2657                                default: size = 0;                      list = NULL;              break;
2658                        }
2659
2660                        ARR_FIND(0, size, player_pos, list[player_pos].id == cid);// position of the player
2661                        ARR_FIND(0, size, fame_pos, list[fame_pos].fame <= fame);// where the player should be
2662
2663                        if( player_pos == size && fame_pos == size )
2664                                ;// not on list and not enough fame to get on it
2665                        else if( fame_pos == player_pos )
2666                        {// same position
2667                                list[player_pos].fame = fame;
2668                                char_update_fame_list(type, player_pos, fame);
2669                        }
2670                        else
2671                        {// move in the list
2672                                if( player_pos == size )
2673                                {// new ranker - not in the list
2674                                        ARR_MOVE(size - 1, fame_pos, list, struct fame_list);
2675                                        list[fame_pos].id = cid;
2676                                        list[fame_pos].fame = fame;
2677                                        char_loadName(cid, list[fame_pos].name);
2678                                }
2679                                else
2680                                {// already in the list
2681                                        if( fame_pos == size )
2682                                                --fame_pos;// move to the end of the list
2683                                        ARR_MOVE(player_pos, fame_pos, list, struct fame_list);
2684                                        list[fame_pos].fame = fame;
2685                                }
2686                                char_send_fame_list(-1);
2687                        }
2688
2689                        RFIFOSKIP(fd,11);
2690                }
2691                break;
2692
2693                // Divorce chars
2694                case 0x2b11:
2695                        if( RFIFOREST(fd) < 10 )
2696                                return 0;
2697
2698                        divorce_char_sql(RFIFOL(fd,2), RFIFOL(fd,6));
2699                        RFIFOSKIP(fd,10);
2700                break;
2701
2702                case 0x2b16: // Receive rates [Wizputer]
2703                        if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8))
2704                                return 0;
2705                {
2706                        char motd[256];
2707                        char esc_motd[sizeof(motd)*2+1];
2708                        char esc_server_name[sizeof(server_name)*2+1];
2709
2710                        strncpy(motd, (char*)RFIFOP(fd,10), 255); //First copy it to make sure the motd fits.
2711                        motd[255] = '\0';
2712                        Sql_EscapeString(sql_handle, esc_motd, motd);
2713                        Sql_EscapeString(sql_handle, esc_server_name, server_name);
2714
2715                        if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `ragsrvinfo` SET `index`='%d',`name`='%s',`exp`='%d',`jexp`='%d',`drop`='%d',`motd`='%s'",
2716                                fd, esc_server_name, RFIFOW(fd,2), RFIFOW(fd,4), RFIFOW(fd,6), esc_motd) )
2717                                Sql_ShowDebug(sql_handle);
2718                        RFIFOSKIP(fd,RFIFOW(fd,8));
2719                }
2720                break;
2721
2722                case 0x2b17: // Character disconnected set online 0 [Wizputer]
2723                        if (RFIFOREST(fd) < 6)
2724                                return 0;
2725                        set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6));
2726                        RFIFOSKIP(fd,10);
2727                break;
2728
2729                case 0x2b18: // Reset all chars to offline [Wizputer]
2730                        set_all_offline(id);
2731                        RFIFOSKIP(fd,2);
2732                break;
2733               
2734                case 0x2b19: // Character set online [Wizputer]
2735                        if (RFIFOREST(fd) < 10)
2736                                return 0;
2737                        set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6));
2738                        RFIFOSKIP(fd,10);
2739                break;
2740
2741                case 0x2b1a: // Build and send fame ranking lists [DracoRPG]
2742                        if (RFIFOREST(fd) < 2)
2743                                return 0;
2744                        char_read_fame_list();
2745                        char_send_fame_list(-1);
2746                        RFIFOSKIP(fd,2);
2747                break;
2748
2749                case 0x2b1c: //Request to save status change data. [Skotlex]
2750                        if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
2751                                return 0;
2752                {
2753#ifdef ENABLE_SC_SAVING
2754                        int count, aid, cid;
2755                       
2756                        aid = RFIFOL(fd, 4);
2757                        cid = RFIFOL(fd, 8);
2758                        count = RFIFOW(fd, 12);
2759
2760                        if( count > 0 )
2761                        {
2762                                struct status_change_data data;
2763                                StringBuf buf;
2764                                int i;
2765
2766                                StringBuf_Init(&buf);
2767                                StringBuf_Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ", scdata_db);
2768                                for( i = 0; i < count; ++i )
2769                                {
2770                                        memcpy (&data, RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data));
2771                                        if( i > 0 )
2772                                                StringBuf_AppendStr(&buf, ", ");
2773                                        StringBuf_Printf(&buf, "('%d','%d','%hu','%d','%d','%d','%d','%d')", aid, cid,
2774                                                data.type, data.tick, data.val1, data.val2, data.val3, data.val4);
2775                                }
2776                                if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
2777                                        Sql_ShowDebug(sql_handle);
2778                                StringBuf_Destroy(&buf);
2779                        }
2780#endif
2781                        RFIFOSKIP(fd, RFIFOW(fd, 2));
2782                }
2783                break;
2784
2785                case 0x2b23: // map-server alive packet
2786                        WFIFOHEAD(fd,2);
2787                        WFIFOW(fd,0) = 0x2b24;
2788                        WFIFOSET(fd,2);
2789                        RFIFOSKIP(fd,2);
2790                break;
2791
2792                case 0x2b26: // auth request from map-server
2793                        if (RFIFOREST(fd) < 19)
2794                                return 0;
2795
2796                {
2797                        int account_id;
2798                        int char_id;
2799                        int login_id1;
2800                        char sex;
2801                        uint32 ip;
2802                        struct auth_node* node;
2803                        struct mmo_charstatus* cd;
2804                        struct mmo_charstatus char_dat;
2805
2806                        account_id = RFIFOL(fd,2);
2807                        char_id    = RFIFOL(fd,6);
2808                        login_id1  = RFIFOL(fd,10);
2809                        sex        = RFIFOB(fd,14);
2810                        ip         = ntohl(RFIFOL(fd,15));
2811                        RFIFOSKIP(fd,19);
2812
2813                        node = (struct auth_node*)idb_get(auth_db, account_id);
2814                        cd = (struct mmo_charstatus*)uidb_get(char_db_,char_id);
2815                        if( cd == NULL )
2816                        {       //Really shouldn't happen.
2817                                mmo_char_fromsql(char_id, &char_dat, true);
2818                                cd = (struct mmo_charstatus*)uidb_get(char_db_,char_id);
2819                        }
2820                        if( node != NULL && cd != NULL &&
2821                                node->account_id == account_id &&
2822                                node->char_id == char_id &&
2823                                node->login_id1 == login_id1 &&
2824                                node->sex == sex &&
2825                                node->ip == ip )
2826                        {// auth ok
2827                                cd->sex = sex;
2828
2829                                WFIFOHEAD(fd,20 + sizeof(struct mmo_charstatus));
2830                                WFIFOW(fd,0) = 0x2afd;
2831                                WFIFOW(fd,2) = 20 + sizeof(struct mmo_charstatus);
2832                                WFIFOL(fd,4) = account_id;
2833                                WFIFOL(fd,8) = login_id1;
2834                                WFIFOL(fd,12) = (uint32)node->expiration_time; // FIXME: will wrap to negative after "19-Jan-2038, 03:14:07 AM GMT"
2835                                WFIFOL(fd,16) = node->login_id2;
2836                                memcpy(WFIFOP(fd,20), cd, sizeof(struct mmo_charstatus));
2837                                WFIFOSET(fd, WFIFOW(fd,2));
2838
2839                                // only use the auth once and mark user online
2840                                idb_remove(auth_db, account_id);
2841                                set_char_online(id, char_id, account_id);
2842                        }
2843                        else
2844                        {// auth failed
2845                                WFIFOHEAD(fd,19);
2846                                WFIFOW(fd,0) = 0x2b27;
2847                                WFIFOL(fd,2) = account_id;
2848                                WFIFOL(fd,6) = char_id;
2849                                WFIFOL(fd,10) = login_id1;
2850                                WFIFOB(fd,14) = sex;
2851                                WFIFOL(fd,15) = htonl(ip);
2852                                WFIFOSET(fd,19);
2853                        }
2854                }
2855                break;
2856
2857                case 0x2736: // ip address update
2858                        if (RFIFOREST(fd) < 6) return 0;
2859                        server[id].ip = ntohl(RFIFOL(fd, 2));
2860                        ShowInfo("Updated IP address of map-server #%d to %d.%d.%d.%d.\n", id, CONVIP(server[id].ip));
2861                        RFIFOSKIP(fd,6);
2862                break;
2863
2864                default:
2865                {
2866                        // inter server - packet
2867                        int r = inter_parse_frommap(fd);
2868                        if (r == 1) break;              // processed
2869                        if (r == 2) return 0;   // need more packet
2870
2871                        // no inter server packet. no char server packet -> disconnect
2872                        ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0));
2873                        set_eof(fd);
2874                        return 0;
2875                }
2876                } // switch
2877        } // while
2878       
2879        return 0;
2880}
2881
2882// Searches for the mapserver that has a given map (and optionally ip/port, if not -1).
2883// If found, returns the server's index in the 'server' array (otherwise returns -1).
2884int search_mapserver(unsigned short map, uint32 ip, uint16 port)
2885{
2886        int i, j;
2887       
2888        for(i = 0; i < MAX_MAP_SERVERS; i++)
2889        {
2890                if (server[i].fd > 0
2891                && (ip == (uint32)-1 || server[i].ip == ip)
2892                && (port == (uint16)-1 || server[i].port == port))
2893                {
2894                        for (j = 0; server[i].map[j]; j++)
2895                                if (server[i].map[j] == map)
2896                                        return i;
2897                }
2898        }
2899
2900        return -1;
2901}
2902
2903int char_mapif_init(int fd)
2904{
2905        return inter_mapif_init(fd);
2906}
2907
2908//--------------------------------------------
2909// Test to know if an IP come from LAN or WAN.
2910//--------------------------------------------
2911int lan_subnetcheck(uint32 ip)
2912{
2913        int i;
2914        ARR_FIND( 0, subnet_count, i, (subnet[i].char_ip & subnet[i].mask) == (ip & subnet[i].mask) );
2915        if( i < subnet_count ) {
2916                ShowInfo("Subnet check [%u.%u.%u.%u]: Matches "CL_CYAN"%u.%u.%u.%u/%u.%u.%u.%u"CL_RESET"\n", CONVIP(ip), CONVIP(subnet[i].char_ip & subnet[i].mask), CONVIP(subnet[i].mask));
2917                return subnet[i].char_ip;
2918        } else {
2919                ShowInfo("Subnet check [%u.%u.%u.%u]: "CL_CYAN"WAN"CL_RESET"\n", CONVIP(ip));
2920                return 0;
2921        }
2922}
2923
2924int parse_char(int fd)
2925{
2926        int i, ch = 0;
2927        char email[40]; 
2928        unsigned short cmd;
2929        int map_fd;
2930        struct char_session_data* sd;
2931        uint32 ipl = session[fd]->client_addr;
2932
2933        sd = (struct char_session_data*)session[fd]->session_data;
2934
2935        // disconnect any player if no login-server.
2936        if(login_fd < 0)
2937                set_eof(fd);
2938
2939        if(session[fd]->flag.eof)
2940        {
2941                if( sd != NULL && sd->auth )
2942                {       // already authed client
2943                        struct online_char_data* data = (struct online_char_data*)idb_get(online_char_db, sd->account_id);
2944                        if( data == NULL || data->server == -1) //If it is not in any server, send it offline. [Skotlex]
2945                                set_char_offline(-1,sd->account_id);
2946                        if( data != NULL && data->fd == fd)
2947                                data->fd = -1;
2948                }
2949                do_close(fd);
2950                return 0;
2951        }
2952
2953        while( RFIFOREST(fd) >= 2 )
2954        {
2955                //For use in packets that depend on an sd being present [Skotlex]
2956                #define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL || !sd->auth) { RFIFOSKIP(fd,rest); return 0; } }
2957
2958                cmd = RFIFOW(fd,0);
2959                switch( cmd )
2960                {
2961
2962                // request to connect
2963                // 0065 <account id>.L <login id1>.L <login id2>.L <???>.W <sex>.B
2964                case 0x65:
2965                        if( RFIFOREST(fd) < 17 )
2966                                return 0;
2967                {
2968                        struct auth_node* node;
2969
2970                        int account_id = RFIFOL(fd,2);
2971                        uint32 login_id1 = RFIFOL(fd,6);
2972                        uint32 login_id2 = RFIFOL(fd,10);
2973                        int sex = RFIFOB(fd,16);
2974                        RFIFOSKIP(fd,17);
2975
2976                        ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", account_id, login_id1, login_id2);
2977
2978                        if (sd) {
2979                                //Received again auth packet for already authentified account?? Discard it.
2980                                //TODO: Perhaps log this as a hack attempt?
2981                                //TODO: and perhaps send back a reply?
2982                                break;
2983                        }
2984                       
2985                        CREATE(session[fd]->session_data, struct char_session_data, 1);
2986                        sd = (struct char_session_data*)session[fd]->session_data;
2987                        sd->expiration_time = 0; // unknown or unlimited (not displaying on map-server)
2988                        sd->account_id = account_id;
2989                        sd->login_id1 = login_id1;
2990                        sd->login_id2 = login_id2;
2991                        sd->sex = sex;
2992                        sd->auth = false; // not authed yet
2993
2994                        // send back account_id
2995                        WFIFOHEAD(fd,4);
2996                        WFIFOL(fd,0) = account_id;
2997                        WFIFOSET(fd,4);
2998
2999                        // search authentification
3000                        node = (struct auth_node*)idb_get(auth_db, account_id);
3001                        if( node != NULL &&
3002                            node->account_id == account_id &&
3003                                node->login_id1  == login_id1 &&
3004                                node->login_id2  == login_id2 &&
3005                                node->ip         == ipl )
3006                        {// authentication found (coming from map server)
3007                                idb_remove(auth_db, account_id);
3008                                char_auth_ok(fd, sd);
3009                        }
3010                        else
3011                        {// authentication not found (coming from login server)
3012                                if (login_fd > 0) { // don't send request if no login-server
3013                                        WFIFOHEAD(login_fd,19);
3014                                        WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account
3015                                        WFIFOL(login_fd,2) = sd->account_id;
3016                                        WFIFOL(login_fd,6) = sd->login_id1;
3017                                        WFIFOL(login_fd,10) = sd->login_id2;
3018                                        WFIFOB(login_fd,14) = sd->sex;
3019                                        WFIFOL(login_fd,15) = htonl(ipl);
3020                                        WFIFOSET(login_fd,19);
3021                                } else { // if no login-server, we must refuse connection
3022                                        WFIFOHEAD(fd,3);
3023                                        WFIFOW(fd,0) = 0x6c;
3024                                        WFIFOB(fd,2) = 0;
3025                                        WFIFOSET(fd,3);
3026                                }
3027                        }
3028                }
3029                break;
3030
3031                // char select
3032                case 0x66:
3033                        FIFOSD_CHECK(3);
3034                {
3035                        struct mmo_charstatus char_dat;
3036                        struct mmo_charstatus * cp;
3037                        char* data;
3038                        int char_id;
3039                        uint32 subnet_map_ip;
3040                        struct auth_node* node;
3041
3042                        int slot = RFIFOB(fd,2);
3043                        RFIFOSKIP(fd,3);
3044
3045                        if ( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'", char_db, sd->account_id, slot)
3046                          || SQL_SUCCESS != Sql_NextRow(sql_handle)
3047                          || SQL_SUCCESS != Sql_GetData(sql_handle, 0, &data, NULL) )
3048                        {
3049                                //Not found?? May be forged packet.
3050                                Sql_ShowDebug(sql_handle);
3051                                Sql_FreeResult(sql_handle);
3052                                //TODO: perhaps add some reply? (otherwise it hangs the client)
3053                                break;
3054                        }
3055
3056                        char_id = atoi(data);
3057                        Sql_FreeResult(sql_handle);
3058                        mmo_char_fromsql(char_id, &char_dat, true);
3059
3060                        //Have to switch over to the DB instance otherwise data won't propagate [Kevin]
3061                        cp = (struct mmo_charstatus *)idb_get(char_db_, char_id);
3062                        cp->sex = sd->sex;
3063
3064                        if (log_char) {
3065                                char esc_name[NAME_LENGTH*2+1];
3066
3067                                Sql_EscapeStringLen(sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH));
3068                                if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')",
3069                                        charlog_db, sd->account_id, slot, esc_name) )
3070                                        Sql_ShowDebug(sql_handle);
3071                        }
3072                        ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name);
3073
3074                        // searching map server
3075                        i = search_mapserver(char_dat.last_point.map, -1, -1);
3076
3077                        // if map is not found, we check major cities
3078                        if (i < 0) {
3079                                unsigned short j;
3080                                //First check that there's actually a map server online.
3081                                ARR_FIND( 0, MAX_MAP_SERVERS, j, server[j].fd >= 0 && server[j].map[0] );
3082                                if (j == MAX_MAP_SERVERS) {
3083                                        ShowInfo("Connection Closed. No map servers available.\n");
3084                                        WFIFOHEAD(fd,3);
3085                                        WFIFOW(fd,0) = 0x81;
3086                                        WFIFOB(fd,2) = 1; // 01 = Server closed
3087                                        WFIFOSET(fd,3);
3088                                        break;
3089                                }
3090                                if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) {
3091                                        cp->last_point.x = 273;
3092                                        cp->last_point.y = 354;
3093                                } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) {
3094                                        cp->last_point.x = 120;
3095                                        cp->last_point.y = 100;
3096                                } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) {
3097                                        cp->last_point.x = 160;
3098                                        cp->last_point.y = 94;
3099                                } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) {
3100                                        cp->last_point.x = 116;
3101                                        cp->last_point.y = 57;
3102                                } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) {
3103                                        cp->last_point.x = 87;
3104                                        cp->last_point.y = 117;
3105                                } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) {
3106                                        cp->last_point.x = 94;
3107                                        cp->last_point.y = 103;
3108                                } else {
3109                                        ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(char_dat.last_point.map));
3110                                        WFIFOHEAD(fd,3);
3111                                        WFIFOW(fd,0) = 0x81;
3112                                        WFIFOB(fd,2) = 1; // 01 = Server closed
3113                                        WFIFOSET(fd,3);
3114                                        break;
3115                                }
3116                                ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(char_dat.last_point.map), mapindex_id2name(j));
3117                                cp->last_point.map = j;
3118                        }
3119
3120                        //Send NEW auth packet [Kevin]
3121                        //FIXME: is this case even possible? [ultramage]
3122                        if ((map_fd = server[i].fd) < 1 || session[map_fd] == NULL)
3123                        {
3124                                ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i);
3125                                server[i].fd = -1;
3126                                memset(&server[i], 0, sizeof(struct mmo_map_server));
3127                                //Send server closed.
3128                                WFIFOHEAD(fd,3);
3129                                WFIFOW(fd,0) = 0x81;
3130                                WFIFOB(fd,2) = 1; // 01 = Server closed
3131                                WFIFOSET(fd,3);
3132                                break;
3133                        }
3134
3135                        //Send player to map
3136                        WFIFOHEAD(fd,28);
3137                        WFIFOW(fd,0) = 0x71;
3138                        WFIFOL(fd,2) = cp->char_id;
3139                        mapindex_getmapname_ext(mapindex_id2name(cp->last_point.map), (char*)WFIFOP(fd,6));
3140
3141                        // Advanced subnet check [LuzZza]
3142                        subnet_map_ip = lan_subnetcheck(ipl);
3143                        WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : server[i].ip);
3144                        WFIFOW(fd,26) = ntows(htons(server[i].port)); // [!] LE byte order here [!]
3145                        WFIFOSET(fd,28);
3146
3147                        // create temporary auth entry
3148                        CREATE(node, struct auth_node, 1);
3149                        node->account_id = sd->account_id;
3150                        node->char_id = cp->char_id;
3151                        node->login_id1 = sd->login_id1;
3152                        node->login_id2 = sd->login_id2;
3153                        node->sex = sd->sex;
3154                        node->expiration_time = sd->expiration_time;
3155                        node->ip = ipl;
3156                        idb_put(auth_db, sd->account_id, node);
3157
3158                        set_char_online(-2,node->char_id,sd->account_id);
3159
3160                }
3161                break;
3162
3163                // create new char
3164                // S 0067 <name>.24B <str>.B <agi>.B <vit>.B <int>.B <dex>.B <luk>.B <slot>.B <hair color>.W <hair style>.W
3165                case 0x67:
3166                        FIFOSD_CHECK(37);
3167
3168                        if( !char_new ) //turn character creation on/off [Kevin]
3169                                i = -2;
3170                        else
3171                                i = 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));
3172
3173                        //'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3)
3174                        if (i < 0)
3175                        {
3176                                WFIFOHEAD(fd,3);
3177                                WFIFOW(fd,0) = 0x6e;
3178                                switch (i) {
3179                                case -1: WFIFOB(fd,2) = 0x00; break;
3180                                case -2: WFIFOB(fd,2) = 0x02; break;
3181                                case -3: WFIFOB(fd,2) = 0x01; break;
3182                                }
3183                                WFIFOSET(fd,3);
3184                                RFIFOSKIP(fd,37);
3185                                break;
3186                        }
3187                        else
3188                        {
3189                                int len;
3190                                // retrieve data
3191                                struct mmo_charstatus char_dat;
3192                                mmo_char_fromsql(i, &char_dat, false); //Only the short data is needed.
3193
3194                                // send to player
3195                                WFIFOHEAD(fd,110);
3196                                WFIFOW(fd,0) = 0x6d;
3197                                len = 2 + mmo_char_tobuf(WFIFOP(fd,2), &char_dat);
3198                                WFIFOSET(fd,len);
3199
3200                                // add new entry to the chars list
3201                                ARR_FIND( 0, MAX_CHARS, ch, sd->found_char[ch] == -1 );
3202                                if( ch < MAX_CHARS )
3203                                                sd->found_char[ch] = i; // the char_id of the new char
3204                        }
3205
3206                        RFIFOSKIP(fd,37);
3207                break;
3208
3209                // delete char
3210                case 0x68:
3211                // 2004-04-19aSakexe+ langtype 12 char deletion packet
3212                case 0x1fb:
3213                        if (cmd == 0x68) FIFOSD_CHECK(46);
3214                        if (cmd == 0x1fb) FIFOSD_CHECK(56);
3215                {
3216                        int cid = RFIFOL(fd,2);
3217
3218                        WFIFOHEAD(fd,46);
3219                        ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid);
3220                        memcpy(email, RFIFOP(fd,6), 40);
3221                        RFIFOSKIP(fd,RFIFOREST(fd)); // hack to make the other deletion packet work
3222                       
3223                        // Check if e-mail is correct
3224                        if(strcmpi(email, sd->email) && //email does not matches and
3225                        (
3226                                strcmp("a@a.com", sd->email) || //it is not default email, or
3227                                (strcmp("a@a.com", email) && strcmp("", email)) //email sent does not matches default
3228                        )) {    //Fail
3229                                WFIFOHEAD(fd,3);
3230                                WFIFOW(fd,0) = 0x70;
3231                                WFIFOB(fd,2) = 0; // 00 = Incorrect Email address
3232                                WFIFOSET(fd,3);
3233                                break;
3234                        }
3235                       
3236                        // check if this char exists
3237                        ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
3238                        if( i == MAX_CHARS )
3239                        { // Such a character does not exist in the account
3240                                WFIFOHEAD(fd,3);
3241                                WFIFOW(fd,0) = 0x70;
3242                                WFIFOB(fd,2) = 0;
3243                                WFIFOSET(fd,3);
3244                                break;
3245                        }
3246
3247                        // remove char from list and compact it
3248                        for(ch = i; ch < MAX_CHARS-1; ch++)
3249                                sd->found_char[ch] = sd->found_char[ch+1];
3250                        sd->found_char[MAX_CHARS-1] = -1;
3251                       
3252                        /* Delete character */
3253                        if(delete_char_sql(cid)<0){
3254                                //can't delete the char
3255                                //either SQL error or can't delete by some CONFIG conditions
3256                                //del fail
3257                                WFIFOW(fd, 0) = 0x70;
3258                                WFIFOB(fd, 2) = 0;
3259                                WFIFOSET(fd, 3);
3260                                break;
3261                        }
3262                        /* Char successfully deleted.*/
3263                        WFIFOHEAD(fd,2);
3264                        WFIFOW(fd,0) = 0x6f;
3265                        WFIFOSET(fd,2);
3266                }
3267                break;
3268
3269                // client keep-alive packet (every 12 seconds)
3270                // R 0187 <account ID>.l
3271                case 0x187:
3272                        if (RFIFOREST(fd) < 6)
3273                                return 0;
3274                        RFIFOSKIP(fd,6);
3275                break;
3276
3277                // char rename request
3278                // R 028d <account ID>.l <char ID>.l <new name>.24B
3279                case 0x28d:
3280                        FIFOSD_CHECK(34);
3281                        //not implemented
3282                        RFIFOSKIP(fd,34);
3283                break;
3284
3285                // log in as map-server
3286                case 0x2af8:
3287                        if (RFIFOREST(fd) < 60)
3288                                return 0;
3289                {
3290                        char* l_user = (char*)RFIFOP(fd,2);
3291                        char* l_pass = (char*)RFIFOP(fd,26);
3292                        l_user[23] = '\0';
3293                        l_pass[23] = '\0';
3294                        ARR_FIND( 0, MAX_MAP_SERVERS, i, server[i].fd <= 0 );
3295                        if (i == MAX_MAP_SERVERS || strcmp(l_user, userid) || strcmp(l_pass, passwd)) {
3296                                WFIFOHEAD(fd,3);
3297                                WFIFOW(fd,0) = 0x2af9;
3298                                WFIFOB(fd,2) = 3;
3299                                WFIFOSET(fd,3);
3300                        } else {
3301                                WFIFOHEAD(fd,3);
3302                                WFIFOW(fd,0) = 0x2af9;
3303                                WFIFOB(fd,2) = 0;
3304                                WFIFOSET(fd,3);
3305
3306                                server[i].fd = fd;
3307                                server[i].ip = ntohl(RFIFOL(fd,54));
3308                                server[i].port = ntohs(RFIFOW(fd,58));
3309                                server[i].users = 0;
3310                                memset(server[i].map, 0, sizeof(server[i].map));
3311                                session[fd]->func_parse = parse_frommap;
3312                                session[fd]->flag.server = 1;
3313                                realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
3314                                char_mapif_init(fd);
3315                                // send gm acccounts level to map-servers
3316                                WFIFOHEAD(fd,4+5*GM_num); 
3317                                WFIFOW(fd,0) = 0x2b15;
3318                                for(i = 0; i < GM_num; i++) {
3319                                        WFIFOL(fd,4+5*i) = gm_account[i].account_id;
3320                                        WFIFOB(fd,4+5*i+4) = (unsigned char)gm_account[i].level;
3321                                }
3322                                WFIFOW(fd,2) = 4+5*GM_num;
3323                                WFIFOSET(fd,WFIFOW(fd,2));
3324                        }
3325                       
3326                        RFIFOSKIP(fd,60);
3327                }
3328                return 0; // avoid processing of followup packets here
3329
3330                // Athena info get
3331                case 0x7530:
3332                        WFIFOHEAD(fd,10);
3333                        WFIFOW(fd,0) = 0x7531;
3334                        WFIFOB(fd,2) = ATHENA_MAJOR_VERSION;
3335                        WFIFOB(fd,3) = ATHENA_MINOR_VERSION;
3336                        WFIFOB(fd,4) = ATHENA_REVISION;
3337                        WFIFOB(fd,5) = ATHENA_RELEASE_FLAG;
3338                        WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG;
3339                        WFIFOB(fd,7) = ATHENA_SERVER_INTER | ATHENA_SERVER_CHAR;
3340                        WFIFOW(fd,8) = ATHENA_MOD_VERSION;
3341                        WFIFOSET(fd,10);
3342
3343                        RFIFOSKIP(fd,2);
3344                break;
3345
3346                // unknown packet received
3347                default:
3348                        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));
3349                        set_eof(fd);
3350                        return 0;
3351                }
3352        }
3353
3354        RFIFOFLUSH(fd);
3355        return 0;
3356}
3357
3358// Console Command Parser [Wizputer]
3359int parse_console(char* buf)
3360{
3361        char command[256];
3362
3363        memset(command, 0, sizeof(command));
3364
3365        sscanf(buf, "%[^\n]", command);
3366
3367        //login_log("Console command :%s\n", command);
3368
3369        if( strcmpi("shutdown", command) == 0 ||
3370            strcmpi("exit", command) == 0 ||
3371            strcmpi("quit", command) == 0 ||
3372            strcmpi("end", command) == 0 )
3373                runflag = 0;
3374        else if( strcmpi("alive", command) == 0 ||
3375                 strcmpi("status", command) == 0 )
3376                ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
3377        else if( strcmpi("help", command) == 0 ){
3378                ShowInfo(CL_BOLD"Help of commands:"CL_RESET"\n");
3379                ShowInfo("  To shutdown the server:\n");
3380                ShowInfo("  'shutdown|exit|qui|end'\n");
3381                ShowInfo("  To know if server is alive:\n");
3382                ShowInfo("  'alive|status'\n");
3383        }
3384
3385        return 0;
3386}
3387
3388// MAP send all
3389int mapif_sendall(unsigned char *buf, unsigned int len)
3390{
3391        int i, c;
3392
3393        c = 0;
3394        for(i = 0; i < MAX_MAP_SERVERS; i++) {
3395                int fd;
3396                if ((fd = server[i].fd) > 0) {
3397                        WFIFOHEAD(fd,len);
3398                        memcpy(WFIFOP(fd,0), buf, len);
3399                        WFIFOSET(fd,len);
3400                        c++;
3401                }
3402        }
3403
3404        return c;
3405}
3406
3407int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len)
3408{
3409        int i, c;
3410
3411        c = 0;
3412        for(i = 0; i < MAX_MAP_SERVERS; i++) {
3413                int fd;
3414                if ((fd = server[i].fd) > 0 && fd != sfd) {
3415                        WFIFOHEAD(fd,len);
3416                        memcpy(WFIFOP(fd,0), buf, len);
3417                        WFIFOSET(fd,len);
3418                        c++;
3419                }
3420        }
3421
3422        return c;
3423}
3424
3425int mapif_send(int fd, unsigned char *buf, unsigned int len)
3426{
3427        int i;
3428
3429        if (fd >= 0) {
3430                ARR_FIND( 0, MAX_MAP_SERVERS, i, fd == server[i].fd );
3431                if( i < MAX_MAP_SERVERS )
3432                {
3433                        WFIFOHEAD(fd,len);
3434                        memcpy(WFIFOP(fd,0), buf, len);
3435                        WFIFOSET(fd,len);
3436                        return 1;
3437                }
3438        }
3439        return 0;
3440}
3441
3442int broadcast_user_count(int tid, unsigned int tick, int id, intptr data)
3443{
3444        uint8 buf[6];
3445        int users = count_users();
3446
3447        // only send an update when needed
3448        static int prev_users = 0;
3449        if( prev_users == users )
3450                return 0;
3451        prev_users = users;
3452
3453        if( login_fd > 0 && session[login_fd] )
3454        {
3455                // send number of user to login server
3456                WFIFOHEAD(login_fd,6);
3457                WFIFOW(login_fd,0) = 0x2714;
3458                WFIFOL(login_fd,2) = users;
3459                WFIFOSET(login_fd,6);
3460        }
3461
3462        // send number of players to all map-servers
3463        WBUFW(buf,0) = 0x2b00;
3464        WBUFL(buf,2) = users;
3465        mapif_sendall(buf,6);
3466
3467        return 0;
3468}
3469
3470/// load this char's account id into the 'online accounts' packet
3471static int send_accounts_tologin_sub(DBKey key, void* data, va_list ap)
3472{
3473        struct online_char_data* character = (struct online_char_data*)data;
3474        int* i = va_arg(ap, int*);
3475
3476        if(character->server > -1)
3477        {
3478                WFIFOL(login_fd,8+(*i)*4) = character->account_id;
3479                (*i)++;
3480                return 1;
3481        }
3482        return 0;
3483}
3484
3485int send_accounts_tologin(int tid, unsigned int tick, int id, intptr data)
3486{
3487        if (login_fd > 0 && session[login_fd])
3488        {
3489                // send account list to login server
3490                int users = online_char_db->size(online_char_db);
3491                int i = 0;
3492
3493                WFIFOHEAD(login_fd,8+users*4);
3494                WFIFOW(login_fd,0) = 0x272d;
3495                online_char_db->foreach(online_char_db, send_accounts_tologin_sub, &i, users);
3496                WFIFOW(login_fd,2) = 8+ i*4;
3497                WFIFOL(login_fd,4) = i;
3498                WFIFOSET(login_fd,WFIFOW(login_fd,2));
3499        }
3500        return 0;
3501}
3502
3503int check_connect_login_server(int tid, unsigned int tick, int id, intptr data)
3504{
3505        if (login_fd > 0 && session[login_fd] != NULL)
3506                return 0;
3507
3508        ShowInfo("Attempt to connect to login-server...\n");
3509        login_fd = make_connection(login_ip, login_port);
3510        if (login_fd == -1)
3511        {       //Try again later. [Skotlex]
3512                login_fd = 0;
3513                return 0;
3514        }
3515        session[login_fd]->func_parse = parse_fromlogin;
3516        session[login_fd]->flag.server = 1;
3517        realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
3518       
3519        WFIFOHEAD(login_fd,86);
3520        WFIFOW(login_fd,0) = 0x2710;
3521        memcpy(WFIFOP(login_fd,2), userid, 24);
3522        memcpy(WFIFOP(login_fd,26), passwd, 24);
3523        WFIFOL(login_fd,50) = 0;
3524        WFIFOL(login_fd,54) = htonl(char_ip);
3525        WFIFOL(login_fd,58) = htons(char_port);
3526        memcpy(WFIFOP(login_fd,60), server_name, 20);
3527        WFIFOW(login_fd,80) = 0;
3528        WFIFOW(login_fd,82) = char_maintenance;
3529        WFIFOW(login_fd,84) = char_new_display; //only display (New) if they want to [Kevin]
3530        WFIFOSET(login_fd,86);
3531       
3532        return 1;
3533}
3534
3535// sends a ping packet to login server (will receive pong 0x2718)
3536int ping_login_server(int tid, unsigned int tick, int id, intptr data)
3537{
3538        if (login_fd > 0 && session[login_fd] != NULL)
3539        {
3540                WFIFOHEAD(login_fd,2);
3541                WFIFOW(login_fd,0) = 0x2719;
3542                WFIFOSET(login_fd,2);
3543        }
3544        return 0;
3545}
3546
3547//------------------------------------------------
3548//Invoked 15 seconds after mapif_disconnectplayer in case the map server doesn't
3549//replies/disconnect the player we tried to kick. [Skotlex]
3550//------------------------------------------------
3551static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, intptr data)
3552{
3553        struct online_char_data* character;
3554        if ((character = (struct online_char_data*)idb_get(online_char_db, id)) != NULL && character->waiting_disconnect == tid)
3555        {       //Mark it offline due to timeout.
3556                character->waiting_disconnect = -1;
3557                set_char_offline(character->char_id, character->account_id);
3558        }
3559        return 0;
3560}
3561
3562static int online_data_cleanup_sub(DBKey key, void *data, va_list ap)
3563{
3564        struct online_char_data *character= (struct online_char_data*)data;
3565        if (character->fd != -1)
3566                return 0; //Still connected
3567        if (character->server == -2) //Unknown server.. set them offline
3568                set_char_offline(character->char_id, character->account_id);
3569        if (character->server < 0)
3570                //Free data from players that have not been online for a while.
3571                db_remove(online_char_db, key);
3572        return 0;
3573}
3574
3575static int online_data_cleanup(int tid, unsigned int tick, int id, intptr data)
3576{
3577        online_char_db->foreach(online_char_db, online_data_cleanup_sub);
3578        return 0;
3579}
3580
3581//----------------------------------
3582// Reading Lan Support configuration
3583// Rewrote: Anvanced subnet check [LuzZza]
3584//----------------------------------
3585int char_lan_config_read(const char *lancfgName)
3586{
3587        FILE *fp;
3588        int line_num = 0;
3589        char line[1024], w1[64], w2[64], w3[64], w4[64];
3590       
3591        if((fp = fopen(lancfgName, "r")) == NULL) {
3592                ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName);
3593                return 1;
3594        }
3595
3596        ShowInfo("Reading the configuration file %s...\n", lancfgName);
3597
3598        while(fgets(line, sizeof(line), fp))
3599        {
3600                line_num++;             
3601                if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n')
3602                        continue;
3603
3604                if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) {
3605       
3606                        ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num);       
3607                        continue;
3608                }
3609
3610                remove_control_chars(w1);
3611                remove_control_chars(w2);
3612                remove_control_chars(w3);
3613                remove_control_chars(w4);
3614
3615                if( strcmpi(w1, "subnet") == 0 )
3616                {
3617                        subnet[subnet_count].mask = str2ip(w2);
3618                        subnet[subnet_count].char_ip = str2ip(w3);
3619                        subnet[subnet_count].map_ip = str2ip(w4);
3620
3621                        if( (subnet[subnet_count].char_ip & subnet[subnet_count].mask) != (subnet[subnet_count].map_ip & subnet[subnet_count].mask) )
3622                        {
3623                                ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4);
3624                                continue;
3625                        }
3626                               
3627                        subnet_count++;
3628                }
3629        }
3630
3631        ShowStatus("Read information about %d subnetworks.\n", subnet_count);
3632
3633        fclose(fp);
3634        return 0;
3635}
3636#endif //TXT_SQL_CONVERT
3637
3638void sql_config_read(const char* cfgName)
3639{
3640        char line[1024], w1[1024], w2[1024];
3641        FILE* fp;
3642
3643        ShowInfo("Reading file %s...\n", cfgName);
3644
3645        if ((fp = fopen(cfgName, "r")) == NULL) {
3646                ShowError("file not found: %s\n", cfgName);
3647                return;
3648        }
3649
3650        while(fgets(line, sizeof(line), fp))
3651        {
3652                if(line[0] == '/' && line[1] == '/')
3653                        continue;
3654
3655                if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2)
3656                        continue;
3657
3658                if(!strcmpi(w1,"char_db"))
3659                        strcpy(char_db,w2);
3660#ifndef TXT_SQL_CONVERT
3661                else if(!strcmpi(w1,"gm_read_method"))
3662                        char_gm_read = config_switch(w2);
3663                //custom columns for login database
3664                else if(!strcmpi(w1,"login_db"))
3665                        strcpy(login_db,w2);
3666                else if(!strcmpi(w1,"login_db_level"))
3667                        strcpy(login_db_level,w2);
3668                else if(!strcmpi(w1,"login_db_account_id"))
3669                        strcpy(login_db_account_id,w2);
3670                else if(!strcmpi(w1,"lowest_gm_level")) {
3671                        lowest_gm_level = atoi(w2);
3672                        ShowStatus("set lowest_gm_level : %s\n", w2);
3673                }
3674#endif
3675                else if(!strcmpi(w1,"scdata_db"))
3676                        strcpy(scdata_db,w2);
3677                else if(!strcmpi(w1,"cart_db"))
3678                        strcpy(cart_db,w2);
3679                else if(!strcmpi(w1,"inventory_db"))
3680                        strcpy(inventory_db, w2);
3681                else if(!strcmpi(w1,"charlog_db"))
3682                        strcpy(charlog_db,w2);
3683                else if(!strcmpi(w1,"storage_db"))
3684                        strcpy(storage_db,w2);
3685                else if(!strcmpi(w1,"reg_db"))
3686                        strcpy(reg_db,w2);
3687                else if(!strcmpi(w1,"skill_db"))
3688                        strcpy(skill_db,w2);
3689                else if(!strcmpi(w1,"interlog_db"))
3690                        strcpy(interlog_db,w2);
3691                else if(!strcmpi(w1,"memo_db"))
3692                        strcpy(memo_db,w2);
3693                else if(!strcmpi(w1,"guild_db"))
3694                        strcpy(guild_db,w2);
3695                else if(!strcmpi(w1,"guild_alliance_db"))
3696                        strcpy(guild_alliance_db,w2);
3697                else if(!strcmpi(w1,"guild_castle_db"))
3698                        strcpy(guild_castle_db,w2);
3699                else if(!strcmpi(w1,"guild_expulsion_db"))
3700                        strcpy(guild_expulsion_db,w2);
3701                else if(!strcmpi(w1,"guild_member_db"))
3702                        strcpy(guild_member_db,w2);
3703                else if(!strcmpi(w1,"guild_skill_db"))
3704                        strcpy(guild_skill_db,w2);
3705                else if(!strcmpi(w1,"guild_position_db"))
3706                        strcpy(guild_position_db,w2);
3707                else if(!strcmpi(w1,"guild_storage_db"))
3708                        strcpy(guild_storage_db,w2);
3709                else if(!strcmpi(w1,"party_db"))
3710                        strcpy(party_db,w2);
3711                else if(!strcmpi(w1,"pet_db"))
3712                        strcpy(pet_db,w2);
3713                else if(!strcmpi(w1,"mail_db"))
3714                        strcpy(mail_db,w2);
3715                else if(!strcmpi(w1,"auction_db"))
3716                        strcpy(auction_db,w2);
3717                else if(!strcmpi(w1,"friend_db"))
3718                        strcpy(friend_db,w2);
3719                else if(!strcmpi(w1,"hotkey_db"))
3720                        strcpy(hotkey_db,w2);
3721#ifndef TXT_SQL_CONVERT
3722                else if(!strcmpi(w1,"db_path"))
3723                        strcpy(db_path,w2);
3724#endif
3725                //support the import command, just like any other config
3726                else if(!strcmpi(w1,"import"))
3727                        sql_config_read(w2);
3728        }
3729        fclose(fp);
3730        ShowInfo("Done reading %s.\n", cfgName);
3731}
3732#ifndef TXT_SQL_CONVERT
3733
3734int char_config_read(const char* cfgName)
3735{
3736        char line[1024], w1[1024], w2[1024];
3737        FILE* fp = fopen(cfgName, "r");
3738
3739        if (fp == NULL) {
3740                ShowError("Configuration file not found: %s.\n", cfgName);
3741                return 1;
3742        }
3743
3744        ShowInfo("Reading configuration file %s...\n", cfgName);
3745        while(fgets(line, sizeof(line), fp))
3746        {
3747                if (line[0] == '/' && line[1] == '/')
3748                        continue;
3749
3750                if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2)
3751                        continue;
3752
3753                remove_control_chars(w1);
3754                remove_control_chars(w2);
3755                if(strcmpi(w1,"timestamp_format") == 0) {
3756                        strncpy(timestamp_format, w2, 20);
3757                } else if(strcmpi(w1,"console_silent")==0){
3758                        ShowInfo("Console Silent Setting: %d\n", atoi(w2));
3759                        msg_silent = atoi(w2);
3760                } else if(strcmpi(w1,"stdout_with_ansisequence")==0){
3761                        stdout_with_ansisequence = config_switch(w2);
3762                } else if (strcmpi(w1, "userid") == 0) {
3763                        strncpy(userid, w2, 24);
3764                } else if (strcmpi(w1, "passwd") == 0) {
3765                        strncpy(passwd, w2, 24);
3766                } else if (strcmpi(w1, "server_name") == 0) {
3767                        strncpy(server_name, w2, 20);
3768                        server_name[sizeof(server_name) - 1] = '\0';
3769                        ShowStatus("%s server has been initialized\n", w2);
3770                } else if (strcmpi(w1, "wisp_server_name") == 0) {
3771                        if (strlen(w2) >= 4) {
3772                                memcpy(wisp_server_name, w2, sizeof(wisp_server_name));
3773                                wisp_server_name[sizeof(wisp_server_name) - 1] = '\0';
3774                        }
3775                } else if (strcmpi(w1, "login_ip") == 0) {
3776                        char ip_str[16];
3777                        login_ip = host2ip(w2);
3778                        if (login_ip) {
3779                                strncpy(login_ip_str, w2, sizeof(login_ip_str));
3780                                ShowStatus("Login server IP address : %s -> %s\n", w2, ip2str(login_ip, ip_str));
3781                        }
3782                } else if (strcmpi(w1, "login_port") == 0) {
3783                        login_port = atoi(w2);
3784                } else if (strcmpi(w1, "char_ip") == 0) {
3785                        char ip_str[16];
3786                        char_ip = host2ip(w2);
3787                        if (char_ip){
3788                                strncpy(char_ip_str, w2, sizeof(char_ip_str));
3789                                ShowStatus("Character server IP address : %s -> %s\n", w2, ip2str(char_ip, ip_str));
3790                        }
3791                } else if (strcmpi(w1, "bind_ip") == 0) {
3792                        char ip_str[16];
3793                        bind_ip = host2ip(w2);
3794                        if (bind_ip) {
3795                                strncpy(bind_ip_str, w2, sizeof(bind_ip_str));
3796                                ShowStatus("Character server binding IP address : %s -> %s\n", w2, ip2str(bind_ip, ip_str));
3797                        }
3798                } else if (strcmpi(w1, "char_port") == 0) {
3799                        char_port = atoi(w2);
3800                } else if (strcmpi(w1, "char_maintenance") == 0) {
3801                        char_maintenance = atoi(w2);
3802                } else if (strcmpi(w1, "char_new") == 0) {
3803                        char_new = (bool)atoi(w2);
3804                } else if (strcmpi(w1, "char_new_display") == 0) {
3805                        char_new_display = atoi(w2);
3806                } else if (strcmpi(w1, "max_connect_user") == 0) {
3807                        max_connect_user = atoi(w2);
3808                        if (max_connect_user < 0)
3809                                max_connect_user = 0; // unlimited online players
3810                } else if(strcmpi(w1, "gm_allow_level") == 0) {
3811                        gm_allow_level = atoi(w2);
3812                        if(gm_allow_level < 0)
3813                                gm_allow_level = 99;
3814                } else if (strcmpi(w1, "online_check") == 0) {
3815                        online_check = config_switch(w2);
3816                } else if (strcmpi(w1, "autosave_time") == 0) {
3817                        autosave_interval = atoi(w2)*1000;
3818                        if (autosave_interval <= 0)
3819                                autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
3820                } else if (strcmpi(w1, "save_log") == 0) {
3821                        save_log = config_switch(w2);
3822                } else if (strcmpi(w1, "start_point") == 0) {
3823                        char map[MAP_NAME_LENGTH_EXT];
3824                        int x, y;
3825                        if (sscanf(w2, "%15[^,],%d,%d", map, &x, &y) < 3)
3826                                continue;
3827                        start_point.map = mapindex_name2id(map);
3828                        if (!start_point.map)
3829                                ShowError("Specified start_point %s not found in map-index cache.\n", map);
3830                        start_point.x = x;
3831                        start_point.y = y;
3832                } else if (strcmpi(w1, "start_zeny") == 0) {
3833                        start_zeny = atoi(w2);
3834                        if (start_zeny < 0)
3835                                start_zeny = 0;
3836                } else if (strcmpi(w1, "start_weapon") == 0) {
3837                        start_weapon = atoi(w2);
3838                        if (start_weapon < 0)
3839                                start_weapon = 0;
3840                } else if (strcmpi(w1, "start_armor") == 0) {
3841                        start_armor = atoi(w2);
3842                        if (start_armor < 0)
3843                                start_armor = 0;
3844                } else if(strcmpi(w1,"log_char")==0) {          //log char or not [devil]
3845                        log_char = atoi(w2);
3846                } else if (strcmpi(w1, "unknown_char_name") == 0) {
3847                        strcpy(unknown_char_name, w2);
3848                        unknown_char_name[NAME_LENGTH-1] = '\0';
3849                } else if (strcmpi(w1, "name_ignoring_case") == 0) {
3850                        name_ignoring_case = config_switch(w2);
3851                } else if (strcmpi(w1, "char_name_option") == 0) {
3852                        char_name_option = atoi(w2);
3853                } else if (strcmpi(w1, "char_name_letters") == 0) {
3854                        strcpy(char_name_letters, w2);
3855                } else if (strcmpi(w1, "char_rename") == 0) {
3856                        char_rename = config_switch(w2);
3857                } else if (strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius]
3858                        char_per_account = atoi(w2);
3859                } else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus]
3860                        char_del_level = atoi(w2);
3861                } else if (strcmpi(w1, "console") == 0) {
3862                        console = config_switch(w2);
3863                } else if (strcmpi(w1, "fame_list_alchemist") == 0) {
3864                        fame_list_size_chemist = atoi(w2);
3865                        if (fame_list_size_chemist > MAX_FAME_LIST) {
3866                                ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST);
3867                                fame_list_size_chemist = MAX_FAME_LIST;
3868                        }
3869                } else if (strcmpi(w1, "fame_list_blacksmith") == 0) {
3870                        fame_list_size_smith = atoi(w2);
3871                        if (fame_list_size_smith > MAX_FAME_LIST) {
3872                                ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST);
3873                                fame_list_size_smith = MAX_FAME_LIST;
3874                        }
3875                } else if (strcmpi(w1, "fame_list_taekwon") == 0) {
3876                        fame_list_size_taekwon = atoi(w2);
3877                        if (fame_list_size_taekwon > MAX_FAME_LIST) {
3878                                ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST);
3879                                fame_list_size_taekwon = MAX_FAME_LIST;
3880                        }
3881                } else if (strcmpi(w1, "guild_exp_rate") == 0) {
3882                        guild_exp_rate = atoi(w2);
3883                } else if (strcmpi(w1, "import") == 0) {
3884                        char_config_read(w2);
3885                }
3886        }
3887        fclose(fp);
3888       
3889        ShowInfo("Done reading %s.\n", cfgName);
3890        return 0;
3891}
3892
3893void do_final(void)
3894{
3895        ShowInfo("Doing final stage...\n");
3896        //check SQL save progress.
3897        //wait until save char complete
3898
3899        set_all_offline(-1);
3900        set_all_offline_sql();
3901
3902        inter_final();
3903
3904        flush_fifos();
3905
3906        mapindex_final();
3907
3908        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ragsrvinfo") )
3909                Sql_ShowDebug(sql_handle);
3910
3911        if(gm_account)  {
3912                aFree(gm_account);
3913                gm_account = 0;
3914        }
3915
3916        if (login_fd > 0)
3917                do_close(login_fd);
3918        if (char_fd > 0)
3919                do_close(char_fd);
3920        char_db_->destroy(char_db_, NULL);
3921        online_char_db->destroy(online_char_db, NULL);
3922        auth_db->destroy(auth_db, NULL);
3923
3924        Sql_Free(sql_handle);
3925        if( lsql_handle )
3926                Sql_Free(lsql_handle);
3927
3928        ShowInfo("ok! all done...\n");
3929}
3930
3931//------------------------------
3932// Function called when the server
3933// has received a crash signal.
3934//------------------------------
3935void do_abort(void)
3936{
3937}
3938
3939void set_server_type(void)
3940{
3941        SERVER_TYPE = ATHENA_SERVER_CHAR;
3942}
3943
3944int do_init(int argc, char **argv)
3945{
3946        int i;
3947
3948        for(i = 0; i < MAX_MAP_SERVERS; i++) {
3949                memset(&server[i], 0, sizeof(struct mmo_map_server));
3950                server[i].fd = -1;
3951        }
3952
3953        //Read map indexes
3954        mapindex_init();
3955        start_point.map = mapindex_name2id("new_zone01");
3956       
3957        char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]);
3958        char_lan_config_read((argc > 3) ? argv[3] : LAN_CONF_NAME);
3959        sql_config_read(SQL_CONF_NAME);
3960
3961        if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) {
3962                ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n");
3963                ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n");
3964                ShowNotice("And then change the user/password to use in conf/char_athena.conf (or conf/import/char_conf.txt)\n");
3965        }
3966       
3967        ShowInfo("Finished reading the char-server configuration.\n");
3968
3969        inter_init_sql((argc > 2) ? argv[2] : inter_cfgName); // inter server ÃʱâÈ­
3970        ShowInfo("Finished reading the inter-server configuration.\n");
3971       
3972        ShowInfo("Initializing char server.\n");
3973        auth_db = idb_alloc(DB_OPT_RELEASE_DATA);
3974        online_char_db = idb_alloc(DB_OPT_RELEASE_DATA);
3975        mmo_char_sql_init();
3976        char_read_fame_list(); //Read fame lists.
3977        if(char_gm_read)
3978                read_gm_account();
3979        ShowInfo("char server initialized.\n");
3980
3981        set_defaultparse(parse_char);
3982
3983        if ((naddr_ != 0) && (!login_ip || !char_ip))
3984        {
3985                char ip_str[16];
3986                ip2str(addr_[0], ip_str);
3987
3988                if (naddr_ > 1)
3989                        ShowStatus("Multiple interfaces detected..  using %s as our IP address\n", ip_str);
3990                else
3991                        ShowStatus("Defaulting to %s as our IP address\n", ip_str);
3992                if (!login_ip) {
3993                        strcpy(login_ip_str, ip_str);
3994                        login_ip = str2ip(login_ip_str);
3995                }
3996                if (!char_ip) {
3997                        strcpy(char_ip_str, ip_str);
3998                        char_ip = str2ip(char_ip_str);
3999                }
4000        }
4001
4002        // establish char-login connection if not present
4003        add_timer_func_list(check_connect_login_server, "check_connect_login_server");
4004        add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000);
4005
4006        // keep the char-login connection alive
4007        add_timer_func_list(ping_login_server, "ping_login_server");
4008        add_timer_interval(gettick() + 1000, ping_login_server, 0, 0, ((int)stall_time-2) * 1000);
4009
4010        // periodically update the overall user count on all mapservers + login server
4011        add_timer_func_list(broadcast_user_count, "broadcast_user_count");
4012        add_timer_interval(gettick() + 1000, broadcast_user_count, 0, 0, 5 * 1000);
4013
4014        // send a list of all online account IDs to login server
4015        add_timer_func_list(send_accounts_tologin, "send_accounts_tologin");
4016        add_timer_interval(gettick() + 1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour.
4017
4018        // ???
4019        add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect");
4020
4021        // ???
4022        add_timer_func_list(online_data_cleanup, "online_data_cleanup");
4023        add_timer_interval(gettick() + 1000, online_data_cleanup, 0, 0, 600 * 1000);
4024
4025        if( console )
4026        {
4027                //##TODO invoke a CONSOLE_START plugin event
4028        }
4029       
4030        //Cleaning the tables for NULL entrys @ startup [Sirius]
4031        //Chardb clean
4032        ShowInfo("Cleaning the '%s' table...\n", char_db);
4033        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '0'", char_db) )
4034                Sql_ShowDebug(sql_handle);
4035
4036        //guilddb clean
4037    ShowInfo("Cleaning the '%s' table...\n", guild_db);
4038        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_lv` = '0' AND `max_member` = '0' AND `exp` = '0' AND `next_exp` = '0' AND `average_lv` = '0'", guild_db) )
4039                Sql_ShowDebug(sql_handle);
4040
4041        //guildmemberdb clean
4042        ShowInfo("Cleaning the '%s' table...\n", guild_member_db);
4043        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '0' AND `account_id` = '0' AND `char_id` = '0'", guild_member_db) )
4044                Sql_ShowDebug(sql_handle);
4045
4046        ShowInfo("End of char server initilization function.\n");
4047
4048        ShowInfo("open port %d.....\n",char_port);
4049        char_fd = make_listen_bind(bind_ip, char_port);
4050        ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", char_port);
4051
4052        return 0;
4053}
4054
4055#endif //TXT_SQL_CONVERT
Note: See TracBrowser for help on using the browser.