root/src/map/chrif.c @ 25

Revision 24, 50.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/malloc.h"
6#include "../common/socket.h"
7#include "../common/timer.h"
8#include "../common/nullpo.h"
9#include "../common/showmsg.h"
10#include "../common/strlib.h"
11#include "../common/ers.h"
12
13#include "map.h"
14#include "battle.h"
15#include "clif.h"
16#include "intif.h"
17#include "npc.h"
18#include "pc.h"
19#include "pet.h"
20#include "skill.h"
21#include "status.h"
22#include "mercenary.h"
23#include "chrif.h"
24#include "quest.h"
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/types.h>
30#include <time.h>
31
32static struct eri *auth_db_ers; //For reutilizing player login structures.
33static DBMap* auth_db; // int id -> struct auth_node*
34
35static const int packet_len_table[0x3d] = { // U - used, F - free
36        60, 3,-1,27,10,-1, 6,-1,        // 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff
37         6,-1,18, 7,-1,35,30,-1,        // 2b00-2b07: U->2b00, U->2b01, U->2b02, U->2b03, U->2b04, U->2b05, U->2b06, F->2b07
38         6,30,-1,-1,86, 7,44,34,        // 2b08-2b0f: U->2b08, U->2b09, F->2b0a, F->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f
39        11,10,10, 6,11,-1,266,10,       // 2b10-2b17: U->2b10, U->2b11, U->2b12, U->2b13, U->2b14, U->2b15, U->2b16, U->2b17
40         2,10, 2,-1,-1,-1, 2, 7,        // 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, U->2b1e, U->2b1f
41        -1,10, 8, 2, 2,14,19,19,        // 2b20-2b27: U->2b20, U->2b21, U->2b22, U->2b23, U->2b24, U->2b25, U->2b26, U->2b27
42};
43
44//Used Packets:
45//2af8: Outgoing, chrif_connect -> 'connect to charserver / auth @ charserver'
46//2af9: Incoming, chrif_connectack -> 'answer of the 2af8 login(ok / fail)'
47//2afa: Outgoing, chrif_sendmap -> 'sending our maps'
48//2afb: Incoming, chrif_sendmapack -> 'Maps received successfully / or not ..'
49//2afc: Outgoing, chrif_scdata_request -> request sc_data for pc_authok'ed char. <- new command reuses previous one.
50//2afd: Incoming, chrif_authok -> 'client authentication ok'
51//2afe: Outgoing, send_usercount_tochar -> 'sends player count of this map server to charserver'
52//2aff: Outgoing, send_users_tochar -> 'sends all actual connected character ids to charserver'
53//2b00: Incoming, map_setusers -> 'set the actual usercount? PACKET.2B COUNT.L.. ?' (not sure)
54//2b01: Outgoing, chrif_save -> 'charsave of char XY account XY (complete struct)'
55//2b02: Outgoing, chrif_charselectreq -> 'player returns from ingame to charserver to select another char.., this packets includes sessid etc' ? (not 100% sure)
56//2b03: Incoming, clif_charselectok -> '' (i think its the packet after enterworld?) (not sure)
57//2b04: Incoming, chrif_recvmap -> 'getting maps from charserver of other mapserver's'
58//2b05: Outgoing, chrif_changemapserver -> 'Tell the charserver the mapchange / quest for ok...'
59//2b06: Incoming, chrif_changemapserverack -> 'awnser of 2b05, ok/fail, data: dunno^^'
60//2b07: FREE
61//2b08: Outgoing, chrif_searchcharid -> '...'
62//2b09: Incoming, map_addchariddb -> 'Adds a name to the nick db'
63//2b0a: FREE
64//2b0b: FREE
65//2b0c: Outgoing, chrif_changeemail -> 'change mail address ...'
66//2b0d: Incoming, chrif_changedsex -> 'Change sex of acc XY'
67//2b0e: Outgoing, chrif_char_ask_name -> 'Do some operations (change sex, ban / unban etc)'
68//2b0f: Incoming, chrif_char_ask_name_answer -> 'answer of the 2b0e'
69//2b10: Outgoing, chrif_updatefamelist -> 'Update the fame ranking lists and send them'
70//2b11: Outgoing, chrif_divorce -> 'tell the charserver to do divorce'
71//2b12: Incoming, chrif_divorceack -> 'divorce chars
72//2b13: Incoming, chrif_accountdeletion -> 'Delete acc XX, if the player is on, kick ....'
73//2b14: Incoming, chrif_accountban -> 'not sure: kick the player with message XY'
74//2b15: Incoming, chrif_recvgmaccounts -> 'receive gm accs from charserver (seems to be incomplete !)'
75//2b16: Outgoing, chrif_ragsrvinfo -> 'sends motd / rates ....'
76//2b17: Outgoing, chrif_char_offline -> 'tell the charserver that the char is now offline'
77//2b18: Outgoing, chrif_char_reset_offline -> 'set all players OFF!'
78//2b19: Outgoing, chrif_char_online -> 'tell the charserver that the char .. is online'
79//2b1a: Outgoing, chrif_buildfamelist -> 'Build the fame ranking lists and send them'
80//2b1b: Incoming, chrif_recvfamelist -> 'Receive fame ranking lists'
81//2b1c: Outgoing, chrif_save_scdata -> 'Send sc_data of player for saving.'
82//2b1d: Incoming, chrif_load_scdata -> 'received sc_data of player for loading.'
83//2b1e: Incoming, chrif_update_ip -> 'Reqest forwarded from char-server for interserver IP sync.' [Lance]
84//2b1f: Incoming, chrif_disconnectplayer -> 'disconnects a player (aid X) with the message XY ... 0x81 ..' [Sirius]
85//2b20: Incoming, chrif_removemap -> 'remove maps of a server (sample: its going offline)' [Sirius]
86//2b21: Incoming, chrif_save_ack. Returned after a character has been "final saved" on the char-server. [Skotlex]
87//2b22: Incoming, chrif_updatefamelist_ack. Updated one position in the fame list.
88//2b23: Outgoing, chrif_keepalive. charserver ping.
89//2b24: Incoming, chrif_keepalive_ack. charserver ping reply.
90//2b25: Incoming, chrif_deadopt -> 'Removes baby from Father ID and Mother ID'
91//2b26: Outgoing, chrif_authreq -> 'client authentication request'
92//2b27: Incoming, chrif_authfail -> 'client authentication failed'
93
94int chrif_connected = 0;
95int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex]
96int srvinfo;
97static char char_ip_str[128];
98static uint32 char_ip = 0;
99static uint16 char_port = 6121;
100static char userid[NAME_LENGTH], passwd[NAME_LENGTH];
101static int chrif_state = 0;
102int other_mapserver_count=0; //Holds count of how many other map servers are online (apart of this instance) [Skotlex]
103
104//Interval at which map server updates online listing. [Valaris]
105#define CHECK_INTERVAL 3600000
106//Interval at which map server sends number of connected users. [Skotlex]
107#define UPDATE_INTERVAL 10000
108//This define should spare writing the check in every function. [Skotlex]
109#define chrif_check(a) { if(!chrif_isconnected()) return a; }
110
111struct auth_node* chrif_search(int account_id)
112{
113        return (struct auth_node*)idb_get(auth_db, account_id);
114}
115
116struct auth_node* chrif_auth_check(int account_id, int char_id, enum sd_state state)
117{
118        struct auth_node *node = chrif_search(account_id);
119        return (node && node->char_id == char_id && node->state == state)?node:NULL;
120}
121
122bool chrif_auth_delete(int account_id, int char_id, enum sd_state state)
123{
124        struct auth_node *node;
125        if ((node=chrif_auth_check(account_id, char_id, state)))
126        {
127                int fd = node->sd?node->sd->fd:node->fd;
128                if (session[fd] && session[fd]->session_data == node->sd)
129                        session[fd]->session_data = NULL;
130                if (node->char_dat) aFree(node->char_dat);
131                if (node->sd) aFree(node->sd);
132                ers_free(auth_db_ers, node);
133                idb_remove(auth_db,account_id);
134                return true;
135        }
136        return false;
137}
138
139//Moves the sd character to the auth_db structure.
140static bool chrif_sd_to_auth(TBL_PC* sd, enum sd_state state)
141{
142        struct auth_node *node;
143        if (chrif_search(sd->status.account_id))
144                return false; //Already exists?
145
146        node = ers_alloc(auth_db_ers, struct auth_node);
147        memset(node, 0, sizeof(struct auth_node));
148        node->account_id = sd->status.account_id;
149        node->char_id = sd->status.char_id;
150        node->login_id1 = sd->login_id1;
151        node->login_id2 = sd->login_id2;
152        node->sex = sd->status.sex;
153        node->fd = sd->fd;
154        node->sd = sd;  //Data from logged on char.
155        node->node_created = gettick(); //timestamp for node timeouts
156        node->state = state;
157
158        sd->state.active = 0;
159        idb_put(auth_db, node->account_id, node);
160        return true;
161}
162
163static bool chrif_auth_logout(TBL_PC* sd, enum sd_state state)
164{
165        if(sd->fd && state == ST_LOGOUT)
166        {       //Disassociate player, and free it after saving ack returns. [Skotlex]
167                //fd info must not be lost for ST_MAPCHANGE as a final packet needs to be sent to the player.
168                if (session[sd->fd])
169                        session[sd->fd]->session_data = NULL;
170                sd->fd = 0;
171        }
172        return chrif_sd_to_auth(sd, state);
173}
174
175bool chrif_auth_finished(TBL_PC* sd)
176{
177        struct auth_node *node= chrif_search(sd->status.account_id);
178        if (node && node->sd == sd && node->state == ST_LOGIN) {
179                node->sd = NULL;
180                return chrif_auth_delete(node->account_id, node->char_id, ST_LOGIN);
181        }
182        return false;
183}
184// sets char-server's user id
185void chrif_setuserid(char *id)
186{
187        memcpy(userid, id, NAME_LENGTH);
188}
189
190// sets char-server's password
191void chrif_setpasswd(char *pwd)
192{
193        memcpy(passwd, pwd, NAME_LENGTH);
194}
195
196// security check, prints warning if using default password
197void chrif_checkdefaultlogin(void)
198{
199        if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) {
200                ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n");
201#ifdef TXT_ONLY
202                ShowNotice("Please edit your save/account.txt file to create a proper inter-server user/password (gender 'S')\n");
203#else
204                ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n");
205#endif
206                ShowNotice("and then edit your user/password in conf/map_athena.conf (or conf/import/map_conf.txt)\n");
207        }
208}
209
210// sets char-server's ip address
211int chrif_setip(const char* ip)
212{
213        char ip_str[16];
214        char_ip = host2ip(ip);
215        if (!char_ip) {
216                ShowWarning("Failed to Resolve Char Server Address! (%s)\n", ip);
217                return 0;
218        }
219        strncpy(char_ip_str, ip, sizeof(char_ip_str));
220        ShowInfo("Char Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(char_ip, ip_str));
221        return 1;
222}
223
224// sets char-server's port number
225void chrif_setport(uint16 port)
226{
227        char_port = port;
228}
229
230// says whether the char-server is connected or not
231int chrif_isconnected(void)
232{
233        return (char_fd > 0 && session[char_fd] != NULL && chrif_state == 2);
234}
235
236/*==========================================
237 * Saves character data.
238 * Flag = 1: Character is quitting
239 * Flag = 2: Character is changing map-servers
240 *------------------------------------------*/
241int chrif_save(struct map_session_data *sd, int flag)
242{
243        nullpo_retr(-1, sd);
244
245        if (!flag) //The flag check is needed to prevent 'nosave' taking effect when a jailed player logs out.
246                pc_makesavestatus(sd);
247       
248        if (flag && sd->state.active) //Store player data which is quitting.
249        {
250                //FIXME: SC are lost if there's no connection at save-time because of the way its related data is cleared immediately after this function. [Skotlex]
251                if (chrif_isconnected()) chrif_save_scdata(sd);
252                if (!chrif_auth_logout(sd, flag==1?ST_LOGOUT:ST_MAPCHANGE))
253                        ShowError("chrif_save: Failed to set up player %d:%d for proper quitting!\n", sd->status.account_id, sd->status.char_id);
254        }
255
256        if(!chrif_isconnected())
257                return -1; //Character is saved on reconnect.
258
259        //For data sync
260        if (sd->state.storage_flag == 1)
261                storage_storage_save(sd->status.account_id, flag);
262        else if (sd->state.storage_flag == 2)
263                storage_guild_storagesave(sd->status.account_id, sd->status.guild_id, flag);
264        if (flag) sd->state.storage_flag = 0; //Force close it.
265
266        //Saving of registry values.
267        if (sd->state.reg_dirty&4)
268                intif_saveregistry(sd, 3); //Save char regs
269        if (sd->state.reg_dirty&2)
270                intif_saveregistry(sd, 2); //Save account regs
271        if (sd->state.reg_dirty&1)
272                intif_saveregistry(sd, 1); //Save account2 regs
273
274        WFIFOHEAD(char_fd, sizeof(sd->status) + 13);
275        WFIFOW(char_fd,0) = 0x2b01;
276        WFIFOW(char_fd,2) = sizeof(sd->status) + 13;
277        WFIFOL(char_fd,4) = sd->status.account_id;
278        WFIFOL(char_fd,8) = sd->status.char_id;
279        WFIFOB(char_fd,12) = (flag==1)?1:0; //Flag to tell char-server this character is quitting.
280        memcpy(WFIFOP(char_fd,13), &sd->status, sizeof(sd->status));
281        WFIFOSET(char_fd, WFIFOW(char_fd,2));
282
283
284        if(sd->status.pet_id > 0 && sd->pd)
285                intif_save_petdata(sd->status.account_id,&sd->pd->pet);
286
287        if (sd->hd && merc_is_hom_active(sd->hd))
288                merc_save(sd->hd);
289
290        return 0;
291}
292
293// connects to char-server (plaintext)
294int chrif_connect(int fd)
295{
296        ShowStatus("Logging in to char server...\n", char_fd);
297        WFIFOHEAD(fd,60);
298        WFIFOW(fd,0) = 0x2af8;
299        memcpy(WFIFOP(fd,2), userid, NAME_LENGTH);
300        memcpy(WFIFOP(fd,26), passwd, NAME_LENGTH);
301        WFIFOL(fd,50) = 0;
302        WFIFOL(fd,54) = htonl(clif_getip());
303        WFIFOW(fd,58) = htons(clif_getport());
304        WFIFOSET(fd,60);
305
306        return 0;
307}
308
309// sends maps to char-server
310int chrif_sendmap(int fd)
311{
312        int i;
313        ShowStatus("Sending maps to char server...\n");
314        WFIFOHEAD(fd, 4 + map_num * 4);
315        WFIFOW(fd,0) = 0x2afa;
316        for(i = 0; i < map_num; i++)
317                WFIFOW(fd,4+i*4) = map[i].index;
318        WFIFOW(fd,2) = 4 + i * 4;
319        WFIFOSET(fd,WFIFOW(fd,2));
320
321        return 0;
322}
323
324// receive maps from some other map-server (relayed via char-server)
325int chrif_recvmap(int fd)
326{
327        int i, j;
328        uint32 ip = ntohl(RFIFOL(fd,4));
329        uint16 port = ntohs(RFIFOW(fd,8));
330
331        for(i = 10, j = 0; i < RFIFOW(fd,2); i += 4, j++) {
332                map_setipport(RFIFOW(fd,i), ip, port);
333        }
334        if (battle_config.etc_log)
335                ShowStatus("Received maps from %d.%d.%d.%d:%d (%d maps)\n", CONVIP(ip), port, j);
336
337        other_mapserver_count++;
338        return 0;
339}
340
341// remove specified maps (used when some other map-server disconnects)
342int chrif_removemap(int fd)
343{
344        int i, j;
345        uint32 ip =  RFIFOL(fd,4);
346        uint16 port = RFIFOW(fd,8);
347
348        for(i = 10, j = 0; i < RFIFOW(fd, 2); i += 4, j++)
349                map_eraseipport(RFIFOW(fd, i), ip, port);
350
351        other_mapserver_count--;
352        if(battle_config.etc_log)
353                ShowStatus("remove map of server %d.%d.%d.%d:%d (%d maps)\n", CONVIP(ip), port, j);
354        return 0;
355}
356
357// received after a character has been "final saved" on the char-server
358static void chrif_save_ack(int fd)
359{
360        chrif_auth_delete(RFIFOL(fd,2), RFIFOL(fd,6), ST_LOGOUT);
361}
362
363// request to move a character between mapservers
364int chrif_changemapserver(struct map_session_data* sd, uint32 ip, uint16 port)
365{
366        nullpo_retr(-1, sd);
367
368        if (other_mapserver_count < 1)
369        {       //No other map servers are online!
370                clif_authfail_fd(sd->fd, 0);
371                return -1;
372        }
373
374        chrif_check(-1);
375
376        WFIFOHEAD(char_fd,35);
377        WFIFOW(char_fd, 0) = 0x2b05;
378        WFIFOL(char_fd, 2) = sd->bl.id;
379        WFIFOL(char_fd, 6) = sd->login_id1;
380        WFIFOL(char_fd,10) = sd->login_id2;
381        WFIFOL(char_fd,14) = sd->status.char_id;
382        WFIFOW(char_fd,18) = sd->mapindex;
383        WFIFOW(char_fd,20) = sd->bl.x;
384        WFIFOW(char_fd,22) = sd->bl.y;
385        WFIFOL(char_fd,24) = htonl(ip);
386        WFIFOW(char_fd,28) = htons(port);
387        WFIFOB(char_fd,30) = sd->status.sex;
388        WFIFOL(char_fd,31) = htonl(session[sd->fd]->client_addr);
389        WFIFOSET(char_fd,35);
390        return 0;
391}
392
393/// map-server change request acknowledgement (positive or negative)
394/// R 2b06 <account_id>.L <login_id1>.L <login_id2>.L <char_id>.L <map_index>.W <x>.W <y>.W <ip>.L <port>.W
395int chrif_changemapserverack(int account_id, int login_id1, int login_id2, int char_id, short map_index, short x, short y, uint32 ip, uint16 port)
396{
397        struct auth_node *node;
398        if (!(node=chrif_auth_check(account_id, char_id, ST_MAPCHANGE)))
399                return -1;
400
401        if (!login_id1) {
402                ShowError("map server change failed.\n");
403                clif_authfail_fd(node->fd, 0);
404        } else
405                clif_changemapserver(node->sd, map_index, x, y, ntohl(ip), ntohs(port));
406
407        //Player has been saved already, remove him from memory. [Skotlex]
408        chrif_auth_delete(account_id, char_id, ST_MAPCHANGE);
409
410        return 0;
411}
412
413/*==========================================
414 *
415 *------------------------------------------*/
416int chrif_connectack(int fd)
417{
418        static bool char_init_done = false;
419
420        if (RFIFOB(fd,2)) {
421                ShowFatalError("Connection to char-server failed %d.\n", RFIFOB(fd,2));
422                exit(EXIT_FAILURE);
423        }
424
425        ShowStatus("Successfully logged on to Char Server (Connection: '"CL_WHITE"%d"CL_RESET"').\n",fd);
426        chrif_state = 1;
427        chrif_connected = 1;
428
429        chrif_sendmap(fd);
430
431        ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit"));
432        ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit"));
433        if( !char_init_done ) {
434                char_init_done = true;
435                ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce"));
436        }
437
438        return 0;
439}
440static int chrif_reconnect(DBKey key,void *data,va_list ap)
441{
442        struct auth_node *node=(struct auth_node*)data;
443        switch (node->state) {
444        case ST_LOGIN:
445                if (node->sd && node->char_dat == NULL)
446                {       //Since there is no way to request the char auth, make it fail.
447                        pc_authfail(node->sd);
448                        chrif_char_offline(node->sd);
449                        chrif_auth_delete(node->account_id, node->char_id, ST_LOGIN);
450                }
451                break;
452        case ST_LOGOUT:
453                //Re-send final save
454                chrif_save(node->sd, 1);
455                break;
456        case ST_MAPCHANGE:
457                {       //Re-send map-change request.
458                struct map_session_data *sd = node->sd;
459                uint32 ip;
460                uint16 port;
461                if(map_mapname2ipport(sd->mapindex,&ip,&port)==0)
462                        chrif_changemapserver(sd, ip, port);
463                else //too much lag/timeout is the closest explanation for this error.
464                        clif_authfail_fd(sd->fd, 3);
465                break;
466                }
467        }
468        return 0;
469}
470
471/*==========================================
472 *
473 *------------------------------------------*/
474int chrif_sendmapack(int fd)
475{
476        if (RFIFOB(fd,2)) {
477                ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2));
478                exit(EXIT_FAILURE);
479        }
480
481        memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH);
482        ShowStatus("Map sending complete. Map Server is now online.\n");
483        chrif_state = 2;
484
485        //If there are players online, send them to the char-server. [Skotlex]
486        send_users_tochar();
487       
488        //Re-save any storages that were modified in the disconnection time. [Skotlex]
489        auth_db->foreach(auth_db,chrif_reconnect);
490        do_reconnect_storage();
491
492        return 0;
493}
494
495/*==========================================
496 * Request sc_data from charserver [Skotlex]
497 *------------------------------------------*/
498int chrif_scdata_request(int account_id, int char_id)
499{
500#ifdef ENABLE_SC_SAVING
501        chrif_check(-1);
502
503        WFIFOHEAD(char_fd,10);
504        WFIFOW(char_fd,0) = 0x2afc;
505        WFIFOL(char_fd,2) = account_id;
506        WFIFOL(char_fd,6) = char_id;
507        WFIFOSET(char_fd,10);
508#endif
509        return 0;
510}
511
512/*==========================================
513 * new auth system [Kevin]
514 *------------------------------------------*/
515void chrif_authreq(struct map_session_data *sd)
516{
517        struct auth_node *node= chrif_search(sd->bl.id);
518
519        if(!node) {
520                //request data from char server and store current auth info
521                WFIFOHEAD(char_fd,19);
522                WFIFOW(char_fd,0) = 0x2b26;
523                WFIFOL(char_fd,2) = sd->status.account_id;
524                WFIFOL(char_fd,6) = sd->status.char_id;
525                WFIFOL(char_fd,10) = sd->login_id1;
526                WFIFOB(char_fd,14) = sd->status.sex;
527                WFIFOL(char_fd,15) = htonl(session[sd->fd]->client_addr);
528                WFIFOSET(char_fd,19);
529                chrif_sd_to_auth(sd, ST_LOGIN);
530                return;
531        } else {        //char already online? kick connect request and tell char server that this person is online
532                                        //This case shouldn't happen in an ideal system
533                pc_authfail(sd);
534                chrif_char_online(sd);
535        }
536        return;
537}
538
539//character selected, insert into auth db
540void chrif_authok(int fd)
541{
542        int account_id;
543        uint32 login_id1;
544        time_t expiration_time;
545        uint32 login_id2;
546        struct mmo_charstatus* status;
547        int char_id;
548        struct auth_node *node;
549        TBL_PC* sd;
550
551        //Check if both servers agree on the struct's size
552        if( RFIFOW(fd,2) - 20 != sizeof(struct mmo_charstatus) )
553        {
554                ShowError("chrif_authok: Data size mismatch! %d != %d\n", RFIFOW(fd,2) - 20, sizeof(struct mmo_charstatus));
555                return;
556        }
557
558        account_id = RFIFOL(fd,4);
559        login_id1 = RFIFOL(fd,8);
560        expiration_time = (time_t)(int32)RFIFOL(fd,12);
561        login_id2 = RFIFOL(fd,16);
562        status = (struct mmo_charstatus*)RFIFOP(fd,20);
563        char_id = status->char_id;
564
565        //Check if we don't already have player data in our server
566        //Causes problems if the currently connected player tries to quit or this data belongs to an already connected player which is trying to re-auth.
567        if ((sd = map_id2sd(account_id)) != NULL)
568                return;
569       
570        if ((node = chrif_search(account_id)))
571        {       //Is the character already awaiting authorization?
572                if (node->state != ST_LOGIN)
573                        return; //character in logout phase, do not touch that data.
574                if (node->sd)
575                {
576                        sd = node->sd;
577                        if(node->char_dat == NULL &&
578                                node->account_id == account_id &&
579                                node->char_id == char_id &&
580                                node->login_id1 == login_id1 )
581                        { //Auth Ok
582                                if (pc_authok(sd, login_id2, expiration_time, status))
583                                {
584                                        return;
585                                }
586                        } else { //Auth Failed
587                                pc_authfail(sd);
588                        }
589                        chrif_char_offline(sd); //Set client offline
590                        chrif_auth_delete(account_id, char_id, ST_LOGIN);
591                        return;
592                }
593                //When we receive double login info and the client has not connected yet,
594                //discard the older one and keep the new one.
595                chrif_auth_delete(node->account_id, node->char_id, ST_LOGIN);
596        }
597}
598
599// client authentication failed
600void chrif_authfail(int fd)
601{
602        int account_id;
603        int char_id;
604        uint32 login_id1;
605        char sex;
606        uint32 ip;
607        struct auth_node* node;
608
609        account_id = RFIFOL(fd,2);
610        char_id    = RFIFOL(fd,6);
611        login_id1  = RFIFOL(fd,10);
612        sex        = RFIFOB(fd,14);
613        ip         = ntohl(RFIFOL(fd,15));
614
615        node = chrif_search(account_id);
616        if( node != NULL &&
617                node->account_id == account_id &&
618                node->char_id == char_id &&
619                node->login_id1 == login_id1 &&
620                node->sex == sex &&
621                node->state == ST_LOGIN )
622        {// found a match
623                clif_authfail_fd(node->fd, 0);
624                chrif_auth_delete(account_id, char_id, ST_LOGIN);
625        }
626}
627
628
629//This can still happen (client times out while waiting for char to confirm auth data)
630int auth_db_cleanup_sub(DBKey key,void *data,va_list ap)
631{
632        struct auth_node *node=(struct auth_node*)data;
633        const char* states[] = { "Login", "Logout", "Map change" };
634        if(DIFF_TICK(gettick(),node->node_created)>60000) {
635                switch (node->state)
636                {
637                case ST_LOGOUT:
638                        //Re-save attempt (->sd should never be null here).
639                        node->node_created = gettick(); //Refresh tick (avoid char-server load if connection is really bad)
640                        chrif_save(node->sd, 1);
641                        break;
642                default:
643                        //Clear data. any connected players should have timed out by now.
644                        ShowInfo("auth_db: Node (state %s) timed out for %d:%d\n", states[node->state], node->account_id, node->char_id);
645                        chrif_char_offline_nsd(node->account_id, node->char_id);
646                        chrif_auth_delete(node->account_id, node->char_id, node->state);
647                        break;
648                }
649                return 1;
650        }
651        return 0;
652}
653
654int auth_db_cleanup(int tid, unsigned int tick, int id, intptr data)
655{
656        if(!chrif_isconnected()) return 0;
657        auth_db->foreach(auth_db, auth_db_cleanup_sub);
658        return 0;
659}
660
661/*==========================================
662 *
663 *------------------------------------------*/
664int chrif_charselectreq(struct map_session_data* sd, uint32 s_ip)
665{
666        nullpo_retr(-1, sd);
667
668        if( !sd || !sd->bl.id || !sd->login_id1 )
669                return -1;
670        chrif_check(-1);
671
672        WFIFOHEAD(char_fd,18);
673        WFIFOW(char_fd, 0) = 0x2b02;
674        WFIFOL(char_fd, 2) = sd->bl.id;
675        WFIFOL(char_fd, 6) = sd->login_id1;
676        WFIFOL(char_fd,10) = sd->login_id2;
677        WFIFOL(char_fd,14) = htonl(s_ip);
678        WFIFOSET(char_fd,18);
679
680        return 0;
681}
682
683/*==========================================
684 * ƒLƒƒƒ‰–Œ–â‚¢‡‚킹
685 *------------------------------------------*/
686int chrif_searchcharid(int char_id)
687{
688        if( !char_id )
689                return -1;
690        chrif_check(-1);
691
692        WFIFOHEAD(char_fd,6);
693        WFIFOW(char_fd,0) = 0x2b08;
694        WFIFOL(char_fd,2) = char_id;
695        WFIFOSET(char_fd,6);
696
697        return 0;
698}
699
700/*==========================================
701 * Change Email
702 *------------------------------------------*/
703int chrif_changeemail(int id, const char *actual_email, const char *new_email)
704{
705        if (battle_config.etc_log)
706                ShowInfo("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", id, actual_email, new_email);
707
708        chrif_check(-1);
709
710        WFIFOHEAD(char_fd,86);
711        WFIFOW(char_fd,0) = 0x2b0c;
712        WFIFOL(char_fd,2) = id;
713        memcpy(WFIFOP(char_fd,6), actual_email, 40);
714        memcpy(WFIFOP(char_fd,46), new_email, 40);
715        WFIFOSET(char_fd,86);
716
717        return 0;
718}
719
720/*==========================================
721 * S 2b0e <accid>.l <name>.24B <type>.w { <year>.w <month>.w <day>.w <hour>.w <minute>.w <second>.w }
722 * Send an account modification request to the login server (via char server).
723 * type of operation:
724 *   1: block, 2: ban, 3: unblock, 4: unban, 5: changesex (use next function for 5)
725 *------------------------------------------*/
726int chrif_char_ask_name(int acc, const char* character_name, unsigned short operation_type, int year, int month, int day, int hour, int minute, int second)
727{
728        chrif_check(-1);
729
730        WFIFOHEAD(char_fd,44);
731        WFIFOW(char_fd,0) = 0x2b0e;
732        WFIFOL(char_fd,2) = acc;
733        safestrncpy((char*)WFIFOP(char_fd,6), character_name, NAME_LENGTH);
734        WFIFOW(char_fd,30) = operation_type;
735        if (operation_type == 2) {
736                WFIFOW(char_fd,32) = year;
737                WFIFOW(char_fd,34) = month;
738                WFIFOW(char_fd,36) = day;
739                WFIFOW(char_fd,38) = hour;
740                WFIFOW(char_fd,40) = minute;
741                WFIFOW(char_fd,42) = second;
742        }
743        WFIFOSET(char_fd,44);
744        return 0;
745}
746
747int chrif_changesex(struct map_session_data *sd)
748{
749        chrif_check(-1);
750        WFIFOHEAD(char_fd,44);
751        WFIFOW(char_fd,0) = 0x2b0e;
752        WFIFOL(char_fd,2) = sd->status.account_id;
753        safestrncpy((char*)WFIFOP(char_fd,6), sd->status.name, NAME_LENGTH);
754        WFIFOW(char_fd,30) = 5;
755        WFIFOSET(char_fd,44);
756
757        clif_displaymessage(sd->fd, "Need disconnection to perform change-sex request...");
758
759        if (sd->fd)
760                clif_authfail_fd(sd->fd, 15);
761        else
762                map_quit(sd);
763        return 0;
764}
765
766/*==========================================
767 * R 2b0f <accid>.l <name>.24B <type>.w <answer>.w
768 * Processing a reply to chrif_char_ask_name() (request to modify an account).
769 * type of operation:
770 *   1: block, 2: ban, 3: unblock, 4: unban, 5: changesex
771 * type of answer:
772 *   0: login-server request done
773 *   1: player not found
774 *   2: gm level too low
775 *   3: login-server offline
776 *------------------------------------------*/
777static void chrif_char_ask_name_answer(int acc, const char* player_name, uint16 type, uint16 answer)
778{
779        struct map_session_data* sd;
780        const char* action;
781        char output[256];
782       
783        sd = map_id2sd(acc);
784        if( acc < 0 || sd == NULL ) {
785                ShowError("chrif_char_ask_name_answer failed - player not online.\n");
786                return;
787        }
788
789        switch( type ) {
790        case 1 : action = "block"; break;
791        case 2 : action = "ban"; break;
792        case 3 : action = "unblock"; break;
793        case 4 : action = "unban"; break;
794        case 5 : action = "change the sex of"; break;
795        default: action = "???"; break;
796        }
797       
798        switch( answer ) {
799        case 0 : sprintf(output, "Login-server has been asked to %s the player '%.*s'.", action, NAME_LENGTH, player_name); break;
800        case 1 : sprintf(output, "The player '%.*s' doesn't exist.", NAME_LENGTH, player_name); break;
801        case 2 : sprintf(output, "Your GM level don't authorise you to %s the player '%.*s'.", action, NAME_LENGTH, player_name); break;
802        case 3 : sprintf(output, "Login-server is offline. Impossible to %s the player '%.*s'.", action, NAME_LENGTH, player_name); break;
803        default: output[0] = '\0'; break;
804        }
805       
806        clif_displaymessage(sd->fd, output);
807}
808
809/*==========================================
810 * «•ʕω»I—¹ (modified by Yor)
811 *------------------------------------------*/
812int chrif_changedsex(int fd)
813{
814        int acc, sex, i;
815        struct map_session_data *sd;
816
817        acc = RFIFOL(fd,2);
818        sex = RFIFOL(fd,6);
819        if (battle_config.etc_log)
820                ShowNotice("chrif_changedsex %d.\n", acc);
821        sd = map_id2sd(acc);
822        if (sd) { //Normally there should not be a char logged on right now!
823                if (sd->status.sex == sex) 
824                        return 0; //Do nothing? Likely safe.
825                sd->status.sex = !sd->status.sex;
826
827                // reset skill of some job
828                if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) {
829                        // remove specifical skills of Bard classes
830                        for(i = 315; i <= 322; i++) {
831                                if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
832                                        if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv)
833                                                sd->status.skill_point = USHRT_MAX;
834                                        else
835                                                sd->status.skill_point += sd->status.skill[i].lv;
836                                        sd->status.skill[i].id = 0;
837                                        sd->status.skill[i].lv = 0;
838                                }
839                        }
840                        // remove specifical skills of Dancer classes
841                        for(i = 323; i <= 330; i++) {
842                                if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
843                                        if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv)
844                                                sd->status.skill_point = USHRT_MAX;
845                                        else
846                                                sd->status.skill_point += sd->status.skill[i].lv;
847                                        sd->status.skill[i].id = 0;
848                                        sd->status.skill[i].lv = 0;
849                                }
850                        }
851                        clif_updatestatus(sd, SP_SKILLPOINT);
852                        // change job if necessary
853                        if (sd->status.sex) //Changed from Dancer
854                                sd->status.class_ -= 1;
855                        else    //Changed from Bard
856                                sd->status.class_ += 1;
857                        //sd->class_ needs not be updated as both Dancer/Bard are the same.
858                }
859                // save character
860                sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
861                                                          // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it)
862                clif_displaymessage(sd->fd, "Your sex has been changed (need disconnection by the server)...");
863                set_eof(sd->fd); // forced to disconnect for the change
864        }
865        return 0;
866}
867/*==========================================
868 * Request Char Server to Divorce Players
869 *------------------------------------------*/
870int chrif_divorce(int partner_id1, int partner_id2)
871{
872        chrif_check(-1);
873
874        WFIFOHEAD(char_fd,10);
875        WFIFOW(char_fd,0) = 0x2b11;
876        WFIFOL(char_fd,2) = partner_id1;
877        WFIFOL(char_fd,6) = partner_id2;
878        WFIFOSET(char_fd,10);
879
880        return 0;
881}
882
883/*==========================================
884 * Divorce players
885 *------------------------------------------*/
886int chrif_divorceack(int char_id, int partner_id)
887{
888        struct map_session_data* sd;
889        int i;
890
891        if (!char_id || !partner_id || (sd = map_charid2sd(partner_id)) == NULL || sd->status.partner_id != char_id)
892                return 0;
893
894        // Update Partner info
895        sd->status.partner_id = 0;
896
897        // Remove Wedding Rings from inventory
898        for(i = 0; i < MAX_INVENTORY; i++)
899                if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
900                        pc_delitem(sd, i, 1, 0);
901
902        return 0;
903}
904/*==========================================
905 * Removes Baby from parents
906 *------------------------------------------*/
907int chrif_deadopt(int father_id, int mother_id, int child_id)
908{
909        struct map_session_data* sd;
910
911        if( father_id && (sd = map_charid2sd(father_id)) != NULL && sd->status.child == child_id )
912        {
913                sd->status.child = 0;
914                sd->status.skill[WE_CALLBABY].id = 0;
915                sd->status.skill[WE_CALLBABY].lv = 0;
916                sd->status.skill[WE_CALLBABY].flag = 0;
917                clif_skillinfoblock(sd);
918        }
919
920        if( mother_id && (sd = map_charid2sd(mother_id)) != NULL && sd->status.child == child_id )
921        {
922                sd->status.child = 0;
923                sd->status.skill[WE_CALLBABY].id = 0;
924                sd->status.skill[WE_CALLBABY].lv = 0;
925                sd->status.skill[WE_CALLBABY].flag = 0;
926                clif_skillinfoblock(sd);
927        }
928
929        return 0;
930}
931/*==========================================
932 * Disconnection of a player (account has been deleted in login-server) by [Yor]
933 *------------------------------------------*/
934int chrif_accountdeletion(int fd)
935{
936        int acc;
937        struct map_session_data *sd;
938
939        acc = RFIFOL(fd,2);
940        if (battle_config.etc_log)
941                ShowNotice("chrif_accountdeletion %d.\n", acc);
942        sd = map_id2sd(acc);
943        if (acc > 0) {
944                if (sd != NULL) {
945                        sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
946                        clif_displaymessage(sd->fd, "Your account has been deleted (disconnection)...");
947                        set_eof(sd->fd); // forced to disconnect for the change
948                }
949        } else {
950                if (sd != NULL)
951                        ShowError("chrif_accountdeletion failed - player not online.\n");
952        }
953
954        return 0;
955}
956
957/*==========================================
958 * Disconnection of a player (account has been banned of has a status, from login-server) by [Yor]
959 *------------------------------------------*/
960int chrif_accountban(int fd)
961{
962        int acc;
963        struct map_session_data *sd;
964
965        acc = RFIFOL(fd,2);
966        if (battle_config.etc_log)
967                ShowNotice("chrif_accountban %d.\n", acc);
968        sd = map_id2sd(acc);
969
970        if (acc < 0 || sd == NULL) {
971                ShowError("chrif_accountban failed - player not online.\n");
972                return 0;
973        }
974
975        sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
976        if (RFIFOB(fd,6) == 0) // 0: change of statut, 1: ban
977        { 
978                switch (RFIFOL(fd,7)) { // status or final date of a banishment
979                case 1: clif_displaymessage(sd->fd, "Your account has 'Unregistered'."); break;
980                case 2: clif_displaymessage(sd->fd, "Your account has an 'Incorrect Password'..."); break;
981                case 3: clif_displaymessage(sd->fd, "Your account has expired."); break;
982                case 4: clif_displaymessage(sd->fd, "Your account has been rejected from server."); break;
983                case 5: clif_displaymessage(sd->fd, "Your account has been blocked by the GM Team."); break;
984                case 6: clif_displaymessage(sd->fd, "Your Game's EXE file is not the latest version."); break;
985                case 7: clif_displaymessage(sd->fd, "Your account has been prohibited to log in."); break;
986                case 8: clif_displaymessage(sd->fd, "Server is jammed due to over populated."); break;
987                case 9: clif_displaymessage(sd->fd, "Your account has not more authorised."); break;
988                case 100: clif_displaymessage(sd->fd, "Your account has been totally erased."); break;
989                default:  clif_displaymessage(sd->fd, "Your account has not more authorised."); break;
990                }
991        }
992        else if (RFIFOB(fd,6) == 1) // 0: change of statut, 1: ban
993        { 
994                time_t timestamp;
995                char tmpstr[2048];
996                timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment
997                strcpy(tmpstr, "Your account has been banished until ");
998                strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(&timestamp));
999                clif_displaymessage(sd->fd, tmpstr);
1000        }
1001
1002        set_eof(sd->fd); // forced to disconnect for the change
1003        return 0;
1004}
1005
1006//Disconnect the player out of the game, simple packet
1007//packet.w AID.L WHY.B 2+4+1 = 7byte
1008int chrif_disconnectplayer(int fd)
1009{
1010        struct map_session_data* sd;
1011
1012        sd = map_id2sd(RFIFOL(fd, 2));
1013        if(sd == NULL)
1014                return -1;
1015
1016        if (!sd->fd)
1017        {       //No connection
1018                if (sd->state.autotrade)
1019                        map_quit(sd); //Remove it.
1020                //Else we don't remove it because the char should have a timer to remove the player because it force-quit before,
1021                //and we don't want them kicking their previous instance before the 10 secs penalty time passes. [Skotlex]
1022                return 0;
1023        }
1024
1025        switch(RFIFOB(fd, 6))
1026        {
1027                case 1: clif_authfail_fd(sd->fd, 1); break; //server closed
1028                case 2: clif_authfail_fd(sd->fd, 2); break; //someone else logged in
1029                case 3: clif_authfail_fd(sd->fd, 4); break; //server overpopulated
1030                case 4: clif_authfail_fd(sd->fd, 10); break; //out of available time paid for
1031                case 5: clif_authfail_fd(sd->fd, 15); break; //forced to dc by gm
1032        }
1033        return 0;
1034}
1035
1036/*==========================================
1037 * Request to reload GM accounts and their levels: send to char-server by [Yor]
1038 *------------------------------------------*/
1039int chrif_reloadGMdb(void)
1040{
1041        chrif_check(-1);
1042
1043        WFIFOHEAD(char_fd,2);
1044        WFIFOW(char_fd,0) = 0x2af7;
1045        WFIFOSET(char_fd,2);
1046
1047        return 0;
1048}
1049
1050/*==========================================
1051 * Receiving GM accounts and their levels from char-server by [Yor]
1052 *------------------------------------------*/
1053int chrif_recvgmaccounts(int fd)
1054{
1055        int nAccounts = pc_read_gm_account(fd);
1056        ShowInfo("From login-server: receiving information of '"CL_WHITE"%d"CL_RESET"' GM accounts.\n", nAccounts);
1057        return 0;
1058}
1059
1060/*==========================================
1061 * Request/Receive top 10 Fame character list
1062 *------------------------------------------*/
1063
1064int chrif_updatefamelist(struct map_session_data* sd)
1065{
1066        char type;
1067        chrif_check(-1);
1068
1069        switch(sd->class_ & MAPID_UPPERMASK)
1070        {
1071                case MAPID_BLACKSMITH: type = 1; break;
1072                case MAPID_ALCHEMIST:  type = 2; break;
1073                case MAPID_TAEKWON:    type = 3; break;
1074                default:
1075                        return 0;
1076        }
1077
1078        WFIFOHEAD(char_fd, 11);
1079        WFIFOW(char_fd,0) = 0x2b10;
1080        WFIFOL(char_fd,2) = sd->status.char_id;
1081        WFIFOL(char_fd,6) = sd->status.fame;
1082        WFIFOB(char_fd,10) = type;
1083        WFIFOSET(char_fd,11);
1084
1085        return 0;
1086}
1087
1088int chrif_buildfamelist(void)
1089{
1090        chrif_check(-1);
1091
1092        WFIFOHEAD(char_fd,2);
1093        WFIFOW(char_fd,0) = 0x2b1a;
1094        WFIFOSET(char_fd,2);
1095
1096        return 0;
1097}
1098
1099int chrif_recvfamelist(int fd)
1100{
1101        int num, size;
1102        int total = 0, len = 8;
1103
1104        memset (smith_fame_list, 0, sizeof(smith_fame_list));
1105        memset (chemist_fame_list, 0, sizeof(chemist_fame_list));
1106        memset (taekwon_fame_list, 0, sizeof(taekwon_fame_list));
1107
1108        size = RFIFOW(fd, 6); //Blacksmith block size
1109        for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
1110                memcpy(&smith_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
1111                len += sizeof(struct fame_list);
1112        }
1113        total += num;
1114
1115        size = RFIFOW(fd, 4); //Alchemist block size
1116        for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
1117                memcpy(&chemist_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
1118                len += sizeof(struct fame_list);
1119        }
1120        total += num;
1121
1122        size = RFIFOW(fd, 2); //Total packet length
1123        for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
1124                memcpy(&taekwon_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
1125                len += sizeof(struct fame_list);
1126        }
1127        total += num;
1128
1129        ShowInfo("Received Fame List of '"CL_WHITE"%d"CL_RESET"' characters.\n", total);
1130
1131        return 0;
1132}
1133
1134/// fame ranking update confirmation
1135/// R 2b22 <table>.B <index>.B <value>.L
1136int chrif_updatefamelist_ack(int fd)
1137{
1138        struct fame_list* list;
1139        uint8 index;
1140        switch (RFIFOB(fd,2))
1141        {
1142                case 1: list = smith_fame_list;   break;
1143                case 2: list = chemist_fame_list; break;
1144                case 3: list = taekwon_fame_list; break;
1145                default: return 0;
1146        }
1147        index = RFIFOB(fd, 3);
1148        if (index >= MAX_FAME_LIST)
1149                return 0;
1150        list[index].fame = RFIFOL(fd,4);
1151        return 1;
1152}
1153
1154int chrif_save_scdata(struct map_session_data *sd)
1155{       //parses the sc_data of the player and sends it to the char-server for saving. [Skotlex]
1156#ifdef ENABLE_SC_SAVING
1157        int i, count=0;
1158        unsigned int tick;
1159        struct status_change_data data;
1160        struct status_change *sc = &sd->sc;
1161        const struct TimerData *timer;
1162
1163        chrif_check(-1);
1164        tick = gettick();
1165       
1166        WFIFOHEAD(char_fd, 14 + SC_MAX*sizeof(struct status_change_data));
1167        WFIFOW(char_fd,0) = 0x2b1c;
1168        WFIFOL(char_fd,4) = sd->status.account_id;
1169        WFIFOL(char_fd,8) = sd->status.char_id;
1170        for (i = 0; i < SC_MAX; i++)
1171        {
1172                if (!sc->data[i])
1173                        continue;
1174                if (sc->data[i]->timer != -1)
1175                {
1176                        timer = get_timer(sc->data[i]->timer);
1177                        if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
1178                                continue;
1179                        data.tick = DIFF_TICK(timer->tick,tick); //Duration that is left before ending.
1180                } else
1181                        data.tick = -1; //Infinite duration
1182                data.type = i;
1183                data.val1 = sc->data[i]->val1;
1184                data.val2 = sc->data[i]->val2;
1185                data.val3 = sc->data[i]->val3;
1186                data.val4 = sc->data[i]->val4;
1187                memcpy(WFIFOP(char_fd,14 +count*sizeof(struct status_change_data)),
1188                        &data, sizeof(struct status_change_data));
1189                count++;
1190        }
1191        if (count == 0)
1192                return 0; //Nothing to save.
1193        WFIFOW(char_fd,12) = count;
1194        WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
1195        WFIFOSET(char_fd,WFIFOW(char_fd,2));
1196#endif
1197        return 0;
1198}
1199
1200//Retrieve and load sc_data for a player. [Skotlex]
1201int chrif_load_scdata(int fd)
1202{       
1203#ifdef ENABLE_SC_SAVING
1204        struct map_session_data *sd;
1205        struct status_change_data *data;
1206        int aid, cid, i, count;
1207
1208        aid = RFIFOL(fd,4); //Player Account ID
1209        cid = RFIFOL(fd,8); //Player Char ID
1210       
1211        sd = map_id2sd(aid);
1212        if (!sd)
1213        {
1214                ShowError("chrif_load_scdata: Player of AID %d not found!\n", aid);
1215                return -1;
1216        }
1217        if (sd->status.char_id != cid)
1218        {
1219                ShowError("chrif_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid);
1220                return -1;
1221        }
1222        count = RFIFOW(fd,12); //sc_count
1223        for (i = 0; i < count; i++)
1224        {
1225                data = (struct status_change_data*)RFIFOP(fd,14 + i*sizeof(struct status_change_data));
1226                status_change_start(&sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 15);
1227        }
1228#endif
1229        return 0;
1230}
1231
1232/*==========================================
1233 * Send rates and motd to char server [Wizputer]
1234 * S 2b16 <base rate>.w <job rate>.w <drop rate>.w <motd len>.w <motd>.256B
1235 *------------------------------------------*/
1236 int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate)
1237{
1238        char buf[256];
1239        FILE *fp;
1240        int i;
1241
1242        chrif_check(-1);
1243
1244        WFIFOHEAD(char_fd, sizeof(buf) + 10);
1245        WFIFOW(char_fd,0) = 0x2b16;
1246        WFIFOW(char_fd,2) = base_rate;
1247        WFIFOW(char_fd,4) = job_rate;
1248        WFIFOW(char_fd,6) = drop_rate;
1249        WFIFOW(char_fd,8) = sizeof(buf) + 10;
1250
1251        if ((fp = fopen(motd_txt, "r")) != NULL) {
1252                if (fgets(buf, sizeof(buf), fp) != NULL)
1253                {
1254                        for(i = 0; buf[i]; i++) {
1255                                if (buf[i] == '\r' || buf[i] == '\n') {
1256                                        buf[i] = 0;
1257                                        break;
1258                                }
1259                        }
1260                        memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
1261                }
1262                fclose(fp);
1263        } else {
1264                memset(buf, 0, sizeof(buf)); //No data found, send empty packets?
1265                memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
1266        }
1267        WFIFOSET(char_fd,WFIFOW(char_fd,8));
1268        return 0;
1269}
1270
1271
1272/*=========================================
1273 * Tell char-server charcter disconnected [Wizputer]
1274 *-----------------------------------------*/
1275int chrif_char_offline(struct map_session_data *sd)
1276{
1277        chrif_check(-1);
1278
1279        WFIFOHEAD(char_fd,10);
1280        WFIFOW(char_fd,0) = 0x2b17;
1281        WFIFOL(char_fd,2) = sd->status.char_id;
1282        WFIFOL(char_fd,6) = sd->status.account_id;
1283        WFIFOSET(char_fd,10);
1284
1285        return 0;
1286}
1287int chrif_char_offline_nsd(int account_id, int char_id)
1288{
1289        chrif_check(-1);
1290
1291        WFIFOHEAD(char_fd,10);
1292        WFIFOW(char_fd,0) = 0x2b17;
1293        WFIFOL(char_fd,2) = char_id;
1294        WFIFOL(char_fd,6) = account_id;
1295        WFIFOSET(char_fd,10);
1296
1297        return 0;
1298}
1299
1300/*=========================================
1301 * Tell char-server to reset all chars offline [Wizputer]
1302 *-----------------------------------------*/
1303int chrif_flush_fifo(void)
1304{
1305        chrif_check(-1);
1306
1307        set_nonblocking(char_fd, 0);
1308        flush_fifos();
1309        set_nonblocking(char_fd, 1);
1310
1311        return 0;
1312}
1313
1314/*=========================================
1315 * Tell char-server to reset all chars offline [Wizputer]
1316 *-----------------------------------------*/
1317int chrif_char_reset_offline(void)
1318{
1319        chrif_check(-1);
1320
1321        WFIFOHEAD(char_fd,2);
1322        WFIFOW(char_fd,0) = 0x2b18;
1323        WFIFOSET(char_fd,2);
1324
1325        return 0;
1326}
1327
1328/*=========================================
1329 * Tell char-server charcter is online [Wizputer]
1330 *-----------------------------------------*/
1331
1332int chrif_char_online(struct map_session_data *sd)
1333{
1334        chrif_check(-1);
1335
1336        WFIFOHEAD(char_fd,10);
1337        WFIFOW(char_fd,0) = 0x2b19;
1338        WFIFOL(char_fd,2) = sd->status.char_id;
1339        WFIFOL(char_fd,6) = sd->status.account_id;
1340        WFIFOSET(char_fd,10);
1341
1342        return 0;
1343}
1344
1345int chrif_disconnect(int fd)
1346{
1347        if(fd == char_fd) {
1348                char_fd = 0;
1349                ShowWarning("Map Server disconnected from Char Server.\n\n");
1350                chrif_connected = 0;
1351               
1352                other_mapserver_count=0; //Reset counter. We receive ALL maps from all map-servers on reconnect.
1353                map_eraseallipport();
1354
1355                //Attempt to reconnect in a second. [Skotlex]
1356                add_timer(gettick() + 1000, check_connect_char_server, 0, 0);
1357        }
1358        return 0;
1359}
1360
1361void chrif_update_ip(int fd)
1362{
1363        uint32 new_ip;
1364        WFIFOHEAD(fd,6);
1365        new_ip = host2ip(char_ip_str);
1366        if (new_ip && new_ip != char_ip)
1367                char_ip = new_ip; //Update char_ip
1368
1369        new_ip = clif_refresh_ip();
1370        if (!new_ip) return; //No change
1371        WFIFOW(fd,0) = 0x2736;
1372        WFIFOL(fd,2) = htonl(new_ip);
1373        WFIFOSET(fd,6);
1374}
1375
1376// pings the charserver
1377void chrif_keepalive(int fd)
1378{
1379        WFIFOHEAD(fd,2);
1380        WFIFOW(fd,0) = 0x2b23;
1381        WFIFOSET(fd,2);
1382}
1383
1384void chrif_keepalive_ack(int fd)
1385{
1386}
1387
1388/*==========================================
1389 *
1390 *------------------------------------------*/
1391int chrif_parse(int fd)
1392{
1393        int packet_len, cmd;
1394
1395        // only process data from the char-server
1396        if (fd != char_fd)
1397        {
1398                ShowDebug("chrif_parse: Disconnecting invalid session #%d (is not the char-server)\n", fd);
1399                do_close(fd);
1400                return 0;
1401        }
1402
1403        if (session[fd]->flag.eof)
1404        {
1405                if (chrif_connected == 1)
1406                        chrif_disconnect(fd);
1407
1408                do_close(fd);
1409                return 0;
1410        }
1411
1412        while (RFIFOREST(fd) >= 2)
1413        {
1414                cmd = RFIFOW(fd,0);
1415                if (cmd < 0x2af8 || cmd >= 0x2af8 + ARRAYLENGTH(packet_len_table) || packet_len_table[cmd-0x2af8] == 0)
1416                {
1417                        int r = intif_parse(fd); // intif‚É“n‚·
1418
1419                        if (r == 1) continue;   // intif‚ŏˆ—‚µ‚œ
1420                        if (r == 2) return 0;   // intif‚ŏˆ—‚µ‚œ‚ªAƒf[ƒ^‚ª‘«‚è‚È‚¢
1421
1422                        ShowWarning("chrif_parse: session #%d, intif_parse failed (unrecognized command 0x%.4x).\n", fd, cmd);
1423                        set_eof(fd);
1424                        return 0;
1425                }
1426
1427                packet_len = packet_len_table[cmd-0x2af8];
1428                if (packet_len == -1)
1429                { // dynamic-length packet, second WORD holds the length
1430                        if (RFIFOREST(fd) < 4)
1431                                return 0;
1432                        packet_len = RFIFOW(fd,2);
1433                }
1434
1435                if ((int)RFIFOREST(fd) < packet_len)
1436                        return 0;
1437
1438                //ShowDebug("Received packet 0x%4x (%d bytes) from char-server (connection %d)\n", RFIFOW(fd,0), packet_len, fd);
1439
1440                switch(cmd)
1441                {
1442                case 0x2af9: chrif_connectack(fd); break;
1443                case 0x2afb: chrif_sendmapack(fd); break;
1444                case 0x2afd: chrif_authok(fd); break;
1445                case 0x2b00: map_setusers(RFIFOL(fd,2)); chrif_keepalive(fd); break;
1446                case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break;
1447                case 0x2b04: chrif_recvmap(fd); break;
1448                case 0x2b06: chrif_changemapserverack(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOW(fd,18), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28)); break;
1449                case 0x2b09: map_addnickdb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break;
1450                case 0x2b0d: chrif_changedsex(fd); break;
1451                case 0x2b0f: chrif_char_ask_name_answer(RFIFOL(fd,2), (char*)RFIFOP(fd,6), RFIFOW(fd,30), RFIFOW(fd,32)); break;
1452                case 0x2b12: chrif_divorceack(RFIFOL(fd,2), RFIFOL(fd,6)); break;
1453                case 0x2b13: chrif_accountdeletion(fd); break;
1454                case 0x2b14: chrif_accountban(fd); break;
1455                case 0x2b15: chrif_recvgmaccounts(fd); break;
1456                case 0x2b1b: chrif_recvfamelist(fd); break;
1457                case 0x2b1d: chrif_load_scdata(fd); break;
1458                case 0x2b1e: chrif_update_ip(fd); break;
1459                case 0x2b1f: chrif_disconnectplayer(fd); break;
1460                case 0x2b20: chrif_removemap(fd); break;
1461                case 0x2b21: chrif_save_ack(fd); break;
1462                case 0x2b22: chrif_updatefamelist_ack(fd); break;
1463                case 0x2b24: chrif_keepalive_ack(fd); break;
1464                case 0x2b25: chrif_deadopt(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
1465                case 0x2b27: chrif_authfail(fd); break;
1466                default:
1467                        ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd);
1468                        set_eof(fd);
1469                        return 0;
1470                }
1471                if (fd == char_fd) //There's the slight chance we lost the connection during parse, in which case this would segfault if not checked [Skotlex]
1472                        RFIFOSKIP(fd, packet_len);
1473        }
1474
1475        return 0;
1476}
1477
1478int ping_char_server(int tid, unsigned int tick, int id, intptr data)
1479{
1480        chrif_check(-1);
1481        chrif_keepalive(char_fd);
1482        return 0;
1483}
1484
1485// unused
1486int send_usercount_tochar(int tid, unsigned int tick, int id, intptr data)
1487{
1488        chrif_check(-1);
1489
1490        WFIFOHEAD(char_fd,4);
1491        WFIFOW(char_fd,0) = 0x2afe;
1492        WFIFOW(char_fd,2) = map_usercount();
1493        WFIFOSET(char_fd,4);
1494        return 0;
1495}
1496
1497/*==========================================
1498 * timerŠÖ”
1499 * ¡‚±‚ÌmapŽI‚ÉŒq‚ª‚Á‚Ä‚¢‚éƒNƒ‰ƒCƒAƒ“ƒgl”‚ðcharŽI‚Ö‘—‚é
1500 *------------------------------------------*/
1501int send_users_tochar(void)
1502{
1503        int users = 0, i = 0;
1504        struct map_session_data* sd;
1505        struct s_mapiterator* iter;
1506
1507        chrif_check(-1);
1508
1509        // get user count (TODO: improve this)
1510        iter = mapit_getallusers();
1511        for( mapit_first(iter); mapit_exists(iter); mapit_next(iter) )
1512                users++;
1513        mapit_free(iter);
1514
1515        // build the packet
1516        WFIFOHEAD(char_fd, 6+8*users);
1517        WFIFOW(char_fd,0) = 0x2aff;
1518        iter = mapit_getallusers();
1519        for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
1520        {
1521                WFIFOL(char_fd,6+8*i) = sd->status.account_id;
1522                WFIFOL(char_fd,6+8*i+4) = sd->status.char_id;
1523                i++;
1524        }
1525        mapit_free(iter);
1526        WFIFOW(char_fd,2) = 6 + 8*users;
1527        WFIFOW(char_fd,4) = users;
1528        WFIFOSET(char_fd, 6+8*users);
1529
1530        return 0;
1531}
1532
1533/*==========================================
1534 * timerŠÖ”
1535 * charŽI‚Ƃ̐ڑ±‚ðŠm”F‚µA‚à‚µØ‚ê‚Ä‚¢‚œ‚çÄ“xÚ‘±‚·‚é
1536 *------------------------------------------*/
1537int check_connect_char_server(int tid, unsigned int tick, int id, intptr data)
1538{
1539        static int displayed = 0;
1540        if (char_fd <= 0 || session[char_fd] == NULL)
1541        {
1542                if (!displayed)
1543                {
1544                        ShowStatus("Attempting to connect to Char Server. Please wait.\n");
1545                        displayed = 1;
1546                }
1547
1548                chrif_state = 0;
1549                char_fd = make_connection(char_ip, char_port);
1550                if (char_fd == -1)
1551                {       //Attempt to connect later. [Skotlex]
1552                        char_fd = 0;
1553                        return 0;
1554                }
1555
1556                session[char_fd]->func_parse = chrif_parse;
1557                session[char_fd]->flag.server = 1;
1558                realloc_fifo(char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
1559
1560                chrif_connect(char_fd);
1561                chrif_connected = (chrif_state == 2);
1562#ifndef TXT_ONLY
1563                srvinfo = 0;
1564#endif /* not TXT_ONLY */
1565        } else {
1566#ifndef TXT_ONLY
1567                if (srvinfo == 0) {
1568                        chrif_ragsrvinfo(battle_config.base_exp_rate, battle_config.job_exp_rate, battle_config.item_rate_common);
1569                        srvinfo = 1;
1570                }
1571#endif /* not TXT_ONLY */
1572        }
1573        if (chrif_isconnected()) displayed = 0;
1574        return 0;
1575}
1576
1577int auth_db_final(DBKey k,void *d,va_list ap)
1578{
1579        struct auth_node *node=(struct auth_node*)d;
1580        if (node->char_dat)
1581                aFree(node->char_dat);
1582        if (node->sd)
1583                aFree(node->sd);
1584        ers_free(auth_db_ers, node);
1585        return 0;
1586}
1587
1588/*==========================================
1589 * I—¹
1590 *------------------------------------------*/
1591int do_final_chrif(void)
1592{
1593        if (char_fd > 0)
1594                do_close(char_fd);
1595
1596        auth_db->destroy(auth_db, auth_db_final);
1597        ers_destroy(auth_db_ers);
1598        return 0;
1599}
1600
1601/*==========================================
1602 *
1603 *------------------------------------------*/
1604int do_init_chrif(void)
1605{
1606        auth_db = idb_alloc(DB_OPT_BASE);
1607        auth_db_ers = ers_new(sizeof(struct auth_node));
1608
1609        add_timer_func_list(check_connect_char_server, "check_connect_char_server");
1610        add_timer_func_list(ping_char_server, "ping_char_server");
1611        add_timer_func_list(auth_db_cleanup, "auth_db_cleanup");
1612
1613        // establish map-char connection if not present
1614        add_timer_interval(gettick() + 1000, check_connect_char_server, 0, 0, 10 * 1000);
1615
1616        // keep the map-char connection alive
1617        add_timer_interval(gettick() + 1000, ping_char_server, 0, 0, ((int)stall_time-2) * 1000);
1618
1619        // wipe stale data for timed-out client connection requests
1620        add_timer_interval(gettick() + 1000, auth_db_cleanup, 0, 0, 30 * 1000);
1621
1622        // send the user count every 10 seconds, to hide the charserver's online counting problem
1623        add_timer_interval(gettick() + 1000, send_usercount_tochar, 0, 0, UPDATE_INTERVAL);
1624
1625        return 0;
1626}
Note: See TracBrowser for help on using the browser.