root/src/char/char.c @ 10

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