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