Guest User

Untitled

a guest
May 11th, 2017
673
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 211.64 KB | None | 0 0
  1. /**
  2. * This file is part of Hercules.
  3. * http://herc.ws - http://github.com/HerculesWS/Hercules
  4. *
  5. * Copyright (C) 2012-2015 Hercules Dev Team
  6. * Copyright (C) Athena Dev Teams
  7. *
  8. * Hercules is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #define HERCULES_CORE
  22.  
  23. #include "config/core.h" // CONSOLE_INPUT
  24. #include "char.h"
  25.  
  26. #include "char/HPMchar.h"
  27. #include "char/geoip.h"
  28. #include "char/int_auction.h"
  29. #include "char/int_elemental.h"
  30. #include "char/int_guild.h"
  31. #include "char/int_homun.h"
  32. #include "char/int_mail.h"
  33. #include "char/int_mercenary.h"
  34. #include "char/int_party.h"
  35. #include "char/int_pet.h"
  36. #include "char/int_quest.h"
  37. #include "char/int_storage.h"
  38. #include "char/inter.h"
  39. #include "char/loginif.h"
  40. #include "char/mapif.h"
  41. #include "char/pincode.h"
  42.  
  43. #include "common/HPM.h"
  44. #include "common/cbasetypes.h"
  45. #include "common/console.h"
  46. #include "common/core.h"
  47. #include "common/db.h"
  48. #include "common/memmgr.h"
  49. #include "common/mapindex.h"
  50. #include "common/mmo.h"
  51. #include "common/nullpo.h"
  52. #include "common/showmsg.h"
  53. #include "common/socket.h"
  54. #include "common/strlib.h"
  55. #include "common/sql.h"
  56. #include "common/timer.h"
  57. #include "common/utils.h"
  58.  
  59. #include <signal.h>
  60. #include <stdarg.h>
  61. #include <stdio.h>
  62. #include <stdlib.h>
  63. #include <sys/types.h>
  64. #ifndef WIN32
  65. # include <unistd.h>
  66. #endif
  67.  
  68. // private declarations
  69. char char_db[256] = "char";
  70. char scdata_db[256] = "sc_data";
  71. char cart_db[256] = "cart_inventory";
  72. char inventory_db[256] = "inventory";
  73. char charlog_db[256] = "charlog";
  74. char storage_db[256] = "storage";
  75. char interlog_db[256] = "interlog";
  76. char skill_db[256] = "skill";
  77. char memo_db[256] = "memo";
  78. char guild_db[256] = "guild";
  79. char guild_alliance_db[256] = "guild_alliance";
  80. char guild_castle_db[256] = "guild_castle";
  81. char guild_expulsion_db[256] = "guild_expulsion";
  82. char guild_member_db[256] = "guild_member";
  83. char guild_position_db[256] = "guild_position";
  84. char guild_skill_db[256] = "guild_skill";
  85. char guild_storage_db[256] = "guild_storage";
  86. char party_db[256] = "party";
  87. char pet_db[256] = "pet";
  88. char mail_db[256] = "mail"; // MAIL SYSTEM
  89. char auction_db[256] = "auction"; // Auctions System
  90. char friend_db[256] = "friends";
  91. char hotkey_db[256] = "hotkey";
  92. char quest_db[256] = "quest";
  93. char homunculus_db[256] = "homunculus";
  94. char skill_homunculus_db[256] = "skill_homunculus";
  95. char mercenary_db[256] = "mercenary";
  96. char mercenary_owner_db[256] = "mercenary_owner";
  97. char ragsrvinfo_db[256] = "ragsrvinfo";
  98. char elemental_db[256] = "elemental";
  99. char account_data_db[256] = "account_data";
  100. char acc_reg_num_db[32] = "acc_reg_num_db";
  101. char acc_reg_str_db[32] = "acc_reg_str_db";
  102. char char_reg_str_db[32] = "char_reg_str_db";
  103. char char_reg_num_db[32] = "char_reg_num_db";
  104.  
  105. struct char_interface char_s;
  106. struct char_interface *chr;
  107.  
  108. // show loading/saving messages
  109. int save_log = 1;
  110.  
  111. char db_path[1024] = "db";
  112.  
  113. char wisp_server_name[NAME_LENGTH] = "Server";
  114. char login_ip_str[128];
  115. uint32 login_ip = 0;
  116. uint16 login_port = 6900;
  117. char char_ip_str[128];
  118. char bind_ip_str[128];
  119. uint32 bind_ip = INADDR_ANY;
  120. int char_maintenance_min_group_id = 0;
  121. bool char_new = true;
  122.  
  123. bool name_ignoring_case = false; // Allow or not identical name for characters but with a different case by [Yor]
  124. int char_name_option = 0; // Option to know which letters/symbols are authorized 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]
  125. char unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the requested name cannot be determined
  126. #define TRIM_CHARS "\255\xA0\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex]
  127. char char_name_letters[1024] = ""; // list of letters/symbols allowed (or not) in a character name. by [Yor]
  128.  
  129. int char_del_level = 0; //From which level u can delete character [Lupus]
  130. int char_del_delay = 86400;
  131.  
  132. int log_char = 1; // logging char or not [devil]
  133. int log_inter = 1; // logging inter or not [devil]
  134.  
  135. int char_aegis_delete = 0; // Verify if char is in guild/party or char and reacts as Aegis does (doesn't allow deletion), see chr->delete2_req for more information
  136.  
  137. int max_connect_user = -1;
  138. int gm_allow_group = -1;
  139. int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
  140. int start_zeny = 0;
  141. int start_items[MAX_START_ITEMS*4];
  142. int guild_exp_rate = 100;
  143.  
  144. //Custom limits for the fame lists. [Skotlex]
  145. int fame_list_size_chemist = MAX_FAME_LIST;
  146. int fame_list_size_smith = MAX_FAME_LIST;
  147. int fame_list_size_taekwon = MAX_FAME_LIST;
  148.  
  149. // Char-server-side stored fame lists [DracoRPG]
  150. struct fame_list smith_fame_list[MAX_FAME_LIST];
  151. struct fame_list chemist_fame_list[MAX_FAME_LIST];
  152. struct fame_list taekwon_fame_list[MAX_FAME_LIST];
  153.  
  154. // Initial position (it's possible to set it in conf file)
  155. #ifdef RENEWAL
  156. struct point start_point = { 0, 97, 90 };
  157. #else
  158. struct point start_point = { 0, 53, 111 };
  159. #endif
  160.  
  161. unsigned short skillid2idx[MAX_SKILL_ID];
  162.  
  163. //-----------------------------------------------------
  164. // Auth database
  165. //-----------------------------------------------------
  166. #define AUTH_TIMEOUT 30000
  167.  
  168. static DBMap* auth_db; // int account_id -> struct char_auth_node*
  169.  
  170. //-----------------------------------------------------
  171. // Online User Database
  172. //-----------------------------------------------------
  173.  
  174. /**
  175. * @see DBCreateData
  176. */
  177. static DBData char_create_online_char_data(DBKey key, va_list args)
  178. {
  179. struct online_char_data* character;
  180. CREATE(character, struct online_char_data, 1);
  181. character->account_id = key.i;
  182. character->char_id = -1;
  183. character->server = -1;
  184. character->pincode_enable = -1;
  185. character->fd = -1;
  186. character->waiting_disconnect = INVALID_TIMER;
  187. return DB->ptr2data(character);
  188. }
  189.  
  190. void char_set_account_online(int account_id)
  191. {
  192. WFIFOHEAD(chr->login_fd,6);
  193. WFIFOW(chr->login_fd,0) = 0x272b;
  194. WFIFOL(chr->login_fd,2) = account_id;
  195. WFIFOSET(chr->login_fd,6);
  196. }
  197.  
  198. void char_set_account_offline(int account_id)
  199. {
  200. WFIFOHEAD(chr->login_fd,6);
  201. WFIFOW(chr->login_fd,0) = 0x272c;
  202. WFIFOL(chr->login_fd,2) = account_id;
  203. WFIFOSET(chr->login_fd,6);
  204. }
  205.  
  206. void char_set_char_charselect(int account_id)
  207. {
  208. struct online_char_data* character;
  209.  
  210. character = (struct online_char_data*)idb_ensure(chr->online_char_db, account_id, chr->create_online_char_data);
  211.  
  212. if( character->server > -1 )
  213. if( chr->server[character->server].users > 0 ) // Prevent this value from going negative.
  214. chr->server[character->server].users--;
  215.  
  216. character->char_id = -1;
  217. character->server = -1;
  218. if(character->pincode_enable == -1)
  219. character->pincode_enable = pincode->charselect + pincode->enabled;
  220.  
  221. if(character->waiting_disconnect != INVALID_TIMER) {
  222. timer->delete(character->waiting_disconnect, chr->waiting_disconnect);
  223. character->waiting_disconnect = INVALID_TIMER;
  224. }
  225.  
  226. if (chr->login_fd > 0 && !sockt->session[chr->login_fd]->flag.eof)
  227. chr->set_account_online(account_id);
  228. }
  229.  
  230. void char_set_char_online(int map_id, int char_id, int account_id)
  231. {
  232. struct online_char_data* character;
  233. struct mmo_charstatus *cp;
  234.  
  235. //Update DB
  236. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online`='1' WHERE `char_id`='%d' LIMIT 1", char_db, char_id) )
  237. Sql_ShowDebug(inter->sql_handle);
  238.  
  239. //Check to see for online conflicts
  240. character = (struct online_char_data*)idb_ensure(chr->online_char_db, account_id, chr->create_online_char_data);
  241. if( character->char_id != -1 && character->server > -1 && character->server != map_id )
  242. {
  243. ShowNotice("chr->set_char_online: Character %d:%d marked in map server %d, but map server %d claims to have (%d:%d) online!\n",
  244. character->account_id, character->char_id, character->server, map_id, account_id, char_id);
  245. mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); // 2: Already connected to server
  246. }
  247.  
  248. //Update state data
  249. character->char_id = char_id;
  250. character->server = map_id;
  251.  
  252. if( character->server > -1 )
  253. chr->server[character->server].users++;
  254.  
  255. //Get rid of disconnect timer
  256. if(character->waiting_disconnect != INVALID_TIMER) {
  257. timer->delete(character->waiting_disconnect, chr->waiting_disconnect);
  258. character->waiting_disconnect = INVALID_TIMER;
  259. }
  260.  
  261. //Set char online in guild cache. If char is in memory, use the guild id on it, otherwise seek it.
  262. cp = (struct mmo_charstatus*)idb_get(chr->char_db_,char_id);
  263. inter_guild->CharOnline(char_id, cp?cp->guild_id:-1);
  264.  
  265. //Notify login server
  266. if (chr->login_fd > 0 && !sockt->session[chr->login_fd]->flag.eof)
  267. chr->set_account_online(account_id);
  268. }
  269.  
  270. void char_set_char_offline(int char_id, int account_id)
  271. {
  272. struct online_char_data* character;
  273.  
  274. if ( char_id == -1 )
  275. {
  276. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online`='0' WHERE `account_id`='%d'", char_db, account_id) )
  277. Sql_ShowDebug(inter->sql_handle);
  278. }
  279. else
  280. {
  281. struct mmo_charstatus* cp = (struct mmo_charstatus*)idb_get(chr->char_db_,char_id);
  282. inter_guild->CharOffline(char_id, cp?cp->guild_id:-1);
  283. if (cp)
  284. idb_remove(chr->char_db_,char_id);
  285.  
  286. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online`='0' WHERE `char_id`='%d' LIMIT 1", char_db, char_id) )
  287. Sql_ShowDebug(inter->sql_handle);
  288. }
  289.  
  290. if ((character = (struct online_char_data*)idb_get(chr->online_char_db, account_id)) != NULL) {
  291. //We don't free yet to avoid aCalloc/aFree spamming during char change. [Skotlex]
  292. if( character->server > -1 )
  293. if( chr->server[character->server].users > 0 ) // Prevent this value from going negative.
  294. chr->server[character->server].users--;
  295.  
  296. if(character->waiting_disconnect != INVALID_TIMER){
  297. timer->delete(character->waiting_disconnect, chr->waiting_disconnect);
  298. character->waiting_disconnect = INVALID_TIMER;
  299. }
  300.  
  301. if(character->char_id == char_id)
  302. {
  303. character->char_id = -1;
  304. character->server = -1;
  305. character->pincode_enable = -1;
  306. }
  307.  
  308. //FIXME? Why Kevin free'd the online information when the char was effectively in the map-server?
  309. }
  310.  
  311. //Remove char if 1- Set all offline, or 2- character is no longer connected to char-server.
  312. if (chr->login_fd > 0 && !sockt->session[chr->login_fd]->flag.eof && (char_id == -1 || character == NULL || character->fd == -1))
  313. chr->set_account_offline(account_id);
  314. }
  315.  
  316. /**
  317. * @see DBApply
  318. */
  319. static int char_db_setoffline(DBKey key, DBData *data, va_list ap)
  320. {
  321. struct online_char_data* character = (struct online_char_data*)DB->data2ptr(data);
  322. int server_id = va_arg(ap, int);
  323. nullpo_ret(character);
  324. if (server_id == -1) {
  325. character->char_id = -1;
  326. character->server = -1;
  327. if(character->waiting_disconnect != INVALID_TIMER){
  328. timer->delete(character->waiting_disconnect, chr->waiting_disconnect);
  329. character->waiting_disconnect = INVALID_TIMER;
  330. }
  331. } else if (character->server == server_id)
  332. character->server = -2; //In some map server that we aren't connected to.
  333. return 0;
  334. }
  335.  
  336. /**
  337. * @see DBApply
  338. */
  339. static int char_db_kickoffline(DBKey key, DBData *data, va_list ap)
  340. {
  341. struct online_char_data* character = (struct online_char_data*)DB->data2ptr(data);
  342. int server_id = va_arg(ap, int);
  343. nullpo_ret(character);
  344.  
  345. if (server_id > -1 && character->server != server_id)
  346. return 0;
  347.  
  348. //Kick out any connected characters, and set them offline as appropriate.
  349. if (character->server > -1 && character->server < MAX_MAP_SERVERS)
  350. mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 1); // 1: Server closed
  351. else if (character->waiting_disconnect == INVALID_TIMER)
  352. chr->set_char_offline(character->char_id, character->account_id);
  353. else
  354. return 0; // fail
  355.  
  356. return 1;
  357. }
  358.  
  359. void char_set_login_all_offline(void)
  360. {
  361. //Tell login-server to also mark all our characters as offline.
  362. WFIFOHEAD(chr->login_fd,2);
  363. WFIFOW(chr->login_fd,0) = 0x2737;
  364. WFIFOSET(chr->login_fd,2);
  365. }
  366.  
  367. void char_set_all_offline(int id)
  368. {
  369. if (id < 0)
  370. ShowNotice("Sending all users offline.\n");
  371. else
  372. ShowNotice("Sending users of map-server %d offline.\n",id);
  373. chr->online_char_db->foreach(chr->online_char_db,chr->db_kickoffline,id);
  374.  
  375. if (id >= 0 || chr->login_fd <= 0 || sockt->session[chr->login_fd]->flag.eof)
  376. return;
  377. chr->set_login_all_offline();
  378. }
  379.  
  380. void char_set_all_offline_sql(void)
  381. {
  382. //Set all players to 'OFFLINE'
  383. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online` = '0'", char_db) )
  384. Sql_ShowDebug(inter->sql_handle);
  385. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online` = '0'", guild_member_db) )
  386. Sql_ShowDebug(inter->sql_handle);
  387. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `connect_member` = '0'", guild_db) )
  388. Sql_ShowDebug(inter->sql_handle);
  389. }
  390.  
  391. /**
  392. * @see DBCreateData
  393. */
  394. static DBData char_create_charstatus(DBKey key, va_list args)
  395. {
  396. struct mmo_charstatus *cp;
  397. cp = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus));
  398. cp->char_id = key.i;
  399. return DB->ptr2data(cp);
  400. }
  401.  
  402. int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p)
  403. {
  404. int i = 0;
  405. int count = 0;
  406. int diff = 0;
  407. char save_status[128]; //For displaying save information. [Skotlex]
  408. struct mmo_charstatus *cp;
  409. int errors = 0; //If there are any errors while saving, "cp" will not be updated at the end.
  410. StringBuf buf;
  411.  
  412. nullpo_ret(p);
  413. if (char_id != p->char_id) return 0;
  414.  
  415. cp = idb_ensure(chr->char_db_, char_id, chr->create_charstatus);
  416.  
  417. StrBuf->Init(&buf);
  418. memset(save_status, 0, sizeof(save_status));
  419.  
  420. //map inventory data
  421. if( memcmp(p->inventory, cp->inventory, sizeof(p->inventory)) ) {
  422. if (!chr->inventory_to_sql(p->inventory, MAX_INVENTORY, p->char_id))
  423. strcat(save_status, " inventory");
  424. else
  425. errors++;
  426. }
  427.  
  428. //map cart data
  429. if( memcmp(p->cart, cp->cart, sizeof(p->cart)) ) {
  430. if (!chr->memitemdata_to_sql(p->cart, MAX_CART, p->char_id, TABLE_CART))
  431. strcat(save_status, " cart");
  432. else
  433. errors++;
  434. }
  435.  
  436. //map storage data
  437. if( memcmp(p->storage.items, cp->storage.items, sizeof(p->storage.items)) ) {
  438. if (!chr->memitemdata_to_sql(p->storage.items, MAX_STORAGE, p->account_id, TABLE_STORAGE))
  439. strcat(save_status, " storage");
  440. else
  441. errors++;
  442. }
  443.  
  444. if (
  445. (p->base_exp != cp->base_exp) || (p->base_level != cp->base_level) ||
  446. (p->job_level != cp->job_level) || (p->job_exp != cp->job_exp) ||
  447. (p->zeny != cp->zeny) ||
  448. (p->last_point.map != cp->last_point.map) ||
  449. (p->last_point.x != cp->last_point.x) || (p->last_point.y != cp->last_point.y) ||
  450. (p->max_hp != cp->max_hp) || (p->hp != cp->hp) ||
  451. (p->max_sp != cp->max_sp) || (p->sp != cp->sp) ||
  452. (p->status_point != cp->status_point) || (p->skill_point != cp->skill_point) ||
  453. (p->str != cp->str) || (p->agi != cp->agi) || (p->vit != cp->vit) ||
  454. (p->int_ != cp->int_) || (p->dex != cp->dex) || (p->luk != cp->luk) ||
  455. (p->option != cp->option) ||
  456. (p->party_id != cp->party_id) || (p->guild_id != cp->guild_id) ||
  457. (p->pet_id != cp->pet_id) || (p->weapon != cp->weapon) || (p->hom_id != cp->hom_id) ||
  458. (p->ele_id != cp->ele_id) || (p->shield != cp->shield) || (p->head_top != cp->head_top) ||
  459. (p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) || (p->delete_date != cp->delete_date) ||
  460. (p->rename != cp->rename) || (p->slotchange != cp->slotchange) || (p->robe != cp->robe) ||
  461. (p->show_equip != cp->show_equip) || (p->allow_party != cp->allow_party) || (p->font != cp->font) ||
  462. (p->uniqueitem_counter != cp->uniqueitem_counter) || (p->hotkey_rowshift != cp->hotkey_rowshift)
  463. ) {
  464. //Save status
  465. unsigned int opt = 0;
  466.  
  467. if( p->allow_party )
  468. opt |= OPT_ALLOW_PARTY;
  469. if( p->show_equip )
  470. opt |= OPT_SHOW_EQUIP;
  471.  
  472. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d',"
  473. "`base_exp`='%u', `job_exp`='%u', `zeny`='%d',"
  474. "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
  475. "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
  476. "`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',`elemental_id`='%d',"
  477. "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
  478. "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d',"
  479. "`delete_date`='%lu',`robe`='%d',`slotchange`='%d', `char_opt`='%u', `font`='%u', `uniqueitem_counter` ='%u',"
  480. "`hotkey_rowshift`='%d'"
  481. " WHERE `account_id`='%d' AND `char_id` = '%d'",
  482. char_db, p->base_level, p->job_level,
  483. p->base_exp, p->job_exp, p->zeny,
  484. p->max_hp, p->hp, p->max_sp, p->sp, p->status_point, p->skill_point,
  485. p->str, p->agi, p->vit, p->int_, p->dex, p->luk,
  486. p->option, p->party_id, p->guild_id, p->pet_id, p->hom_id, p->ele_id,
  487. p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom,
  488. mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
  489. mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename,
  490. (unsigned long)p->delete_date, // FIXME: platform-dependent size
  491. p->robe,p->slotchange,opt,p->font,p->uniqueitem_counter,
  492. p->hotkey_rowshift,
  493. p->account_id, p->char_id) )
  494. {
  495. Sql_ShowDebug(inter->sql_handle);
  496. errors++;
  497. } else
  498. strcat(save_status, " status");
  499. }
  500.  
  501. if( p->bank_vault != cp->bank_vault || p->mod_exp != cp->mod_exp || p->mod_drop != cp->mod_drop || p->mod_death != cp->mod_death ) {
  502. if( SQL_ERROR == SQL->Query(inter->sql_handle, "REPLACE INTO `%s` (`account_id`,`bank_vault`,`base_exp`,`base_drop`,`base_death`) VALUES ('%d','%d','%d','%d','%d')",account_data_db,p->account_id,p->bank_vault,p->mod_exp,p->mod_drop,p->mod_death) ) {
  503. Sql_ShowDebug(inter->sql_handle);
  504. errors++;
  505. } else
  506. strcat(save_status, " accdata");
  507. }
  508.  
  509. //Values that will seldom change (to speed up saving)
  510. if (
  511. (p->hair != cp->hair) || (p->hair_color != cp->hair_color) ||
  512. (p->clothes_color != cp->clothes_color) || (p->body != cp->body) ||
  513. (p->class_ != cp->class_) ||
  514. (p->partner_id != cp->partner_id) || (p->father != cp->father) ||
  515. (p->mother != cp->mother) || (p->child != cp->child) ||
  516. (p->karma != cp->karma) || (p->manner != cp->manner) ||
  517. (p->fame != cp->fame)
  518. )
  519. {
  520. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `class`='%d',"
  521. "`hair`='%d', `hair_color`='%d', `clothes_color`='%d', `body`='%d',"
  522. "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d',"
  523. "`karma`='%d', `manner`='%d', `fame`='%d'"
  524. " WHERE `account_id`='%d' AND `char_id` = '%d'",
  525. char_db, p->class_,
  526. p->hair, p->hair_color, p->clothes_color, p->body,
  527. p->partner_id, p->father, p->mother, p->child,
  528. p->karma, p->manner, p->fame,
  529. p->account_id, p->char_id) )
  530. {
  531. Sql_ShowDebug(inter->sql_handle);
  532. errors++;
  533. } else
  534. strcat(save_status, " status2");
  535. }
  536.  
  537. /* Mercenary Owner */
  538. if( (p->mer_id != cp->mer_id) ||
  539. (p->arch_calls != cp->arch_calls) || (p->arch_faith != cp->arch_faith) ||
  540. (p->spear_calls != cp->spear_calls) || (p->spear_faith != cp->spear_faith) ||
  541. (p->sword_calls != cp->sword_calls) || (p->sword_faith != cp->sword_faith) )
  542. {
  543. if (inter_mercenary->owner_tosql(char_id, p))
  544. strcat(save_status, " mercenary");
  545. else
  546. errors++;
  547. }
  548.  
  549. //memo points
  550. if( memcmp(p->memo_point, cp->memo_point, sizeof(p->memo_point)) )
  551. {
  552. char esc_mapname[NAME_LENGTH*2+1];
  553.  
  554. //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`)
  555. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", memo_db, p->char_id) )
  556. {
  557. Sql_ShowDebug(inter->sql_handle);
  558. errors++;
  559. }
  560.  
  561. //insert here.
  562. StrBuf->Clear(&buf);
  563. StrBuf->Printf(&buf, "INSERT INTO `%s`(`char_id`,`map`,`x`,`y`) VALUES ", memo_db);
  564. for( i = 0, count = 0; i < MAX_MEMOPOINTS; ++i )
  565. {
  566. if( p->memo_point[i].map )
  567. {
  568. if( count )
  569. StrBuf->AppendStr(&buf, ",");
  570. SQL->EscapeString(inter->sql_handle, esc_mapname, mapindex_id2name(p->memo_point[i].map));
  571. StrBuf->Printf(&buf, "('%d', '%s', '%d', '%d')", char_id, esc_mapname, p->memo_point[i].x, p->memo_point[i].y);
  572. ++count;
  573. }
  574. }
  575. if( count )
  576. {
  577. if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) )
  578. {
  579. Sql_ShowDebug(inter->sql_handle);
  580. errors++;
  581. }
  582. }
  583. strcat(save_status, " memo");
  584. }
  585.  
  586. //skills
  587. if( memcmp(p->skill, cp->skill, sizeof(p->skill)) ) {
  588. //`skill` (`char_id`, `id`, `lv`)
  589. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", skill_db, p->char_id) ) {
  590. Sql_ShowDebug(inter->sql_handle);
  591. errors++;
  592. }
  593.  
  594. StrBuf->Clear(&buf);
  595. StrBuf->Printf(&buf, "INSERT INTO `%s`(`char_id`,`id`,`lv`,`flag`) VALUES ", skill_db);
  596. //insert here.
  597. for( i = 0, count = 0; i < MAX_SKILL; ++i ) {
  598. if( p->skill[i].id != 0 && p->skill[i].flag != SKILL_FLAG_TEMPORARY ) {
  599. if( p->skill[i].lv == 0 && ( p->skill[i].flag == SKILL_FLAG_PERM_GRANTED || p->skill[i].flag == SKILL_FLAG_PERMANENT ) )
  600. continue;
  601. if( p->skill[i].flag != SKILL_FLAG_PERMANENT && p->skill[i].flag != SKILL_FLAG_PERM_GRANTED && (p->skill[i].flag - SKILL_FLAG_REPLACED_LV_0) == 0 )
  602. continue;
  603. if( count )
  604. StrBuf->AppendStr(&buf, ",");
  605. StrBuf->Printf(&buf, "('%d','%d','%d','%d')", char_id, p->skill[i].id,
  606. ( (p->skill[i].flag == SKILL_FLAG_PERMANENT || p->skill[i].flag == SKILL_FLAG_PERM_GRANTED) ? p->skill[i].lv : p->skill[i].flag - SKILL_FLAG_REPLACED_LV_0),
  607. p->skill[i].flag == SKILL_FLAG_PERM_GRANTED ? p->skill[i].flag : 0);/* other flags do not need to be saved */
  608. ++count;
  609. }
  610. }
  611. if( count )
  612. {
  613. if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) )
  614. {
  615. Sql_ShowDebug(inter->sql_handle);
  616. errors++;
  617. }
  618. }
  619.  
  620. strcat(save_status, " skills");
  621. }
  622.  
  623. diff = 0;
  624. for(i = 0; i < MAX_FRIENDS; i++){
  625. if(p->friends[i].char_id != cp->friends[i].char_id ||
  626. p->friends[i].account_id != cp->friends[i].account_id){
  627. diff = 1;
  628. break;
  629. }
  630. }
  631.  
  632. if(diff == 1) {
  633. //Save friends
  634. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", friend_db, char_id) )
  635. {
  636. Sql_ShowDebug(inter->sql_handle);
  637. errors++;
  638. }
  639.  
  640. StrBuf->Clear(&buf);
  641. StrBuf->Printf(&buf, "INSERT INTO `%s` (`char_id`, `friend_account`, `friend_id`) VALUES ", friend_db);
  642. for( i = 0, count = 0; i < MAX_FRIENDS; ++i )
  643. {
  644. if( p->friends[i].char_id > 0 )
  645. {
  646. if( count )
  647. StrBuf->AppendStr(&buf, ",");
  648. StrBuf->Printf(&buf, "('%d','%d','%d')", char_id, p->friends[i].account_id, p->friends[i].char_id);
  649. count++;
  650. }
  651. }
  652. if( count )
  653. {
  654. if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) )
  655. {
  656. Sql_ShowDebug(inter->sql_handle);
  657. errors++;
  658. }
  659. }
  660. strcat(save_status, " friends");
  661. }
  662.  
  663. #ifdef HOTKEY_SAVING
  664. // hotkeys
  665. StrBuf->Clear(&buf);
  666. StrBuf->Printf(&buf, "REPLACE INTO `%s` (`char_id`, `hotkey`, `type`, `itemskill_id`, `skill_lvl`) VALUES ", hotkey_db);
  667. diff = 0;
  668. for(i = 0; i < ARRAYLENGTH(p->hotkeys); i++){
  669. if(memcmp(&p->hotkeys[i], &cp->hotkeys[i], sizeof(struct hotkey)))
  670. {
  671. if( diff )
  672. StrBuf->AppendStr(&buf, ",");// not the first hotkey
  673. StrBuf->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);
  674. diff = 1;
  675. }
  676. }
  677. if(diff) {
  678. if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) )
  679. {
  680. Sql_ShowDebug(inter->sql_handle);
  681. errors++;
  682. } else
  683. strcat(save_status, " hotkeys");
  684. }
  685. #endif
  686.  
  687. StrBuf->Destroy(&buf);
  688. if (save_status[0]!='\0' && save_log)
  689. ShowInfo("Saved char %d - %s:%s.\n", char_id, p->name, save_status);
  690. if (!errors)
  691. memcpy(cp, p, sizeof(struct mmo_charstatus));
  692. return 0;
  693. }
  694.  
  695. /// Saves an array of 'item' entries into the specified table.
  696. int char_memitemdata_to_sql(const struct item items[], int max, int id, int tableswitch)
  697. {
  698. StringBuf buf;
  699. SqlStmt* stmt;
  700. int i;
  701. int j;
  702. const char* tablename;
  703. const char* selectoption;
  704. struct item item; // temp storage variable
  705. bool* flag; // bit array for inventory matching
  706. bool found;
  707. int errors = 0;
  708.  
  709. switch (tableswitch) {
  710. case TABLE_INVENTORY: tablename = inventory_db; selectoption = "char_id"; break;
  711. case TABLE_CART: tablename = cart_db; selectoption = "char_id"; break;
  712. case TABLE_STORAGE: tablename = storage_db; selectoption = "account_id"; break;
  713. case TABLE_GUILD_STORAGE: tablename = guild_storage_db; selectoption = "guild_id"; break;
  714. default:
  715. ShowError("Invalid table name!\n");
  716. return 1;
  717. }
  718.  
  719.  
  720. // The following code compares inventory with current database values
  721. // and performs modification/deletion/insertion only on relevant rows.
  722. // This approach is more complicated than a trivial delete&insert, but
  723. // it significantly reduces cpu load on the database server.
  724.  
  725. StrBuf->Init(&buf);
  726. StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`");
  727. for( j = 0; j < MAX_SLOTS; ++j )
  728. StrBuf->Printf(&buf, ", `card%d`", j);
  729. StrBuf->Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id);
  730.  
  731. stmt = SQL->StmtMalloc(inter->sql_handle);
  732. if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf))
  733. || SQL_ERROR == SQL->StmtExecute(stmt) )
  734. {
  735. SqlStmt_ShowDebug(stmt);
  736. SQL->StmtFree(stmt);
  737. StrBuf->Destroy(&buf);
  738. return 1;
  739. }
  740.  
  741. memset(&item, 0, sizeof(item));
  742. SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL);
  743. SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL);
  744. SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL);
  745. SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &item.equip, 0, NULL, NULL);
  746. SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &item.identify, 0, NULL, NULL);
  747. SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL);
  748. SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL);
  749. SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
  750. SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &item.bound, 0, NULL, NULL);
  751. for( j = 0; j < MAX_SLOTS; ++j )
  752. SQL->StmtBindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
  753.  
  754. // bit array indicating which inventory items have already been matched
  755. flag = (bool*) aCalloc(max, sizeof(bool));
  756.  
  757. while( SQL_SUCCESS == SQL->StmtNextRow(stmt) )
  758. {
  759. found = false;
  760. // search for the presence of the item in the char's inventory
  761. for( i = 0; i < max; ++i )
  762. {
  763. // skip empty and already matched entries
  764. if( items[i].nameid == 0 || flag[i] )
  765. continue;
  766.  
  767. if( items[i].nameid == item.nameid
  768. && items[i].card[0] == item.card[0]
  769. && items[i].card[2] == item.card[2]
  770. && items[i].card[3] == item.card[3]
  771. ) {
  772. //They are the same item.
  773. ARR_FIND( 0, MAX_SLOTS, j, items[i].card[j] != item.card[j] );
  774. if( j == MAX_SLOTS
  775. && items[i].amount == item.amount
  776. && items[i].equip == item.equip
  777. && items[i].identify == item.identify
  778. && items[i].refine == item.refine
  779. && items[i].attribute == item.attribute
  780. && items[i].expire_time == item.expire_time
  781. && items[i].bound == item.bound
  782. ) {
  783. ; //Do nothing.
  784. } else {
  785. // update all fields.
  786. StrBuf->Clear(&buf);
  787. StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `bound`='%d'",
  788. tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].bound);
  789. for( j = 0; j < MAX_SLOTS; ++j )
  790. StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
  791. StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);
  792.  
  793. if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) )
  794. {
  795. Sql_ShowDebug(inter->sql_handle);
  796. errors++;
  797. }
  798. }
  799.  
  800. found = flag[i] = true; //Item dealt with,
  801. break; //skip to next item in the db.
  802. }
  803. }
  804. if( !found )
  805. {// Item not present in inventory, remove it.
  806. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE from `%s` where `id`='%d' LIMIT 1", tablename, item.id) )
  807. {
  808. Sql_ShowDebug(inter->sql_handle);
  809. errors++;
  810. }
  811. }
  812. }
  813. SQL->StmtFree(stmt);
  814.  
  815. StrBuf->Clear(&buf);
  816. StrBuf->Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", tablename, selectoption);
  817. for( j = 0; j < MAX_SLOTS; ++j )
  818. StrBuf->Printf(&buf, ", `card%d`", j);
  819. StrBuf->AppendStr(&buf, ") VALUES ");
  820.  
  821. found = false;
  822. // insert non-matched items into the db as new items
  823. for( i = 0; i < max; ++i )
  824. {
  825. // skip empty and already matched entries
  826. if( items[i].nameid == 0 || flag[i] )
  827. continue;
  828.  
  829. if( found )
  830. StrBuf->AppendStr(&buf, ",");
  831. else
  832. found = true;
  833.  
  834. StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'",
  835. 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].bound, items[i].unique_id);
  836. for( j = 0; j < MAX_SLOTS; ++j )
  837. StrBuf->Printf(&buf, ", '%d'", items[i].card[j]);
  838. StrBuf->AppendStr(&buf, ")");
  839. }
  840.  
  841. if( found && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) )
  842. {
  843. Sql_ShowDebug(inter->sql_handle);
  844. errors++;
  845. }
  846.  
  847. StrBuf->Destroy(&buf);
  848. aFree(flag);
  849.  
  850. return errors;
  851. }
  852. /* pretty much a copy of chr->memitemdata_to_sql except it handles inventory_db exclusively,
  853. * - this is required because inventory db is the only one with the 'favorite' column. */
  854. int char_inventory_to_sql(const struct item items[], int max, int id) {
  855. StringBuf buf;
  856. SqlStmt* stmt;
  857. int i;
  858. int j;
  859. struct item item; // temp storage variable
  860. bool* flag; // bit array for inventory matching
  861. bool found;
  862. int errors = 0;
  863.  
  864. nullpo_ret(items);
  865.  
  866. // The following code compares inventory with current database values
  867. // and performs modification/deletion/insertion only on relevant rows.
  868. // This approach is more complicated than a trivial delete&insert, but
  869. // it significantly reduces cpu load on the database server.
  870.  
  871. StrBuf->Init(&buf);
  872. StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`");
  873. for( j = 0; j < MAX_SLOTS; ++j )
  874. StrBuf->Printf(&buf, ", `card%d`", j);
  875. StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`='%d'", inventory_db, id);
  876.  
  877. stmt = SQL->StmtMalloc(inter->sql_handle);
  878. if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf))
  879. || SQL_ERROR == SQL->StmtExecute(stmt) )
  880. {
  881. SqlStmt_ShowDebug(stmt);
  882. SQL->StmtFree(stmt);
  883. StrBuf->Destroy(&buf);
  884. return 1;
  885. }
  886.  
  887. memset(&item, 0, sizeof(item));
  888. SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL);
  889. SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL);
  890. SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL);
  891. SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &item.equip, 0, NULL, NULL);
  892. SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &item.identify, 0, NULL, NULL);
  893. SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL);
  894. SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL);
  895. SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
  896. SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &item.favorite, 0, NULL, NULL);
  897. SQL->StmtBindColumn(stmt, 9, SQLDT_UCHAR, &item.bound, 0, NULL, NULL);
  898. for( j = 0; j < MAX_SLOTS; ++j )
  899. SQL->StmtBindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
  900.  
  901. // bit array indicating which inventory items have already been matched
  902. flag = (bool*) aCalloc(max, sizeof(bool));
  903.  
  904. while( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) {
  905. found = false;
  906. // search for the presence of the item in the char's inventory
  907. for( i = 0; i < max; ++i ) {
  908. // skip empty and already matched entries
  909. if( items[i].nameid == 0 || flag[i] )
  910. continue;
  911.  
  912. if( items[i].nameid == item.nameid
  913. && items[i].card[0] == item.card[0]
  914. && items[i].card[2] == item.card[2]
  915. && items[i].card[3] == item.card[3]
  916. ) {
  917. //They are the same item.
  918. ARR_FIND( 0, MAX_SLOTS, j, items[i].card[j] != item.card[j] );
  919. if( j == MAX_SLOTS
  920. && items[i].amount == item.amount
  921. && items[i].equip == item.equip
  922. && items[i].identify == item.identify
  923. && items[i].refine == item.refine
  924. && items[i].attribute == item.attribute
  925. && items[i].expire_time == item.expire_time
  926. && items[i].favorite == item.favorite
  927. && items[i].bound == item.bound
  928. ) {
  929. ; //Do nothing.
  930. } else {
  931. // update all fields.
  932. StrBuf->Clear(&buf);
  933. StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `favorite`='%d', `bound`='%d'",
  934. inventory_db, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].bound);
  935. for( j = 0; j < MAX_SLOTS; ++j )
  936. StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
  937. StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);
  938.  
  939. if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) ) {
  940. Sql_ShowDebug(inter->sql_handle);
  941. errors++;
  942. }
  943. }
  944.  
  945. found = flag[i] = true; //Item dealt with,
  946. break; //skip to next item in the db.
  947. }
  948. }
  949. if( !found ) {// Item not present in inventory, remove it.
  950. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE from `%s` where `id`='%d' LIMIT 1", inventory_db, item.id) ) {
  951. Sql_ShowDebug(inter->sql_handle);
  952. errors++;
  953. }
  954. }
  955. }
  956. SQL->StmtFree(stmt);
  957.  
  958. StrBuf->Clear(&buf);
  959. StrBuf->Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`", inventory_db);
  960. for( j = 0; j < MAX_SLOTS; ++j )
  961. StrBuf->Printf(&buf, ", `card%d`", j);
  962. StrBuf->AppendStr(&buf, ") VALUES ");
  963.  
  964. found = false;
  965. // insert non-matched items into the db as new items
  966. for( i = 0; i < max; ++i ) {
  967. // skip empty and already matched entries
  968. if( items[i].nameid == 0 || flag[i] )
  969. continue;
  970.  
  971. if( found )
  972. StrBuf->AppendStr(&buf, ",");
  973. else
  974. found = true;
  975.  
  976. StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%d', '%"PRIu64"'",
  977. 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].favorite, items[i].bound, items[i].unique_id);
  978. for( j = 0; j < MAX_SLOTS; ++j )
  979. StrBuf->Printf(&buf, ", '%d'", items[i].card[j]);
  980. StrBuf->AppendStr(&buf, ")");
  981. }
  982.  
  983. if( found && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) ) {
  984. Sql_ShowDebug(inter->sql_handle);
  985. errors++;
  986. }
  987.  
  988. StrBuf->Destroy(&buf);
  989. aFree(flag);
  990.  
  991. return errors;
  992. }
  993.  
  994. /**
  995. * Returns the correct gender ID for the given character and enum value.
  996. *
  997. * If the per-character sex is defined but not supported by the current packetver, the database entries are corrected.
  998. *
  999. * @param sd Character data, if available.
  1000. * @param p Character status.
  1001. * @param sex Character sex (database enum)
  1002. *
  1003. * @retval SEX_MALE if the per-character sex is male
  1004. * @retval SEX_FEMALE if the per-character sex is female
  1005. * @retval 99 if the per-character sex is not defined or the current PACKETVER doesn't support it.
  1006. */
  1007. int char_mmo_gender(const struct char_session_data *sd, const struct mmo_charstatus *p, char sex)
  1008. {
  1009. #if PACKETVER >= 20141016
  1010. (void)sd; (void)p; // Unused
  1011. switch (sex) {
  1012. case 'M':
  1013. return SEX_MALE;
  1014. case 'F':
  1015. return SEX_FEMALE;
  1016. case 'U':
  1017. default:
  1018. return 99;
  1019. }
  1020. #else
  1021. if (sex == 'M' || sex == 'F') {
  1022. if (!sd) {
  1023. // sd is not available, there isn't much we can do. Just return and print a warning.
  1024. ShowWarning("Character '%s' (CID: %d, AID: %d) has sex '%c', but PACKETVER does not support per-character sex. Defaulting to 'U'.\n",
  1025. p->name, p->char_id, p->account_id, sex);
  1026. return 99;
  1027. }
  1028. if ((sex == 'M' && sd->sex == SEX_FEMALE)
  1029. || (sex == 'F' && sd->sex == SEX_MALE)) {
  1030. ShowWarning("Changing sex of character '%s' (CID: %d, AID: %d) to 'U' due to incompatible PACKETVER.\n", p->name, p->char_id, p->account_id);
  1031. chr->changecharsex(p->char_id, sd->sex);
  1032. } else {
  1033. ShowInfo("Resetting sex of character '%s' (CID: %d, AID: %d) to 'U' due to incompatible PACKETVER.\n", p->name, p->char_id, p->account_id);
  1034. }
  1035. if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `sex` = 'U' WHERE `char_id` = '%d'", char_db, p->char_id)) {
  1036. Sql_ShowDebug(inter->sql_handle);
  1037. }
  1038. }
  1039. return 99;
  1040. #endif
  1041. }
  1042.  
  1043. //=====================================================================================================
  1044. // Loads the basic character rooster for the given account. Returns total buffer used.
  1045. int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
  1046. {
  1047. SqlStmt* stmt;
  1048. struct mmo_charstatus p;
  1049. int j = 0, i;
  1050. char last_map[MAP_NAME_LENGTH_EXT];
  1051. time_t unban_time = 0;
  1052. char sex[2];
  1053.  
  1054. nullpo_ret(sd);
  1055. nullpo_ret(buf);
  1056.  
  1057. stmt = SQL->StmtMalloc(inter->sql_handle);
  1058. if( stmt == NULL ) {
  1059. SqlStmt_ShowDebug(stmt);
  1060. return 0;
  1061. }
  1062. memset(&p, 0, sizeof(p));
  1063.  
  1064. for(i = 0 ; i < MAX_CHARS; i++ ) {
  1065. sd->found_char[i] = -1;
  1066. sd->unban_time[i] = 0;
  1067. }
  1068.  
  1069. // read char data
  1070. if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT "
  1071. "`char_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
  1072. "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`,"
  1073. "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`,"
  1074. "`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`,"
  1075. "`robe`,`slotchange`,`unban_time`,`sex`"
  1076. " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS)
  1077. || SQL_ERROR == SQL->StmtExecute(stmt)
  1078. || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL)
  1079. || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR, &p.slot, 0, NULL, NULL)
  1080. || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &p.name, sizeof(p.name), NULL, NULL)
  1081. || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_SHORT, &p.class_, 0, NULL, NULL)
  1082. || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_UINT, &p.base_level, 0, NULL, NULL)
  1083. || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_UINT, &p.job_level, 0, NULL, NULL)
  1084. || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_UINT, &p.base_exp, 0, NULL, NULL)
  1085. || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &p.job_exp, 0, NULL, NULL)
  1086. || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_INT, &p.zeny, 0, NULL, NULL)
  1087. || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_SHORT, &p.str, 0, NULL, NULL)
  1088. || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_SHORT, &p.agi, 0, NULL, NULL)
  1089. || SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_SHORT, &p.vit, 0, NULL, NULL)
  1090. || SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_SHORT, &p.int_, 0, NULL, NULL)
  1091. || SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_SHORT, &p.dex, 0, NULL, NULL)
  1092. || SQL_ERROR == SQL->StmtBindColumn(stmt, 14, SQLDT_SHORT, &p.luk, 0, NULL, NULL)
  1093. || SQL_ERROR == SQL->StmtBindColumn(stmt, 15, SQLDT_INT, &p.max_hp, 0, NULL, NULL)
  1094. || SQL_ERROR == SQL->StmtBindColumn(stmt, 16, SQLDT_INT, &p.hp, 0, NULL, NULL)
  1095. || SQL_ERROR == SQL->StmtBindColumn(stmt, 17, SQLDT_INT, &p.max_sp, 0, NULL, NULL)
  1096. || SQL_ERROR == SQL->StmtBindColumn(stmt, 18, SQLDT_INT, &p.sp, 0, NULL, NULL)
  1097. || SQL_ERROR == SQL->StmtBindColumn(stmt, 19, SQLDT_UINT, &p.status_point, 0, NULL, NULL)
  1098. || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_UINT, &p.skill_point, 0, NULL, NULL)
  1099. || SQL_ERROR == SQL->StmtBindColumn(stmt, 21, SQLDT_UINT, &p.option, 0, NULL, NULL)
  1100. || SQL_ERROR == SQL->StmtBindColumn(stmt, 22, SQLDT_UCHAR, &p.karma, 0, NULL, NULL)
  1101. || SQL_ERROR == SQL->StmtBindColumn(stmt, 23, SQLDT_SHORT, &p.manner, 0, NULL, NULL)
  1102. || SQL_ERROR == SQL->StmtBindColumn(stmt, 24, SQLDT_SHORT, &p.hair, 0, NULL, NULL)
  1103. || SQL_ERROR == SQL->StmtBindColumn(stmt, 25, SQLDT_SHORT, &p.hair_color, 0, NULL, NULL)
  1104. || SQL_ERROR == SQL->StmtBindColumn(stmt, 26, SQLDT_SHORT, &p.clothes_color, 0, NULL, NULL)
  1105. || SQL_ERROR == SQL->StmtBindColumn(stmt, 27, SQLDT_SHORT, &p.body, 0, NULL, NULL)
  1106. || SQL_ERROR == SQL->StmtBindColumn(stmt, 28, SQLDT_SHORT, &p.weapon, 0, NULL, NULL)
  1107. || SQL_ERROR == SQL->StmtBindColumn(stmt, 29, SQLDT_SHORT, &p.shield, 0, NULL, NULL)
  1108. || SQL_ERROR == SQL->StmtBindColumn(stmt, 30, SQLDT_SHORT, &p.head_top, 0, NULL, NULL)
  1109. || SQL_ERROR == SQL->StmtBindColumn(stmt, 31, SQLDT_SHORT, &p.head_mid, 0, NULL, NULL)
  1110. || SQL_ERROR == SQL->StmtBindColumn(stmt, 32, SQLDT_SHORT, &p.head_bottom, 0, NULL, NULL)
  1111. || SQL_ERROR == SQL->StmtBindColumn(stmt, 33, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL)
  1112. || SQL_ERROR == SQL->StmtBindColumn(stmt, 34, SQLDT_USHORT, &p.rename, 0, NULL, NULL)
  1113. || SQL_ERROR == SQL->StmtBindColumn(stmt, 35, SQLDT_UINT32, &p.delete_date, 0, NULL, NULL)
  1114. || SQL_ERROR == SQL->StmtBindColumn(stmt, 36, SQLDT_SHORT, &p.robe, 0, NULL, NULL)
  1115. || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_USHORT, &p.slotchange, 0, NULL, NULL)
  1116. || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_LONG, &unban_time, 0, NULL, NULL)
  1117. || SQL_ERROR == SQL->StmtBindColumn(stmt, 39, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL)
  1118. ) {
  1119. SqlStmt_ShowDebug(stmt);
  1120. SQL->StmtFree(stmt);
  1121. return 0;
  1122. }
  1123.  
  1124. for( i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->StmtNextRow(stmt); i++ ) {
  1125. if (p.slot >= MAX_CHARS)
  1126. continue;
  1127. p.last_point.map = mapindex->name2id(last_map);
  1128. sd->found_char[p.slot] = p.char_id;
  1129. sd->unban_time[p.slot] = unban_time;
  1130. p.sex = chr->mmo_gender(sd, &p, sex[0]);
  1131. j += chr->mmo_char_tobuf(WBUFP(buf, j), &p);
  1132. }
  1133.  
  1134. memset(sd->new_name,0,sizeof(sd->new_name));
  1135.  
  1136. SQL->StmtFree(stmt);
  1137. return j;
  1138. }
  1139.  
  1140. //=====================================================================================================
  1141. int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything)
  1142. {
  1143. int i,j;
  1144. char t_msg[128] = "";
  1145. struct mmo_charstatus* cp;
  1146. StringBuf buf;
  1147. SqlStmt* stmt;
  1148. char last_map[MAP_NAME_LENGTH_EXT];
  1149. char save_map[MAP_NAME_LENGTH_EXT];
  1150. char point_map[MAP_NAME_LENGTH_EXT];
  1151. struct point tmp_point;
  1152. struct item tmp_item;
  1153. struct s_skill tmp_skill;
  1154. struct s_friend tmp_friend;
  1155. #ifdef HOTKEY_SAVING
  1156. struct hotkey tmp_hotkey;
  1157. int hotkey_num = 0;
  1158. #endif
  1159. unsigned int opt;
  1160. int account_id;
  1161. char sex[2];
  1162.  
  1163. nullpo_ret(p);
  1164.  
  1165. memset(p, 0, sizeof(struct mmo_charstatus));
  1166.  
  1167. if (save_log) ShowInfo("Char load request (%d)\n", char_id);
  1168.  
  1169. stmt = SQL->StmtMalloc(inter->sql_handle);
  1170. if( stmt == NULL )
  1171. {
  1172. SqlStmt_ShowDebug(stmt);
  1173. return 0;
  1174. }
  1175.  
  1176. // read char data
  1177. if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT "
  1178. "`char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
  1179. "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`,"
  1180. "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`,"
  1181. "`hair_color`,`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`,"
  1182. "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`,`slotchange`,"
  1183. "`char_opt`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`"
  1184. " FROM `%s` WHERE `char_id`=? LIMIT 1", char_db)
  1185. || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0)
  1186. || SQL_ERROR == SQL->StmtExecute(stmt)
  1187. || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p->char_id, 0, NULL, NULL)
  1188. || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &p->account_id, 0, NULL, NULL)
  1189. || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UCHAR, &p->slot, 0, NULL, NULL)
  1190. || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_STRING, &p->name, sizeof(p->name), NULL, NULL)
  1191. || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_SHORT, &p->class_, 0, NULL, NULL)
  1192. || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_UINT, &p->base_level, 0, NULL, NULL)
  1193. || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_UINT, &p->job_level, 0, NULL, NULL)
  1194. || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &p->base_exp, 0, NULL, NULL)
  1195. || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_UINT, &p->job_exp, 0, NULL, NULL)
  1196. || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_INT, &p->zeny, 0, NULL, NULL)
  1197. || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_SHORT, &p->str, 0, NULL, NULL)
  1198. || SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_SHORT, &p->agi, 0, NULL, NULL)
  1199. || SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_SHORT, &p->vit, 0, NULL, NULL)
  1200. || SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_SHORT, &p->int_, 0, NULL, NULL)
  1201. || SQL_ERROR == SQL->StmtBindColumn(stmt, 14, SQLDT_SHORT, &p->dex, 0, NULL, NULL)
  1202. || SQL_ERROR == SQL->StmtBindColumn(stmt, 15, SQLDT_SHORT, &p->luk, 0, NULL, NULL)
  1203. || SQL_ERROR == SQL->StmtBindColumn(stmt, 16, SQLDT_INT, &p->max_hp, 0, NULL, NULL)
  1204. || SQL_ERROR == SQL->StmtBindColumn(stmt, 17, SQLDT_INT, &p->hp, 0, NULL, NULL)
  1205. || SQL_ERROR == SQL->StmtBindColumn(stmt, 18, SQLDT_INT, &p->max_sp, 0, NULL, NULL)
  1206. || SQL_ERROR == SQL->StmtBindColumn(stmt, 19, SQLDT_INT, &p->sp, 0, NULL, NULL)
  1207. || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_UINT, &p->status_point, 0, NULL, NULL)
  1208. || SQL_ERROR == SQL->StmtBindColumn(stmt, 21, SQLDT_UINT, &p->skill_point, 0, NULL, NULL)
  1209. || SQL_ERROR == SQL->StmtBindColumn(stmt, 22, SQLDT_UINT, &p->option, 0, NULL, NULL)
  1210. || SQL_ERROR == SQL->StmtBindColumn(stmt, 23, SQLDT_UCHAR, &p->karma, 0, NULL, NULL)
  1211. || SQL_ERROR == SQL->StmtBindColumn(stmt, 24, SQLDT_SHORT, &p->manner, 0, NULL, NULL)
  1212. || SQL_ERROR == SQL->StmtBindColumn(stmt, 25, SQLDT_INT, &p->party_id, 0, NULL, NULL)
  1213. || SQL_ERROR == SQL->StmtBindColumn(stmt, 26, SQLDT_INT, &p->guild_id, 0, NULL, NULL)
  1214. || SQL_ERROR == SQL->StmtBindColumn(stmt, 27, SQLDT_INT, &p->pet_id, 0, NULL, NULL)
  1215. || SQL_ERROR == SQL->StmtBindColumn(stmt, 28, SQLDT_INT, &p->hom_id, 0, NULL, NULL)
  1216. || SQL_ERROR == SQL->StmtBindColumn(stmt, 29, SQLDT_INT, &p->ele_id, 0, NULL, NULL)
  1217. || SQL_ERROR == SQL->StmtBindColumn(stmt, 30, SQLDT_SHORT, &p->hair, 0, NULL, NULL)
  1218. || SQL_ERROR == SQL->StmtBindColumn(stmt, 31, SQLDT_SHORT, &p->hair_color, 0, NULL, NULL)
  1219. || SQL_ERROR == SQL->StmtBindColumn(stmt, 32, SQLDT_SHORT, &p->clothes_color, 0, NULL, NULL)
  1220. || SQL_ERROR == SQL->StmtBindColumn(stmt, 33, SQLDT_SHORT, &p->body, 0, NULL, NULL)
  1221. || SQL_ERROR == SQL->StmtBindColumn(stmt, 34, SQLDT_SHORT, &p->weapon, 0, NULL, NULL)
  1222. || SQL_ERROR == SQL->StmtBindColumn(stmt, 35, SQLDT_SHORT, &p->shield, 0, NULL, NULL)
  1223. || SQL_ERROR == SQL->StmtBindColumn(stmt, 36, SQLDT_SHORT, &p->head_top, 0, NULL, NULL)
  1224. || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_SHORT, &p->head_mid, 0, NULL, NULL)
  1225. || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_SHORT, &p->head_bottom, 0, NULL, NULL)
  1226. || SQL_ERROR == SQL->StmtBindColumn(stmt, 39, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL)
  1227. || SQL_ERROR == SQL->StmtBindColumn(stmt, 40, SQLDT_SHORT, &p->last_point.x, 0, NULL, NULL)
  1228. || SQL_ERROR == SQL->StmtBindColumn(stmt, 41, SQLDT_SHORT, &p->last_point.y, 0, NULL, NULL)
  1229. || SQL_ERROR == SQL->StmtBindColumn(stmt, 42, SQLDT_STRING, &save_map, sizeof(save_map), NULL, NULL)
  1230. || SQL_ERROR == SQL->StmtBindColumn(stmt, 43, SQLDT_SHORT, &p->save_point.x, 0, NULL, NULL)
  1231. || SQL_ERROR == SQL->StmtBindColumn(stmt, 44, SQLDT_SHORT, &p->save_point.y, 0, NULL, NULL)
  1232. || SQL_ERROR == SQL->StmtBindColumn(stmt, 45, SQLDT_INT, &p->partner_id, 0, NULL, NULL)
  1233. || SQL_ERROR == SQL->StmtBindColumn(stmt, 46, SQLDT_INT, &p->father, 0, NULL, NULL)
  1234. || SQL_ERROR == SQL->StmtBindColumn(stmt, 47, SQLDT_INT, &p->mother, 0, NULL, NULL)
  1235. || SQL_ERROR == SQL->StmtBindColumn(stmt, 48, SQLDT_INT, &p->child, 0, NULL, NULL)
  1236. || SQL_ERROR == SQL->StmtBindColumn(stmt, 49, SQLDT_INT, &p->fame, 0, NULL, NULL)
  1237. || SQL_ERROR == SQL->StmtBindColumn(stmt, 50, SQLDT_USHORT, &p->rename, 0, NULL, NULL)
  1238. || SQL_ERROR == SQL->StmtBindColumn(stmt, 51, SQLDT_UINT32, &p->delete_date, 0, NULL, NULL)
  1239. || SQL_ERROR == SQL->StmtBindColumn(stmt, 52, SQLDT_SHORT, &p->robe, 0, NULL, NULL)
  1240. || SQL_ERROR == SQL->StmtBindColumn(stmt, 53, SQLDT_USHORT, &p->slotchange, 0, NULL, NULL)
  1241. || SQL_ERROR == SQL->StmtBindColumn(stmt, 54, SQLDT_UINT, &opt, 0, NULL, NULL)
  1242. || SQL_ERROR == SQL->StmtBindColumn(stmt, 55, SQLDT_UCHAR, &p->font, 0, NULL, NULL)
  1243. || SQL_ERROR == SQL->StmtBindColumn(stmt, 56, SQLDT_UINT, &p->uniqueitem_counter, 0, NULL, NULL)
  1244. || SQL_ERROR == SQL->StmtBindColumn(stmt, 57, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL)
  1245. || SQL_ERROR == SQL->StmtBindColumn(stmt, 58, SQLDT_UCHAR, &p->hotkey_rowshift, 0, NULL, NULL)
  1246. ) {
  1247. SqlStmt_ShowDebug(stmt);
  1248. SQL->StmtFree(stmt);
  1249. return 0;
  1250. }
  1251. if (SQL_SUCCESS != SQL->StmtNextRow(stmt))
  1252. {
  1253. ShowError("Requested non-existant character id: %d!\n", char_id);
  1254. SQL->StmtFree(stmt);
  1255. return 0;
  1256. }
  1257.  
  1258. p->sex = chr->mmo_gender(NULL, p, sex[0]);
  1259.  
  1260. account_id = p->account_id;
  1261.  
  1262. p->last_point.map = mapindex->name2id(last_map);
  1263. p->save_point.map = mapindex->name2id(save_map);
  1264.  
  1265. if( p->last_point.map == 0 ) {
  1266. p->last_point.map = (unsigned short)strdb_iget(mapindex->db, mapindex->default_map);
  1267. p->last_point.x = mapindex->default_x;
  1268. p->last_point.y = mapindex->default_y;
  1269. }
  1270.  
  1271. if( p->save_point.map == 0 ) {
  1272. p->save_point.map = (unsigned short)strdb_iget(mapindex->db, mapindex->default_map);
  1273. p->save_point.x = mapindex->default_x;
  1274. p->save_point.y = mapindex->default_y;
  1275. }
  1276.  
  1277. strcat(t_msg, " status");
  1278.  
  1279. if (!load_everything) // For quick selection of data when displaying the char menu
  1280. {
  1281. SQL->StmtFree(stmt);
  1282. return 1;
  1283. }
  1284.  
  1285. //read memo data
  1286. //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`)
  1287. memset(&tmp_point, 0, sizeof(tmp_point));
  1288. if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `map`,`x`,`y` FROM `%s` WHERE `char_id`=? ORDER by `memo_id` LIMIT %d", memo_db, MAX_MEMOPOINTS)
  1289. || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0)
  1290. || SQL_ERROR == SQL->StmtExecute(stmt)
  1291. || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &point_map, sizeof(point_map), NULL, NULL)
  1292. || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_point.x, 0, NULL, NULL)
  1293. || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_point.y, 0, NULL, NULL)
  1294. )
  1295. SqlStmt_ShowDebug(stmt);
  1296.  
  1297. for( i = 0; i < MAX_MEMOPOINTS && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) {
  1298. tmp_point.map = mapindex->name2id(point_map);
  1299. memcpy(&p->memo_point[i], &tmp_point, sizeof(tmp_point));
  1300. }
  1301. strcat(t_msg, " memo");
  1302.  
  1303. //read inventory
  1304. //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `bound`, `unique_id`)
  1305. StrBuf->Init(&buf);
  1306. StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`");
  1307. for( i = 0; i < MAX_SLOTS; ++i )
  1308. StrBuf->Printf(&buf, ", `card%d`", i);
  1309. StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY);
  1310.  
  1311. memset(&tmp_item, 0, sizeof(tmp_item));
  1312. if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf))
  1313. || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0)
  1314. || SQL_ERROR == SQL->StmtExecute(stmt)
  1315. || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_item.id, 0, NULL, NULL)
  1316. || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_item.nameid, 0, NULL, NULL)
  1317. || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_item.amount, 0, NULL, NULL)
  1318. || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &tmp_item.equip, 0, NULL, NULL)
  1319. || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL)
  1320. || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL)
  1321. || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL)
  1322. || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL)
  1323. || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL)
  1324. || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UCHAR, &tmp_item.bound, 0, NULL, NULL)
  1325. || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_UINT64, &tmp_item.unique_id, 0, NULL, NULL)
  1326. )
  1327. SqlStmt_ShowDebug(stmt);
  1328. for( i = 0; i < MAX_SLOTS; ++i )
  1329. if( SQL_ERROR == SQL->StmtBindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
  1330. SqlStmt_ShowDebug(stmt);
  1331.  
  1332. for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i )
  1333. memcpy(&p->inventory[i], &tmp_item, sizeof(tmp_item));
  1334.  
  1335. strcat(t_msg, " inventory");
  1336.  
  1337. //read cart
  1338. //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `bound`, `unique_id`)
  1339. StrBuf->Clear(&buf);
  1340. StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`");
  1341. for( j = 0; j < MAX_SLOTS; ++j )
  1342. StrBuf->Printf(&buf, ", `card%d`", j);
  1343. StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART);
  1344.  
  1345. memset(&tmp_item, 0, sizeof(tmp_item));
  1346. if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf))
  1347. || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0)
  1348. || SQL_ERROR == SQL->StmtExecute(stmt)
  1349. || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_item.id, 0, NULL, NULL)
  1350. || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_item.nameid, 0, NULL, NULL)
  1351. || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_item.amount, 0, NULL, NULL)
  1352. || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &tmp_item.equip, 0, NULL, NULL)
  1353. || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL)
  1354. || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL)
  1355. || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL)
  1356. || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL)
  1357. || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &tmp_item.bound, 0, NULL, NULL)
  1358. || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &tmp_item.unique_id, 0, NULL, NULL)
  1359. ) {
  1360. SqlStmt_ShowDebug(stmt);
  1361. }
  1362. for( i = 0; i < MAX_SLOTS; ++i )
  1363. if( SQL_ERROR == SQL->StmtBindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
  1364. SqlStmt_ShowDebug(stmt);
  1365.  
  1366. for( i = 0; i < MAX_CART && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i )
  1367. memcpy(&p->cart[i], &tmp_item, sizeof(tmp_item));
  1368. strcat(t_msg, " cart");
  1369.  
  1370. //read storage
  1371. inter_storage->fromsql(p->account_id, &p->storage);
  1372. strcat(t_msg, " storage");
  1373.  
  1374. //read skill
  1375. //`skill` (`char_id`, `id`, `lv`)
  1376. memset(&tmp_skill, 0, sizeof(tmp_skill));
  1377. if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `id`, `lv`,`flag` FROM `%s` WHERE `char_id`=? LIMIT %d", skill_db, MAX_SKILL)
  1378. || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0)
  1379. || SQL_ERROR == SQL->StmtExecute(stmt)
  1380. || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_USHORT, &tmp_skill.id , 0, NULL, NULL)
  1381. || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR , &tmp_skill.lv , 0, NULL, NULL)
  1382. || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UCHAR , &tmp_skill.flag, 0, NULL, NULL)
  1383. ) {
  1384. SqlStmt_ShowDebug(stmt);
  1385. }
  1386.  
  1387. if( tmp_skill.flag != SKILL_FLAG_PERM_GRANTED )
  1388. tmp_skill.flag = SKILL_FLAG_PERMANENT;
  1389.  
  1390. for( i = 0; i < MAX_SKILL && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) {
  1391. if( skillid2idx[tmp_skill.id] )
  1392. memcpy(&p->skill[skillid2idx[tmp_skill.id]], &tmp_skill, sizeof(tmp_skill));
  1393. else
  1394. ShowWarning("chr->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);
  1395. }
  1396. strcat(t_msg, " skills");
  1397.  
  1398. //read friends
  1399. //`friends` (`char_id`, `friend_account`, `friend_id`)
  1400. memset(&tmp_friend, 0, sizeof(tmp_friend));
  1401. if (SQL_ERROR == SQL->StmtPrepare(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)
  1402. || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0)
  1403. || SQL_ERROR == SQL->StmtExecute(stmt)
  1404. || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_friend.account_id, 0, NULL, NULL)
  1405. || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &tmp_friend.char_id, 0, NULL, NULL)
  1406. || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &tmp_friend.name, sizeof(tmp_friend.name), NULL, NULL)
  1407. ) {
  1408. SqlStmt_ShowDebug(stmt);
  1409. }
  1410.  
  1411. for( i = 0; i < MAX_FRIENDS && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i )
  1412. memcpy(&p->friends[i], &tmp_friend, sizeof(tmp_friend));
  1413. strcat(t_msg, " friends");
  1414.  
  1415. #ifdef HOTKEY_SAVING
  1416. //read hotkeys
  1417. //`hotkey` (`char_id`, `hotkey`, `type`, `itemskill_id`, `skill_lvl`
  1418. memset(&tmp_hotkey, 0, sizeof(tmp_hotkey));
  1419. if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `hotkey`, `type`, `itemskill_id`, `skill_lvl` FROM `%s` WHERE `char_id`=?", hotkey_db)
  1420. || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0)
  1421. || SQL_ERROR == SQL->StmtExecute(stmt)
  1422. || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &hotkey_num, 0, NULL, NULL)
  1423. || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR, &tmp_hotkey.type, 0, NULL, NULL)
  1424. || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UINT, &tmp_hotkey.id, 0, NULL, NULL)
  1425. || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &tmp_hotkey.lv, 0, NULL, NULL) )
  1426. SqlStmt_ShowDebug(stmt);
  1427.  
  1428. while( SQL_SUCCESS == SQL->StmtNextRow(stmt) )
  1429. {
  1430. if( hotkey_num >= 0 && hotkey_num < MAX_HOTKEYS )
  1431. memcpy(&p->hotkeys[hotkey_num], &tmp_hotkey, sizeof(tmp_hotkey));
  1432. else
  1433. ShowWarning("chr->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);
  1434. }
  1435. strcat(t_msg, " hotkeys");
  1436. #endif
  1437.  
  1438. /* Mercenary Owner DataBase */
  1439. inter_mercenary->owner_fromsql(char_id, p);
  1440. strcat(t_msg, " mercenary");
  1441.  
  1442. /* default */
  1443. p->mod_exp = p->mod_drop = p->mod_death = 100;
  1444.  
  1445. //`account_data` (`account_id`,`bank_vault`,`base_exp`,`base_drop`,`base_death`)
  1446. if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `bank_vault`,`base_exp`,`base_drop`,`base_death` FROM `%s` WHERE `account_id`=? LIMIT 1", account_data_db)
  1447. || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &account_id, 0)
  1448. || SQL_ERROR == SQL->StmtExecute(stmt)
  1449. || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p->bank_vault, 0, NULL, NULL)
  1450. || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_USHORT, &p->mod_exp, 0, NULL, NULL)
  1451. || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_USHORT, &p->mod_drop, 0, NULL, NULL)
  1452. || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &p->mod_death, 0, NULL, NULL)
  1453. ) {
  1454. SqlStmt_ShowDebug(stmt);
  1455. }
  1456.  
  1457. if( SQL_SUCCESS == SQL->StmtNextRow(stmt) )
  1458. strcat(t_msg, " accdata");
  1459.  
  1460. if (save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfully!
  1461. SQL->StmtFree(stmt);
  1462. StrBuf->Destroy(&buf);
  1463.  
  1464. /* load options into proper vars */
  1465. if( opt & OPT_ALLOW_PARTY )
  1466. p->allow_party = true;
  1467. if( opt & OPT_SHOW_EQUIP )
  1468. p->show_equip = true;
  1469.  
  1470. cp = idb_ensure(chr->char_db_, char_id, chr->create_charstatus);
  1471. memcpy(cp, p, sizeof(struct mmo_charstatus));
  1472. return 1;
  1473. }
  1474.  
  1475. //==========================================================================================================
  1476. int char_mmo_char_sql_init(void)
  1477. {
  1478. chr->char_db_= idb_alloc(DB_OPT_RELEASE_DATA);
  1479.  
  1480. //the 'set offline' part is now in check_login_conn ...
  1481. //if the server connects to loginserver
  1482. //it will dc all off players
  1483. //and send the loginserver the new state....
  1484.  
  1485. // Force all users offline in sql when starting char-server
  1486. // (useful when servers crashes and don't clean the database)
  1487. chr->set_all_offline_sql();
  1488.  
  1489. return 0;
  1490. }
  1491.  
  1492. /* [Ind/Hercules] - special thanks to Yommy for providing the packet structure/data */
  1493. bool char_char_slotchange(struct char_session_data *sd, int fd, unsigned short from, unsigned short to) {
  1494. struct mmo_charstatus char_dat;
  1495. int from_id = 0;
  1496.  
  1497. nullpo_ret(sd);
  1498. if( from >= MAX_CHARS || to >= MAX_CHARS || ( sd->char_slots && to > sd->char_slots ) || sd->found_char[from] <= 0 )
  1499. return false;
  1500.  
  1501. if( !chr->mmo_char_fromsql(sd->found_char[from], &char_dat, false) ) // Only the short data is needed.
  1502. return false;
  1503.  
  1504. if( char_dat.slotchange == 0 )
  1505. return false;
  1506.  
  1507. from_id = sd->found_char[from];
  1508.  
  1509. if( sd->found_char[to] > 0 ) {/* moving char to occupied slot */
  1510. bool result = false;
  1511. /* update both at once */
  1512. if( SQL_SUCCESS != SQL->QueryStr(inter->sql_handle, "START TRANSACTION")
  1513. || SQL_SUCCESS != SQL->Query(inter->sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d' LIMIT 1", char_db, from, sd->found_char[to])
  1514. || SQL_SUCCESS != SQL->Query(inter->sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d' LIMIT 1", char_db, to, sd->found_char[from])
  1515. )
  1516. Sql_ShowDebug(inter->sql_handle);
  1517. else
  1518. result = true;
  1519.  
  1520. if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") ) {
  1521. Sql_ShowDebug(inter->sql_handle);
  1522. result = false;
  1523. }
  1524. if( !result )
  1525. return false;
  1526. } else {/* slot is free. */
  1527. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d' LIMIT 1", char_db, to, sd->found_char[from] ) ) {
  1528. Sql_ShowDebug(inter->sql_handle);
  1529. return false;
  1530. }
  1531. }
  1532.  
  1533. /* update count */
  1534. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `slotchange`=`slotchange`-1 WHERE `char_id`='%d' LIMIT 1", char_db, from_id ) ) {
  1535. Sql_ShowDebug(inter->sql_handle);
  1536. return false;
  1537. }
  1538.  
  1539. return true;
  1540. }
  1541.  
  1542. //-----------------------------------
  1543. // Function to change character's names
  1544. //-----------------------------------
  1545. int char_rename_char_sql(struct char_session_data *sd, int char_id)
  1546. {
  1547. struct mmo_charstatus char_dat;
  1548. char esc_name[NAME_LENGTH*2+1];
  1549.  
  1550. nullpo_retr(2, sd);
  1551.  
  1552. if( sd->new_name[0] == 0 ) // Not ready for rename
  1553. return 2;
  1554.  
  1555. if( !chr->mmo_char_fromsql(char_id, &char_dat, false) ) // Only the short data is needed.
  1556. return 2;
  1557.  
  1558. if (sd->account_id != char_dat.account_id) // Try rename not own char
  1559. return 2;
  1560.  
  1561. if( char_dat.rename == 0 )
  1562. return 1;
  1563.  
  1564. SQL->EscapeStringLen(inter->sql_handle, esc_name, sd->new_name, strnlen(sd->new_name, NAME_LENGTH));
  1565.  
  1566. // check if the char exist
  1567. if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT 1 FROM `%s` WHERE `name` LIKE '%s' LIMIT 1", char_db, esc_name) )
  1568. {
  1569. Sql_ShowDebug(inter->sql_handle);
  1570. return 4;
  1571. }
  1572.  
  1573. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `name` = '%s', `rename` = '%d' WHERE `char_id` = '%d'", char_db, esc_name, --char_dat.rename, char_id) )
  1574. {
  1575. Sql_ShowDebug(inter->sql_handle);
  1576. return 3;
  1577. }
  1578.  
  1579. // Change character's name into guild_db.
  1580. if( char_dat.guild_id )
  1581. inter_guild->charname_changed(char_dat.guild_id, sd->account_id, char_id, sd->new_name);
  1582.  
  1583. safestrncpy(char_dat.name, sd->new_name, NAME_LENGTH);
  1584. memset(sd->new_name,0,sizeof(sd->new_name));
  1585.  
  1586. // log change
  1587. if( log_char )
  1588. {
  1589. if( SQL_ERROR == SQL->Query(inter->sql_handle,
  1590. "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
  1591. "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '0', '0', '0', '0', '0', '0', '0', '0')",
  1592. charlog_db, "change char name", sd->account_id, char_dat.char_id, char_dat.slot, esc_name) )
  1593. Sql_ShowDebug(inter->sql_handle);
  1594. }
  1595.  
  1596. return 0;
  1597. }
  1598.  
  1599. int char_check_char_name(char * name, char * esc_name)
  1600. {
  1601. int i;
  1602.  
  1603. nullpo_retr(-2, name);
  1604. nullpo_retr(-2, esc_name);
  1605.  
  1606. // check length of character name
  1607. if (name[0] == '\0')
  1608. return -2; // empty character name
  1609. /**
  1610. * The client does not allow you to create names with less than 4 characters, however,
  1611. * the use of WPE can bypass this, and this fixes the exploit.
  1612. **/
  1613. if( strlen( name ) < 4 )
  1614. return -2;
  1615. // check content of character name
  1616. if( remove_control_chars(name) )
  1617. return -2; // control chars in name
  1618.  
  1619. // check for reserved names
  1620. if( strcmpi(name, wisp_server_name) == 0 )
  1621. return -1; // nick reserved for internal server messages
  1622.  
  1623. // Check Authorized letters/symbols in the name of the character
  1624. if( char_name_option == 1 )
  1625. { // only letters/symbols in char_name_letters are authorized
  1626. for( i = 0; i < NAME_LENGTH && name[i]; i++ )
  1627. if( strchr(char_name_letters, name[i]) == NULL )
  1628. return -2;
  1629. }
  1630. else if( char_name_option == 2 )
  1631. { // letters/symbols in char_name_letters are forbidden
  1632. for( i = 0; i < NAME_LENGTH && name[i]; i++ )
  1633. if( strchr(char_name_letters, name[i]) != NULL )
  1634. return -5;
  1635. }
  1636. if( name_ignoring_case ) {
  1637. if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT 1 FROM `%s` WHERE BINARY `name` = '%s' LIMIT 1", char_db, esc_name) ) {
  1638. Sql_ShowDebug(inter->sql_handle);
  1639. return -2;
  1640. }
  1641. } else {
  1642. if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT 1 FROM `%s` WHERE `name` = '%s' LIMIT 1", char_db, esc_name) ) {
  1643. Sql_ShowDebug(inter->sql_handle);
  1644. return -2;
  1645. }
  1646. }
  1647. if( SQL->NumRows(inter->sql_handle) > 0 )
  1648. return -1; // name already exists
  1649.  
  1650. return 0;
  1651. }
  1652.  
  1653. /**
  1654. * Creates a new character
  1655. * Return values:
  1656. * -1: 'Charname already exists'
  1657. * -2: 'Char creation denied'/ Unknown error
  1658. * -3: 'You are underaged'
  1659. * -4: 'You are not eligible to open the Character Slot.'
  1660. * -5: 'Symbols in Character Names are forbidden'
  1661. * char_id: Success
  1662. **/
  1663. int char_make_new_char_sql(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style) {
  1664. char name[NAME_LENGTH];
  1665. char esc_name[NAME_LENGTH*2+1];
  1666. int char_id, flag, k, l;
  1667.  
  1668. nullpo_retr(-2, sd);
  1669. nullpo_retr(-2, name_);
  1670. safestrncpy(name, name_, NAME_LENGTH);
  1671. normalize_name(name,TRIM_CHARS);
  1672. SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
  1673.  
  1674. flag = chr->check_char_name(name,esc_name);
  1675. if( flag < 0 )
  1676. return flag;
  1677.  
  1678. //check other inputs
  1679. #if PACKETVER >= 20120307
  1680. if(slot < 0 || slot >= sd->char_slots)
  1681. #else
  1682. if((slot < 0 || slot >= sd->char_slots) // slots
  1683. || (str + agi + vit + int_ + dex + luk != 6*5 ) // stats
  1684. || (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
  1685. || (str + int_ != 10 || agi + luk != 10 || vit + dex != 10) ) // pairs
  1686. #endif
  1687. #if PACKETVER >= 20100413
  1688. return -4; // invalid slot
  1689. #else
  1690. return -2; // invalid input
  1691. #endif
  1692.  
  1693. // check char slot
  1694. if( sd->found_char[slot] != -1 )
  1695. return -2; /* character account limit exceeded */
  1696.  
  1697. #if PACKETVER >= 20120307
  1698. //Insert the new char entry to the database
  1699. if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `status_point`,`str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`,"
  1700. "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES ("
  1701. "'%d', '%d', '%s', '%d', '%d','%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')",
  1702. char_db, sd->account_id , slot, esc_name, start_zeny, 48, str, agi, vit, int_, dex, luk,
  1703. (40 * (100 + vit)/100) , (40 * (100 + vit)/100 ), (11 * (100 + int_)/100), (11 * (100 + int_)/100), hair_style, hair_color,
  1704. mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y) )
  1705. {
  1706. Sql_ShowDebug(inter->sql_handle);
  1707. return -2; //No, stop the procedure!
  1708. }
  1709. #else
  1710. //Insert the new char entry to the database
  1711. if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`,"
  1712. "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES ("
  1713. "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')",
  1714. char_db, sd->account_id , slot, esc_name, start_zeny, str, agi, vit, int_, dex, luk,
  1715. (40 * (100 + vit)/100) , (40 * (100 + vit)/100 ), (11 * (100 + int_)/100), (11 * (100 + int_)/100), hair_style, hair_color,
  1716. mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y) )
  1717. {
  1718. Sql_ShowDebug(inter->sql_handle);
  1719. return -2; //No, stop the procedure!
  1720. }
  1721. #endif
  1722. //Retrieve the newly auto-generated char id
  1723. char_id = (int)SQL->LastInsertId(inter->sql_handle);
  1724.  
  1725. if( !char_id )
  1726. return -2;
  1727.  
  1728. // Validation success, log result
  1729. if (log_char) {
  1730. if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
  1731. "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
  1732. charlog_db, "make new char", sd->account_id, char_id, slot, esc_name, str, agi, vit, int_, dex, luk, hair_style, hair_color) )
  1733. Sql_ShowDebug(inter->sql_handle);
  1734. }
  1735.  
  1736. //Give the char the default items
  1737. for (k = 0; k < ARRAYLENGTH(start_items) && start_items[k] != 0; k += 4) {
  1738. // FIXME: How to define if an item is stackable without having to lookup itemdb? [panikon]
  1739. if( start_items[k+2] == 1 )
  1740. {
  1741. if( SQL_ERROR == SQL->Query(inter->sql_handle,
  1742. "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')",
  1743. inventory_db, char_id, start_items[k], start_items[k + 1], 1) )
  1744. Sql_ShowDebug(inter->sql_handle);
  1745. }
  1746. else if( start_items[k+2] == 0 )
  1747. {
  1748. // Non-stackable items should have their own entries (issue: 7279)
  1749. for( l = 0; l < start_items[k+1]; l++ )
  1750. {
  1751. if( SQL_ERROR == SQL->Query(inter->sql_handle,
  1752. "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`, `equip`) VALUES ('%d', '%d', '%d', '%d', '%d')",
  1753. inventory_db, char_id, start_items[k], 1, 1, start_items[k+3])
  1754. )
  1755. Sql_ShowDebug(inter->sql_handle);
  1756. }
  1757. }
  1758. }
  1759.  
  1760. ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, slot, name);
  1761. return char_id;
  1762. }
  1763.  
  1764. /*----------------------------------------------------------------------------------------------------------*/
  1765. /* Divorce Players */
  1766. /*----------------------------------------------------------------------------------------------------------*/
  1767. int char_divorce_char_sql(int partner_id1, int partner_id2)
  1768. {
  1769. unsigned char buf[64];
  1770.  
  1771. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `partner_id`='0' WHERE `char_id`='%d' OR `char_id`='%d' LIMIT 2", char_db, partner_id1, partner_id2) )
  1772. Sql_ShowDebug(inter->sql_handle);
  1773. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE (`nameid`='%d' OR `nameid`='%d') AND (`char_id`='%d' OR `char_id`='%d') LIMIT 2", inventory_db, WEDDING_RING_M, WEDDING_RING_F, partner_id1, partner_id2) )
  1774. Sql_ShowDebug(inter->sql_handle);
  1775.  
  1776. WBUFW(buf,0) = 0x2b12;
  1777. WBUFL(buf,2) = partner_id1;
  1778. WBUFL(buf,6) = partner_id2;
  1779. mapif->sendall(buf,10);
  1780.  
  1781. return 0;
  1782. }
  1783.  
  1784. /*----------------------------------------------------------------------------------------------------------*/
  1785. /* Delete char - davidsiaw */
  1786. /*----------------------------------------------------------------------------------------------------------*/
  1787. /* Returns 0 if successful
  1788. * Returns < 0 for error
  1789. */
  1790. int char_delete_char_sql(int char_id)
  1791. {
  1792. char name[NAME_LENGTH];
  1793. char esc_name[NAME_LENGTH*2+1]; //Name needs be escaped.
  1794. int account_id, party_id, guild_id, hom_id, base_level, partner_id, father_id, mother_id, elemental_id;
  1795. char *data;
  1796. size_t len;
  1797.  
  1798. if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `name`,`account_id`,`party_id`,`guild_id`,`base_level`,`homun_id`,`partner_id`,`father`,`mother`,`elemental_id` FROM `%s` WHERE `char_id`='%d'", char_db, char_id))
  1799. Sql_ShowDebug(inter->sql_handle);
  1800.  
  1801. if( SQL_SUCCESS != SQL->NextRow(inter->sql_handle) )
  1802. {
  1803. ShowError("chr->delete_char_sql: Unable to fetch character data, deletion aborted.\n");
  1804. SQL->FreeResult(inter->sql_handle);
  1805. return -1;
  1806. }
  1807.  
  1808. SQL->GetData(inter->sql_handle, 0, &data, &len); safestrncpy(name, data, NAME_LENGTH);
  1809. SQL->GetData(inter->sql_handle, 1, &data, NULL); account_id = atoi(data);
  1810. SQL->GetData(inter->sql_handle, 2, &data, NULL); party_id = atoi(data);
  1811. SQL->GetData(inter->sql_handle, 3, &data, NULL); guild_id = atoi(data);
  1812. SQL->GetData(inter->sql_handle, 4, &data, NULL); base_level = atoi(data);
  1813. SQL->GetData(inter->sql_handle, 5, &data, NULL); hom_id = atoi(data);
  1814. SQL->GetData(inter->sql_handle, 6, &data, NULL); partner_id = atoi(data);
  1815. SQL->GetData(inter->sql_handle, 7, &data, NULL); father_id = atoi(data);
  1816. SQL->GetData(inter->sql_handle, 8, &data, NULL); mother_id = atoi(data);
  1817. SQL->GetData(inter->sql_handle, 9, &data, NULL);
  1818. elemental_id = atoi(data);
  1819.  
  1820. SQL->EscapeStringLen(inter->sql_handle, esc_name, name, min(len, NAME_LENGTH));
  1821. SQL->FreeResult(inter->sql_handle);
  1822.  
  1823. //check for config char del condition [Lupus]
  1824. // TODO: Move this out to packet processing (0x68/0x1fb).
  1825. if( ( char_del_level > 0 && base_level >= char_del_level )
  1826. || ( char_del_level < 0 && base_level <= -char_del_level )
  1827. ) {
  1828. ShowInfo("Char deletion aborted: %s, BaseLevel: %i\n", name, base_level);
  1829. return -1;
  1830. }
  1831.  
  1832. /* Divorce [Wizputer] */
  1833. if( partner_id )
  1834. chr->divorce_char_sql(char_id, partner_id);
  1835.  
  1836. /* De-addopt [Zephyrus] */
  1837. if( father_id || mother_id )
  1838. { // Char is Baby
  1839. unsigned char buf[64];
  1840.  
  1841. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `child`='0' WHERE `char_id`='%d' OR `char_id`='%d'", char_db, father_id, mother_id) )
  1842. Sql_ShowDebug(inter->sql_handle);
  1843. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `id` = '410'AND (`char_id`='%d' OR `char_id`='%d')", skill_db, father_id, mother_id) )
  1844. Sql_ShowDebug(inter->sql_handle);
  1845.  
  1846. WBUFW(buf,0) = 0x2b25;
  1847. WBUFL(buf,2) = father_id;
  1848. WBUFL(buf,6) = mother_id;
  1849. WBUFL(buf,10) = char_id; // Baby
  1850. mapif->sendall(buf,14);
  1851. }
  1852.  
  1853. //Make the character leave the party [Skotlex]
  1854. if (party_id)
  1855. inter_party->leave(party_id, account_id, char_id);
  1856.  
  1857. /* delete char's pet */
  1858. //Delete the hatched pet if you have one...
  1859. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d' AND `incubate` = '0'", pet_db, char_id) )
  1860. Sql_ShowDebug(inter->sql_handle);
  1861.  
  1862. //Delete all pets that are stored in eggs (inventory + cart)
  1863. if( SQL_ERROR == SQL->Query(inter->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) )
  1864. Sql_ShowDebug(inter->sql_handle);
  1865. if( SQL_ERROR == SQL->Query(inter->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) )
  1866. Sql_ShowDebug(inter->sql_handle);
  1867.  
  1868. /* remove homunculus */
  1869. if( hom_id )
  1870. mapif->homunculus_delete(hom_id);
  1871.  
  1872. /* remove elemental */
  1873. if (elemental_id)
  1874. mapif->elemental_delete(elemental_id);
  1875.  
  1876. /* remove mercenary data */
  1877. inter_mercenary->owner_delete(char_id);
  1878.  
  1879. /* delete char's friends list */
  1880. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d'", friend_db, char_id) )
  1881. Sql_ShowDebug(inter->sql_handle);
  1882.  
  1883. /* delete char from other's friend list */
  1884. //NOTE: Won't this cause problems for people who are already online? [Skotlex]
  1885. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `friend_id` = '%d'", friend_db, char_id) )
  1886. Sql_ShowDebug(inter->sql_handle);
  1887.  
  1888. #ifdef HOTKEY_SAVING
  1889. /* delete hotkeys */
  1890. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", hotkey_db, char_id) )
  1891. Sql_ShowDebug(inter->sql_handle);
  1892. #endif
  1893.  
  1894. /* delete inventory */
  1895. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", inventory_db, char_id) )
  1896. Sql_ShowDebug(inter->sql_handle);
  1897.  
  1898. /* delete cart inventory */
  1899. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", cart_db, char_id) )
  1900. Sql_ShowDebug(inter->sql_handle);
  1901.  
  1902. /* delete memo areas */
  1903. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", memo_db, char_id) )
  1904. Sql_ShowDebug(inter->sql_handle);
  1905.  
  1906. /* delete character registry */
  1907. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_reg_str_db, char_id) )
  1908. Sql_ShowDebug(inter->sql_handle);
  1909. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_reg_num_db, char_id) )
  1910. Sql_ShowDebug(inter->sql_handle);
  1911.  
  1912. /* delete skills */
  1913. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", skill_db, char_id) )
  1914. Sql_ShowDebug(inter->sql_handle);
  1915.  
  1916. /* delete mails (only received) */
  1917. if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `dest_id`='%d'", mail_db, char_id))
  1918. Sql_ShowDebug(inter->sql_handle);
  1919.  
  1920. #ifdef ENABLE_SC_SAVING
  1921. /* status changes */
  1922. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, account_id, char_id) )
  1923. Sql_ShowDebug(inter->sql_handle);
  1924. #endif
  1925.  
  1926. /* delete character */
  1927. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_db, char_id) )
  1928. Sql_ShowDebug(inter->sql_handle);
  1929. else if( log_char ) {
  1930. if( SQL_ERROR == SQL->Query(inter->sql_handle,
  1931. "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `char_msg`, `name`)"
  1932. " VALUES (NOW(), '%d', '%d', '%d', 'Deleted character', '%s')",
  1933. charlog_db, account_id, char_id, 0, esc_name) )
  1934. Sql_ShowDebug(inter->sql_handle);
  1935. }
  1936.  
  1937. /* No need as we used inter_guild->leave [Skotlex]
  1938. // Also delete info from guildtables.
  1939. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", guild_member_db, char_id) )
  1940. Sql_ShowDebug(inter->sql_handle);
  1941. */
  1942.  
  1943. if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `guild_id` FROM `%s` WHERE `char_id` = '%d'", guild_db, char_id) )
  1944. Sql_ShowDebug(inter->sql_handle);
  1945. else if( SQL->NumRows(inter->sql_handle) > 0 )
  1946. mapif->parse_BreakGuild(0,guild_id);
  1947. else if( guild_id )
  1948. inter_guild->leave(guild_id, account_id, char_id);// Leave your guild.
  1949. return 0;
  1950. }
  1951.  
  1952. //---------------------------------------------------------------------
  1953. // This function return the number of online players in all map-servers
  1954. //---------------------------------------------------------------------
  1955. int char_count_users(void)
  1956. {
  1957. int i, users;
  1958.  
  1959. users = 0;
  1960. for(i = 0; i < ARRAYLENGTH(chr->server); i++) {
  1961. if (chr->server[i].fd > 0) {
  1962. users += chr->server[i].users;
  1963. }
  1964. }
  1965. return users;
  1966. }
  1967.  
  1968. // Writes char data to the buffer in the format used by the client.
  1969. // Used in packets 0x6b (chars info) and 0x6d (new char info)
  1970. // Returns the size
  1971. #define MAX_CHAR_BUF 150 //Max size (for WFIFOHEAD calls)
  1972. int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) {
  1973. unsigned short offset = 0;
  1974. uint8* buf;
  1975.  
  1976. if( buffer == NULL || p == NULL )
  1977. return 0;
  1978.  
  1979. buf = WBUFP(buffer,0);
  1980. WBUFL(buf,0) = p->char_id;
  1981. WBUFL(buf,4) = min(p->base_exp, INT32_MAX);
  1982. WBUFL(buf,8) = p->zeny;
  1983. WBUFL(buf,12) = min(p->job_exp, INT32_MAX);
  1984. WBUFL(buf,16) = p->job_level;
  1985. WBUFL(buf,20) = 0; // probably opt1
  1986. WBUFL(buf,24) = 0; // probably opt2
  1987. WBUFL(buf,28) = (p->option &~ 0x40);
  1988. WBUFL(buf,32) = p->karma;
  1989. WBUFL(buf,36) = p->manner;
  1990. WBUFW(buf,40) = min(p->status_point, INT16_MAX);
  1991. #if PACKETVER > 20081217
  1992. WBUFL(buf,42) = p->hp;
  1993. WBUFL(buf,46) = p->max_hp;
  1994. offset+=4;
  1995. buf = WBUFP(buffer,offset);
  1996. #else
  1997. WBUFW(buf,42) = min(p->hp, INT16_MAX);
  1998. WBUFW(buf,44) = min(p->max_hp, INT16_MAX);
  1999. #endif
  2000. WBUFW(buf,46) = min(p->sp, INT16_MAX);
  2001. WBUFW(buf,48) = min(p->max_sp, INT16_MAX);
  2002. WBUFW(buf,50) = DEFAULT_WALK_SPEED; // p->speed;
  2003. WBUFW(buf,52) = p->class_;
  2004. WBUFW(buf,54) = p->hair;
  2005. #if PACKETVER >= 20141022
  2006. WBUFW(buf,56) = p->body;
  2007. offset+=2;
  2008. buf = WBUFP(buffer,offset);
  2009. #endif
  2010.  
  2011. //When the weapon is sent and your option is riding, the client crashes on login!?
  2012. // FIXME[Haru]: is OPTION_HANBOK intended to be part of this list? And if it is, should the list also include other OPTION_ costumes?
  2013. WBUFW(buf,56) = p->option&(OPTION_RIDING|OPTION_DRAGON|OPTION_WUG|OPTION_WUGRIDER|OPTION_MADOGEAR|OPTION_HANBOK) ? 0 : p->weapon;
  2014.  
  2015. WBUFW(buf,58) = p->base_level;
  2016. WBUFW(buf,60) = min(p->skill_point, INT16_MAX);
  2017. WBUFW(buf,62) = p->head_bottom;
  2018. WBUFW(buf,64) = p->shield;
  2019. WBUFW(buf,66) = p->head_top;
  2020. WBUFW(buf,68) = p->head_mid;
  2021. WBUFW(buf,70) = p->hair_color;
  2022. WBUFW(buf,72) = p->clothes_color;
  2023. memcpy(WBUFP(buf,74), p->name, NAME_LENGTH);
  2024. WBUFB(buf,98) = min(p->str, UINT8_MAX);
  2025. WBUFB(buf,99) = min(p->agi, UINT8_MAX);
  2026. WBUFB(buf,100) = min(p->vit, UINT8_MAX);
  2027. WBUFB(buf,101) = min(p->int_, UINT8_MAX);
  2028. WBUFB(buf,102) = min(p->dex, UINT8_MAX);
  2029. WBUFB(buf,103) = min(p->luk, UINT8_MAX);
  2030. WBUFW(buf,104) = p->slot;
  2031. #if PACKETVER >= 20061023
  2032. WBUFW(buf,106) = ( p->rename > 0 ) ? 0 : 1;
  2033. offset += 2;
  2034. #endif
  2035. #if (PACKETVER >= 20100720 && PACKETVER <= 20100727) || PACKETVER >= 20100803
  2036. mapindex->getmapname_ext(mapindex_id2name(p->last_point.map), (char*)WBUFP(buf,108));
  2037. offset += MAP_NAME_LENGTH_EXT;
  2038. #endif
  2039. #if PACKETVER >= 20100803
  2040. WBUFL(buf,124) = (int)p->delete_date;
  2041. offset += 4;
  2042. #endif
  2043. #if PACKETVER >= 20110111
  2044. WBUFL(buf,128) = p->robe;
  2045. offset += 4;
  2046. #endif
  2047. #if PACKETVER != 20111116 //2011-11-16 wants 136, ask gravity.
  2048. #if PACKETVER >= 20110928
  2049. WBUFL(buf,132) = ( p->slotchange > 0 ) ? 1 : 0; // change slot feature (0 = disabled, otherwise enabled)
  2050. offset += 4;
  2051. #endif
  2052. #if PACKETVER >= 20111025
  2053. WBUFL(buf,136) = ( p->rename > 0 ) ? 1 : 0; // (0 = disabled, otherwise displays "Add-Ons" sidebar)
  2054. offset += 4;
  2055. #endif
  2056. #if PACKETVER >= 20141016
  2057. WBUFB(buf,140) = p->sex;// sex - (0 = female, 1 = male, 99 = logindefined)
  2058. offset += 1;
  2059. #endif
  2060. #endif
  2061.  
  2062. return 106+offset;
  2063. }
  2064.  
  2065. /* Made Possible by Yommy~! <3 */
  2066. void char_mmo_char_send099d(int fd, struct char_session_data *sd) {
  2067. WFIFOHEAD(fd,4 + (MAX_CHARS*MAX_CHAR_BUF));
  2068. WFIFOW(fd,0) = 0x99d;
  2069. WFIFOW(fd,2) = chr->mmo_chars_fromsql(sd, WFIFOP(fd,4)) + 4;
  2070. WFIFOSET(fd,WFIFOW(fd,2));
  2071. }
  2072.  
  2073. /* Sends character ban list */
  2074. /* Made Possible by Yommy~! <3 */
  2075. void char_mmo_char_send_ban_list(int fd, struct char_session_data *sd) {
  2076. int i;
  2077. time_t now = time(NULL);
  2078.  
  2079. nullpo_retv(sd);
  2080. ARR_FIND(0, MAX_CHARS, i, sd->unban_time[i]);
  2081. if( i != MAX_CHARS ) {
  2082. int c;
  2083.  
  2084. WFIFOHEAD(fd, 4 + (MAX_CHARS*24));
  2085.  
  2086. WFIFOW(fd, 0) = 0x20d;
  2087.  
  2088. for(i = 0, c = 0; i < MAX_CHARS; i++) {
  2089. if( sd->unban_time[i] ) {
  2090. timestamp2string((char*)WFIFOP(fd,8 + (28*c)), 20, sd->unban_time[i], "%Y-%m-%d %H:%M:%S");
  2091.  
  2092. if( sd->unban_time[i] > now )
  2093. WFIFOL(fd, 4 + (24*c)) = sd->found_char[i];
  2094. else {
  2095. /* reset -- client keeps this information even if you logout so we need to clear */
  2096. WFIFOL(fd, 4 + (24*c)) = 0;
  2097. /* also update on mysql */
  2098. sd->unban_time[i] = 0;
  2099. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `unban_time`='0' WHERE `char_id`='%d' LIMIT 1", char_db, sd->found_char[i]) )
  2100. Sql_ShowDebug(inter->sql_handle);
  2101. }
  2102. c++;
  2103. }
  2104. }
  2105.  
  2106. WFIFOW(fd, 2) = 4 + (24*c);
  2107.  
  2108. WFIFOSET(fd, WFIFOW(fd, 2));
  2109. }
  2110. }
  2111.  
  2112. //----------------------------------------
  2113. // [Ind/Hercules] notify client about charselect window data
  2114. //----------------------------------------
  2115. void char_mmo_char_send_slots_info(int fd, struct char_session_data* sd) {
  2116. nullpo_retv(sd);
  2117. WFIFOHEAD(fd,29);
  2118. WFIFOW(fd,0) = 0x82d;
  2119. WFIFOW(fd,2) = 29;
  2120. WFIFOB(fd,4) = sd->char_slots;
  2121. WFIFOB(fd,5) = MAX_CHARS - sd->char_slots;
  2122. WFIFOB(fd,6) = 0;
  2123. WFIFOB(fd,7) = sd->char_slots;
  2124. WFIFOB(fd,8) = sd->char_slots;
  2125. memset(WFIFOP(fd,9), 0, 20); // unused bytes
  2126. WFIFOSET(fd,29);
  2127. }
  2128. //----------------------------------------
  2129. // Function to send characters to a player
  2130. //----------------------------------------
  2131. int char_mmo_char_send_characters(int fd, struct char_session_data* sd)
  2132. {
  2133. int j, offset = 0;
  2134. nullpo_ret(sd);
  2135. #if PACKETVER >= 20100413
  2136. offset += 3;
  2137. #endif
  2138. if (save_log)
  2139. ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id);
  2140.  
  2141. j = 24 + offset; // offset
  2142. WFIFOHEAD(fd,j + MAX_CHARS*MAX_CHAR_BUF);
  2143. WFIFOW(fd,0) = 0x6b;
  2144. #if PACKETVER >= 20100413
  2145. WFIFOB(fd,4) = MAX_CHARS; // Max slots.
  2146. WFIFOB(fd,5) = sd->char_slots; // Available slots. (aka PremiumStartSlot)
  2147. WFIFOB(fd,6) = MAX_CHARS; // Premium slots. AKA any existent chars past sd->char_slots but within MAX_CHARS will show a 'Premium Service' in red
  2148. #endif
  2149. memset(WFIFOP(fd,4 + offset), 0, 20); // unknown bytes
  2150. j+=chr->mmo_chars_fromsql(sd, WFIFOP(fd,j));
  2151. WFIFOW(fd,2) = j; // packet len
  2152. WFIFOSET(fd,j);
  2153.  
  2154. return 0;
  2155. }
  2156.  
  2157. int char_char_married(int pl1, int pl2)
  2158. {
  2159. if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `partner_id` FROM `%s` WHERE `char_id` = '%d'", char_db, pl1) )
  2160. Sql_ShowDebug(inter->sql_handle);
  2161. else if( SQL_SUCCESS == SQL->NextRow(inter->sql_handle) )
  2162. {
  2163. char* data;
  2164.  
  2165. SQL->GetData(inter->sql_handle, 0, &data, NULL);
  2166. if( pl2 == atoi(data) )
  2167. {
  2168. SQL->FreeResult(inter->sql_handle);
  2169. return 1;
  2170. }
  2171. }
  2172. SQL->FreeResult(inter->sql_handle);
  2173. return 0;
  2174. }
  2175.  
  2176. int char_char_child(int parent_id, int child_id)
  2177. {
  2178. if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `child` FROM `%s` WHERE `char_id` = '%d'", char_db, parent_id) )
  2179. Sql_ShowDebug(inter->sql_handle);
  2180. else if( SQL_SUCCESS == SQL->NextRow(inter->sql_handle) )
  2181. {
  2182. char* data;
  2183.  
  2184. SQL->GetData(inter->sql_handle, 0, &data, NULL);
  2185. if( child_id == atoi(data) )
  2186. {
  2187. SQL->FreeResult(inter->sql_handle);
  2188. return 1;
  2189. }
  2190. }
  2191. SQL->FreeResult(inter->sql_handle);
  2192. return 0;
  2193. }
  2194.  
  2195. int char_char_family(int cid1, int cid2, int cid3)
  2196. {
  2197. if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `char_id`,`partner_id`,`child` FROM `%s` WHERE `char_id` IN ('%d','%d','%d')", char_db, cid1, cid2, cid3) )
  2198. Sql_ShowDebug(inter->sql_handle);
  2199. else while( SQL_SUCCESS == SQL->NextRow(inter->sql_handle) )
  2200. {
  2201. int charid;
  2202. int partnerid;
  2203. int childid;
  2204. char* data;
  2205.  
  2206. SQL->GetData(inter->sql_handle, 0, &data, NULL); charid = atoi(data);
  2207. SQL->GetData(inter->sql_handle, 1, &data, NULL); partnerid = atoi(data);
  2208. SQL->GetData(inter->sql_handle, 2, &data, NULL); childid = atoi(data);
  2209.  
  2210. if( (cid1 == charid && ((cid2 == partnerid && cid3 == childid ) || (cid2 == childid && cid3 == partnerid))) ||
  2211. (cid1 == partnerid && ((cid2 == charid && cid3 == childid ) || (cid2 == childid && cid3 == charid ))) ||
  2212. (cid1 == childid && ((cid2 == charid && cid3 == partnerid) || (cid2 == partnerid && cid3 == charid ))) )
  2213. {
  2214. SQL->FreeResult(inter->sql_handle);
  2215. return childid;
  2216. }
  2217. }
  2218. SQL->FreeResult(inter->sql_handle);
  2219. return 0;
  2220. }
  2221.  
  2222. //----------------------------------------------------------------------
  2223. // Force disconnection of an online player (with account value) by [Yor]
  2224. //----------------------------------------------------------------------
  2225. void char_disconnect_player(int account_id)
  2226. {
  2227. int i;
  2228. struct char_session_data* sd;
  2229.  
  2230. // disconnect player if online on char-server
  2231. ARR_FIND( 0, sockt->fd_max, i, sockt->session[i] && (sd = (struct char_session_data*)sockt->session[i]->session_data) && sd->account_id == account_id );
  2232. if( i < sockt->fd_max )
  2233. sockt->eof(i);
  2234. }
  2235.  
  2236. void char_authfail_fd(int fd, int type)
  2237. {
  2238. WFIFOHEAD(fd,3);
  2239. WFIFOW(fd,0) = 0x81;
  2240. WFIFOB(fd,2) = type;
  2241. WFIFOSET(fd,3);
  2242. }
  2243.  
  2244. void char_request_account_data(int account_id)
  2245. {
  2246. WFIFOHEAD(chr->login_fd,6);
  2247. WFIFOW(chr->login_fd,0) = 0x2716;
  2248. WFIFOL(chr->login_fd,2) = account_id;
  2249. WFIFOSET(chr->login_fd,6);
  2250. }
  2251.  
  2252. static void char_auth_ok(int fd, struct char_session_data *sd)
  2253. {
  2254. struct online_char_data* character;
  2255.  
  2256. nullpo_retv(sd);
  2257.  
  2258. if( (character = (struct online_char_data*)idb_get(chr->online_char_db, sd->account_id)) != NULL ) {
  2259. // check if character is not online already. [Skotlex]
  2260. if (character->server > -1) {
  2261. //Character already online. KICK KICK KICK
  2262. mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); // 2: Already connected to server
  2263. if (character->waiting_disconnect == INVALID_TIMER)
  2264. character->waiting_disconnect = timer->add(timer->gettick()+20000, chr->waiting_disconnect, character->account_id, 0);
  2265. character->pincode_enable = -1;
  2266. chr->authfail_fd(fd, 8);
  2267. return;
  2268. }
  2269. if (character->fd >= 0 && character->fd != fd) {
  2270. //There's already a connection from this account that hasn't picked a char yet.
  2271. chr->authfail_fd(fd, 8);
  2272. return;
  2273. }
  2274. character->fd = fd;
  2275. }
  2276.  
  2277. if (chr->login_fd > 0) {
  2278. chr->request_account_data(sd->account_id);
  2279. }
  2280.  
  2281. // mark session as 'authed'
  2282. sd->auth = true;
  2283.  
  2284. // set char online on charserver
  2285. chr->set_char_charselect(sd->account_id);
  2286.  
  2287. // continues when account data is received...
  2288. }
  2289.  
  2290. void char_ping_login_server(int fd)
  2291. {
  2292. WFIFOHEAD(fd,2);// sends a ping packet to login server (will receive pong 0x2718)
  2293. WFIFOW(fd,0) = 0x2719;
  2294. WFIFOSET(fd,2);
  2295. }
  2296.  
  2297. int char_parse_fromlogin_connection_state(int fd)
  2298. {
  2299. if (RFIFOB(fd,2)) {
  2300. //printf("connect login server error : %d\n", RFIFOB(fd,2));
  2301. ShowError("Can not connect to login-server.\n");
  2302. ShowError("The server communication passwords (default s1/p1) are probably invalid.\n");
  2303. ShowError("Also, please make sure your login db has the correct communication username/passwords and the gender of the account is S.\n");
  2304. ShowError("The communication passwords are set in /conf/map-server.conf and /conf/char-server.conf\n");
  2305. sockt->eof(fd);
  2306. return 1;
  2307. } else {
  2308. ShowStatus("Connected to login-server (connection #%d).\n", fd);
  2309. loginif->on_ready();
  2310. }
  2311. RFIFOSKIP(fd,3);
  2312. return 0;
  2313. }
  2314.  
  2315. // 0 - rejected from server
  2316. //
  2317. void char_auth_error(int fd, unsigned char flag)
  2318. {
  2319. WFIFOHEAD(fd,3);
  2320. WFIFOW(fd,0) = 0x6c;
  2321. WFIFOB(fd,2) = flag;
  2322. WFIFOSET(fd,3);
  2323. }
  2324.  
  2325. void char_parse_fromlogin_auth_state(int fd)
  2326. {
  2327. struct char_session_data* sd = NULL;
  2328. int account_id = RFIFOL(fd,2);
  2329. uint32 login_id1 = RFIFOL(fd,6);
  2330. uint32 login_id2 = RFIFOL(fd,10);
  2331. uint8 sex = RFIFOB(fd,14);
  2332. uint8 result = RFIFOB(fd,15);
  2333. int request_id = RFIFOL(fd,16);
  2334. uint32 version = RFIFOL(fd,20);
  2335. uint8 clienttype = RFIFOB(fd,24);
  2336. int group_id = RFIFOL(fd,25);
  2337. unsigned int expiration_time = RFIFOL(fd, 29);
  2338. RFIFOSKIP(fd,33);
  2339.  
  2340. if (sockt->session_is_active(request_id) && (sd=(struct char_session_data*)sockt->session[request_id]->session_data) &&
  2341. !sd->auth && sd->account_id == account_id && sd->login_id1 == login_id1 && sd->login_id2 == login_id2 && sd->sex == sex )
  2342. {
  2343. int client_fd = request_id;
  2344. sd->version = version;
  2345. sd->clienttype = clienttype;
  2346. switch( result ) {
  2347. case 0:// ok
  2348. /* restrictions apply */
  2349. if( chr->server_type == CST_MAINTENANCE && group_id < char_maintenance_min_group_id ) {
  2350. chr->auth_error(client_fd, 0);
  2351. break;
  2352. }
  2353. /* the client will already deny this request, this check is to avoid someone bypassing. */
  2354. if( chr->server_type == CST_PAYING && (time_t)expiration_time < time(NULL) ) {
  2355. chr->auth_error(client_fd, 0);
  2356. break;
  2357. }
  2358. chr->auth_ok(client_fd, sd);
  2359. break;
  2360. case 1:// auth failed
  2361. chr->auth_error(client_fd, 0);
  2362. break;
  2363. }
  2364. }
  2365. }
  2366.  
  2367. void char_parse_fromlogin_account_data(int fd)
  2368. {
  2369. struct char_session_data* sd = (struct char_session_data*)sockt->session[fd]->session_data;
  2370. int i;
  2371. // find the authenticated session with this account id
  2372. ARR_FIND( 0, sockt->fd_max, i, sockt->session[i] && (sd = (struct char_session_data*)sockt->session[i]->session_data) && sd->auth && sd->account_id == RFIFOL(fd,2) );
  2373. if( i < sockt->fd_max ) {
  2374. memcpy(sd->email, RFIFOP(fd,6), 40);
  2375. sd->expiration_time = (time_t)RFIFOL(fd,46);
  2376. sd->group_id = RFIFOB(fd,50);
  2377. sd->char_slots = RFIFOB(fd,51);
  2378. if( sd->char_slots > MAX_CHARS ) {
  2379. ShowError("Account '%d' `character_slots` column is higher than supported MAX_CHARS (%d), update MAX_CHARS in mmo.h! capping to MAX_CHARS...\n",sd->account_id,sd->char_slots);
  2380. sd->char_slots = MAX_CHARS;/* cap to maximum */
  2381. } else if ( sd->char_slots <= 0 )/* no value aka 0 in sql */
  2382. sd->char_slots = MAX_CHARS;/* cap to maximum */
  2383. safestrncpy(sd->birthdate, (const char*)RFIFOP(fd,52), sizeof(sd->birthdate));
  2384. safestrncpy(sd->pincode, (const char*)RFIFOP(fd,63), sizeof(sd->pincode));
  2385. sd->pincode_change = RFIFOL(fd,68);
  2386. // continued from chr->auth_ok...
  2387. if( (max_connect_user == 0 && sd->group_id != gm_allow_group) ||
  2388. ( max_connect_user > 0 && chr->count_users() >= max_connect_user && sd->group_id != gm_allow_group ) ) {
  2389. // refuse connection (over populated)
  2390. chr->auth_error(i, 0);
  2391. } else {
  2392. // send characters to player
  2393. #if PACKETVER >= 20130000
  2394. chr->mmo_char_send_slots_info(i, sd);
  2395. chr->mmo_char_send_characters(i, sd);
  2396. #else
  2397. chr->mmo_char_send_characters(i, sd);
  2398. #endif
  2399. #if PACKETVER >= 20060819
  2400. chr->mmo_char_send_ban_list(i, sd);
  2401. #endif
  2402. #if PACKETVER >= 20110309
  2403. pincode->handle(i, sd);
  2404. #endif
  2405. }
  2406. }
  2407. RFIFOSKIP(fd,72);
  2408. }
  2409.  
  2410. void char_parse_fromlogin_login_pong(int fd)
  2411. {
  2412. RFIFOSKIP(fd,2);
  2413. if (sockt->session[fd])
  2414. sockt->session[fd]->flag.ping = 0;
  2415. }
  2416.  
  2417. void char_changesex(int account_id, int sex)
  2418. {
  2419. unsigned char buf[7];
  2420.  
  2421. WBUFW(buf,0) = 0x2b0d;
  2422. WBUFL(buf,2) = account_id;
  2423. WBUFB(buf,6) = sex;
  2424. mapif->sendall(buf, 7);
  2425. }
  2426.  
  2427. /**
  2428. * Performs the necessary operations when changing a character's sex, such as
  2429. * correcting the job class and unequipping items, and propagating the
  2430. * information to the guild data.
  2431. *
  2432. * @param sex The new sex (SEX_MALE or SEX_FEMALE).
  2433. * @param acc The character's account ID.
  2434. * @param char_id The character ID.
  2435. * @param class_ The character's current job class.
  2436. * @param guild_id The character's guild ID.
  2437. */
  2438. void char_change_sex_sub(int sex, int acc, int char_id, int class_, int guild_id)
  2439. {
  2440. // job modification
  2441. if (class_ == JOB_BARD || class_ == JOB_DANCER)
  2442. class_ = (sex == SEX_MALE ? JOB_BARD : JOB_DANCER);
  2443. else if (class_ == JOB_CLOWN || class_ == JOB_GYPSY)
  2444. class_ = (sex == SEX_MALE ? JOB_CLOWN : JOB_GYPSY);
  2445. else if (class_ == JOB_BABY_BARD || class_ == JOB_BABY_DANCER)
  2446. class_ = (sex == SEX_MALE ? JOB_BABY_BARD : JOB_BABY_DANCER);
  2447. else if (class_ == JOB_MINSTREL || class_ == JOB_WANDERER)
  2448. class_ = (sex == SEX_MALE ? JOB_MINSTREL : JOB_WANDERER);
  2449. else if (class_ == JOB_MINSTREL_T || class_ == JOB_WANDERER_T)
  2450. class_ = (sex == SEX_MALE ? JOB_MINSTREL_T : JOB_WANDERER_T);
  2451. else if (class_ == JOB_BABY_MINSTREL || class_ == JOB_BABY_WANDERER)
  2452. class_ = (sex == SEX_MALE ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER);
  2453. else if (class_ == JOB_KAGEROU || class_ == JOB_OBORO)
  2454. class_ = (sex == SEX_MALE ? JOB_KAGEROU : JOB_OBORO);
  2455.  
  2456. if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `equip`='0' WHERE `char_id`='%d'", inventory_db, char_id))
  2457. Sql_ShowDebug(inter->sql_handle);
  2458.  
  2459. if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `class`='%d', `weapon`='0', `shield`='0', "
  2460. "`head_top`='0', `head_mid`='0', `head_bottom`='0' WHERE `char_id`='%d'",
  2461. char_db, class_, char_id))
  2462. Sql_ShowDebug(inter->sql_handle);
  2463. if (guild_id) // If there is a guild, update the guild_member data [Skotlex]
  2464. inter_guild->sex_changed(guild_id, acc, char_id, sex);
  2465. }
  2466.  
  2467. int char_parse_fromlogin_changesex_reply(int fd)
  2468. {
  2469. int char_id = 0, class_ = 0, guild_id = 0;
  2470. int i;
  2471. struct char_auth_node *node;
  2472. SqlStmt *stmt;
  2473.  
  2474. int acc = RFIFOL(fd,2);
  2475. int sex = RFIFOB(fd,6);
  2476.  
  2477. RFIFOSKIP(fd,7);
  2478.  
  2479. // This should _never_ happen
  2480. if (acc <= 0) {
  2481. ShowError("Received invalid account id from login server! (aid: %d)\n", acc);
  2482. return 1;
  2483. }
  2484.  
  2485. node = (struct char_auth_node*)idb_get(auth_db, acc);
  2486. if (node != NULL)
  2487. node->sex = sex;
  2488.  
  2489. // get characters
  2490. stmt = SQL->StmtMalloc(inter->sql_handle);
  2491. if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `char_id`,`class`,`guild_id` FROM `%s` WHERE `account_id` = '%d'", char_db, acc)
  2492. || SQL_ERROR == SQL->StmtExecute(stmt)
  2493. ) {
  2494. SqlStmt_ShowDebug(stmt);
  2495. SQL->StmtFree(stmt);
  2496. }
  2497. SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &char_id, 0, NULL, NULL);
  2498. SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &class_, 0, NULL, NULL);
  2499. SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &guild_id, 0, NULL, NULL);
  2500.  
  2501. for (i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i) {
  2502. char_change_sex_sub(sex, acc, char_id, class_, guild_id);
  2503. }
  2504. SQL->StmtFree(stmt);
  2505.  
  2506. // disconnect player if online on char-server
  2507. chr->disconnect_player(acc);
  2508.  
  2509. // notify all mapservers about this change
  2510. chr->changesex(acc, sex);
  2511. return 0;
  2512. }
  2513.  
  2514. void char_parse_fromlogin_account_reg2(int fd)
  2515. {
  2516. //Receive account_reg2 registry, forward to map servers.
  2517. mapif->sendall(RFIFOP(fd, 0), RFIFOW(fd,2));
  2518. RFIFOSKIP(fd, RFIFOW(fd,2));
  2519. }
  2520.  
  2521. void mapif_ban(int id, unsigned int flag, int status)
  2522. {
  2523. // send to all map-servers to disconnect the player
  2524. unsigned char buf[11];
  2525. WBUFW(buf,0) = 0x2b14;
  2526. WBUFL(buf,2) = id;
  2527. WBUFB(buf,6) = flag; // 0: change of status, 1: ban
  2528. WBUFL(buf,7) = status; // status or final date of a banishment
  2529. mapif->sendall(buf, 11);
  2530. }
  2531.  
  2532. void char_parse_fromlogin_ban(int fd)
  2533. {
  2534. mapif->ban(RFIFOL(fd,2), RFIFOB(fd,6), RFIFOL(fd,7));
  2535. // disconnect player if online on char-server
  2536. chr->disconnect_player(RFIFOL(fd,2));
  2537. RFIFOSKIP(fd,11);
  2538. }
  2539.  
  2540. void char_parse_fromlogin_kick(int fd)
  2541. {
  2542. int aid = RFIFOL(fd,2);
  2543. struct online_char_data* character = (struct online_char_data*)idb_get(chr->online_char_db, aid);
  2544. RFIFOSKIP(fd,6);
  2545. if( character != NULL )
  2546. {// account is already marked as online!
  2547. if( character->server > -1 ) {
  2548. //Kick it from the map server it is on.
  2549. mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); // 2: Already connected to server
  2550. if (character->waiting_disconnect == INVALID_TIMER)
  2551. character->waiting_disconnect = timer->add(timer->gettick()+AUTH_TIMEOUT, chr->waiting_disconnect, character->account_id, 0);
  2552. }
  2553. else
  2554. {// Manual kick from char server.
  2555. struct char_session_data *tsd;
  2556. int i;
  2557. ARR_FIND( 0, sockt->fd_max, i, sockt->session[i] && (tsd = (struct char_session_data*)sockt->session[i]->session_data) && tsd->account_id == aid );
  2558. if( i < sockt->fd_max )
  2559. {
  2560. chr->authfail_fd(i, 2);
  2561. sockt->eof(i);
  2562. }
  2563. else // still moving to the map-server
  2564. chr->set_char_offline(-1, aid);
  2565. }
  2566. }
  2567. idb_remove(auth_db, aid);// reject auth attempts from map-server
  2568. }
  2569.  
  2570. void char_update_ip(int fd)
  2571. {
  2572. WFIFOHEAD(fd,6);
  2573. WFIFOW(fd,0) = 0x2736;
  2574. WFIFOL(fd,2) = htonl(chr->ip);
  2575. WFIFOSET(fd,6);
  2576. }
  2577.  
  2578. void char_parse_fromlogin_update_ip(int fd)
  2579. {
  2580. unsigned char buf[2];
  2581. uint32 new_ip = 0;
  2582.  
  2583. WBUFW(buf,0) = 0x2b1e;
  2584. mapif->sendall(buf, 2);
  2585.  
  2586. new_ip = sockt->host2ip(login_ip_str);
  2587. if (new_ip && new_ip != login_ip)
  2588. login_ip = new_ip; //Update login ip, too.
  2589.  
  2590. new_ip = sockt->host2ip(char_ip_str);
  2591. if (new_ip && new_ip != chr->ip) {
  2592. //Update ip.
  2593. chr->ip = new_ip;
  2594. ShowInfo("Updating IP for [%s].\n", char_ip_str);
  2595. // notify login server about the change
  2596. chr->update_ip(fd);
  2597. }
  2598. RFIFOSKIP(fd,2);
  2599. }
  2600.  
  2601. void char_parse_fromlogin_accinfo2_failed(int fd)
  2602. {
  2603. mapif->parse_accinfo2(false, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14),
  2604. NULL, NULL, NULL, NULL, NULL, NULL, NULL, -1, 0, 0);
  2605. RFIFOSKIP(fd,18);
  2606. }
  2607.  
  2608. void char_parse_fromlogin_accinfo2_ok(int fd)
  2609. {
  2610. mapif->parse_accinfo2(true, RFIFOL(fd,167), RFIFOL(fd,171), RFIFOL(fd,175), RFIFOL(fd,179),
  2611. (char*)RFIFOP(fd,2), (char*)RFIFOP(fd,26), (char*)RFIFOP(fd,59),
  2612. (char*)RFIFOP(fd,99), (char*)RFIFOP(fd,119), (char*)RFIFOP(fd,151),
  2613. (char*)RFIFOP(fd,156), RFIFOL(fd,115), RFIFOL(fd,143), RFIFOL(fd,147));
  2614. RFIFOSKIP(fd,183);
  2615. }
  2616.  
  2617. int char_parse_fromlogin(int fd) {
  2618. // only process data from the login-server
  2619. if( fd != chr->login_fd ) {
  2620. ShowDebug("chr->parse_fromlogin: Disconnecting invalid session #%d (is not the login-server)\n", fd);
  2621. sockt->close(fd);
  2622. return 0;
  2623. }
  2624.  
  2625. if( sockt->session[fd]->flag.eof ) {
  2626. sockt->close(fd);
  2627. chr->login_fd = -1;
  2628. loginif->on_disconnect();
  2629. return 0;
  2630. } else if ( sockt->session[fd]->flag.ping ) {/* we've reached stall time */
  2631. if( DIFF_TICK(sockt->last_tick, sockt->session[fd]->rdata_tick) > (sockt->stall_time * 2) ) {/* we can't wait any longer */
  2632. sockt->eof(fd);
  2633. return 0;
  2634. } else if( sockt->session[fd]->flag.ping != 2 ) { /* we haven't sent ping out yet */
  2635. chr->ping_login_server(fd);
  2636. sockt->session[fd]->flag.ping = 2;
  2637. }
  2638. }
  2639.  
  2640. while(RFIFOREST(fd) >= 2) {
  2641. uint16 command = RFIFOW(fd,0);
  2642.  
  2643. if (VECTOR_LENGTH(HPM->packets[hpParse_FromLogin]) > 0) {
  2644. int result = HPM->parse_packets(fd,hpParse_FromLogin);
  2645. if (result == 1)
  2646. continue;
  2647. if (result == 2)
  2648. return 0;
  2649. }
  2650.  
  2651. switch (command) {
  2652. // acknowledgment of connect-to-loginserver request
  2653. case 0x2711:
  2654. if (RFIFOREST(fd) < 3)
  2655. return 0;
  2656. if (chr->parse_fromlogin_connection_state(fd))
  2657. return 0;
  2658. break;
  2659.  
  2660. // acknowledgment of account authentication request
  2661. case 0x2713:
  2662. if (RFIFOREST(fd) < 33)
  2663. return 0;
  2664. {
  2665. chr->parse_fromlogin_auth_state(fd);
  2666. }
  2667. break;
  2668.  
  2669. case 0x2717: // account data
  2670. {
  2671. if (RFIFOREST(fd) < 72)
  2672. return 0;
  2673. chr->parse_fromlogin_account_data(fd);
  2674. }
  2675. break;
  2676.  
  2677. // login-server alive packet
  2678. case 0x2718:
  2679. if (RFIFOREST(fd) < 2)
  2680. return 0;
  2681. chr->parse_fromlogin_login_pong(fd);
  2682. break;
  2683.  
  2684. // changesex reply
  2685. case 0x2723:
  2686. if (RFIFOREST(fd) < 7)
  2687. return 0;
  2688. {
  2689. if (chr->parse_fromlogin_changesex_reply(fd))
  2690. return 0;
  2691. }
  2692. break;
  2693.  
  2694. // reply to an account_reg2 registry request
  2695. case 0x3804:
  2696. if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
  2697. return 0;
  2698. chr->parse_fromlogin_account_reg2(fd);
  2699. break;
  2700.  
  2701. // State change of account/ban notification (from login-server)
  2702. case 0x2731:
  2703. if (RFIFOREST(fd) < 11)
  2704. return 0;
  2705. chr->parse_fromlogin_ban(fd);
  2706. break;
  2707.  
  2708. // Login server request to kick a character out. [Skotlex]
  2709. case 0x2734:
  2710. if (RFIFOREST(fd) < 6)
  2711. return 0;
  2712. {
  2713. chr->parse_fromlogin_kick(fd);
  2714. }
  2715. break;
  2716.  
  2717. // ip address update signal from login server
  2718. case 0x2735:
  2719. {
  2720. chr->parse_fromlogin_update_ip(fd);
  2721. }
  2722. break;
  2723.  
  2724. case 0x2736: // Failed accinfo lookup to forward to mapserver
  2725. if (RFIFOREST(fd) < 18)
  2726. return 0;
  2727.  
  2728. chr->parse_fromlogin_accinfo2_failed(fd);
  2729. break;
  2730.  
  2731. case 0x2737: // Successful accinfo lookup to forward to mapserver
  2732. if (RFIFOREST(fd) < 183)
  2733. return 0;
  2734.  
  2735. chr->parse_fromlogin_accinfo2_ok(fd);
  2736. break;
  2737.  
  2738. default:
  2739. ShowError("Unknown packet 0x%04x received from login-server, disconnecting.\n", command);
  2740. sockt->eof(fd);
  2741. return 0;
  2742. }
  2743. }
  2744.  
  2745. RFIFOFLUSH(fd);
  2746. return 0;
  2747. }
  2748.  
  2749. int char_request_accreg2(int account_id, int char_id)
  2750. {
  2751. if (chr->login_fd > 0) {
  2752. WFIFOHEAD(chr->login_fd,10);
  2753. WFIFOW(chr->login_fd,0) = 0x272e;
  2754. WFIFOL(chr->login_fd,2) = account_id;
  2755. WFIFOL(chr->login_fd,6) = char_id;
  2756. WFIFOSET(chr->login_fd,10);
  2757. return 1;
  2758. }
  2759. return 0;
  2760. }
  2761.  
  2762. /**
  2763. * Handles global account reg saving that continues with chr->global_accreg_to_login_add and global_accreg_to_send
  2764. **/
  2765. void char_global_accreg_to_login_start (int account_id, int char_id) {
  2766. WFIFOHEAD(chr->login_fd, 60000 + 300);
  2767. WFIFOW(chr->login_fd,0) = 0x2728;
  2768. WFIFOW(chr->login_fd,2) = 14;
  2769. WFIFOL(chr->login_fd,4) = account_id;
  2770. WFIFOL(chr->login_fd,8) = char_id;
  2771. WFIFOW(chr->login_fd,12) = 0;/* count */
  2772. }
  2773.  
  2774. /**
  2775. * Completes global account reg saving that starts chr->global_accreg_to_login_start and continues with chr->global_accreg_to_login_add
  2776. **/
  2777. void char_global_accreg_to_login_send (void) {
  2778. WFIFOSET(chr->login_fd, WFIFOW(chr->login_fd,2));
  2779. }
  2780.  
  2781. /**
  2782. * Handles global account reg saving that starts chr->global_accreg_to_login_start and ends with global_accreg_to_send
  2783. **/
  2784. void char_global_accreg_to_login_add (const char *key, unsigned int index, intptr_t val, bool is_string) {
  2785. int nlen = WFIFOW(chr->login_fd, 2);
  2786. size_t len = strlen(key)+1;
  2787.  
  2788. WFIFOB(chr->login_fd, nlen) = (unsigned char)len;/* won't be higher; the column size is 32 */
  2789. nlen += 1;
  2790.  
  2791. safestrncpy((char*)WFIFOP(chr->login_fd,nlen), key, len);
  2792. nlen += len;
  2793.  
  2794. WFIFOL(chr->login_fd, nlen) = index;
  2795. nlen += 4;
  2796.  
  2797. if( is_string ) {
  2798. WFIFOB(chr->login_fd, nlen) = val ? 2 : 3;
  2799. nlen += 1;
  2800.  
  2801. if( val ) {
  2802. char *sval = (char*)val;
  2803. len = strlen(sval)+1;
  2804.  
  2805. WFIFOB(chr->login_fd, nlen) = (unsigned char)len;/* won't be higher; the column size is 254 */
  2806. nlen += 1;
  2807.  
  2808. safestrncpy((char*)WFIFOP(chr->login_fd,nlen), sval, len);
  2809. nlen += len;
  2810. }
  2811. } else {
  2812. WFIFOB(chr->login_fd, nlen) = val ? 0 : 1;
  2813. nlen += 1;
  2814.  
  2815. if( val ) {
  2816. WFIFOL(chr->login_fd, nlen) = (int)val;
  2817. nlen += 4;
  2818. }
  2819. }
  2820.  
  2821. WFIFOW(chr->login_fd,12) += 1;
  2822.  
  2823. WFIFOW(chr->login_fd, 2) = nlen;
  2824. if( WFIFOW(chr->login_fd, 2) > 60000 ) {
  2825. int account_id = WFIFOL(chr->login_fd,4), char_id = WFIFOL(chr->login_fd,8);
  2826. chr->global_accreg_to_login_send();
  2827. chr->global_accreg_to_login_start(account_id,char_id);/* prepare next */
  2828. }
  2829. }
  2830.  
  2831. void char_read_fame_list(void) {
  2832. int i;
  2833. char* data;
  2834. size_t len;
  2835.  
  2836. // Empty ranking lists
  2837. memset(smith_fame_list, 0, sizeof(smith_fame_list));
  2838. memset(chemist_fame_list, 0, sizeof(chemist_fame_list));
  2839. memset(taekwon_fame_list, 0, sizeof(taekwon_fame_list));
  2840. // Build Blacksmith ranking list
  2841. if( SQL_ERROR == SQL->Query(inter->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) )
  2842. Sql_ShowDebug(inter->sql_handle);
  2843. for( i = 0; i < fame_list_size_smith && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i )
  2844. {
  2845. // char_id
  2846. SQL->GetData(inter->sql_handle, 0, &data, NULL);
  2847. smith_fame_list[i].id = atoi(data);
  2848. // fame
  2849. SQL->GetData(inter->sql_handle, 1, &data, &len);
  2850. smith_fame_list[i].fame = atoi(data);
  2851. // name
  2852. SQL->GetData(inter->sql_handle, 2, &data, &len);
  2853. memcpy(smith_fame_list[i].name, data, min(len, NAME_LENGTH));
  2854. }
  2855. // Build Alchemist ranking list
  2856. if( SQL_ERROR == SQL->Query(inter->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) )
  2857. Sql_ShowDebug(inter->sql_handle);
  2858. for( i = 0; i < fame_list_size_chemist && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i )
  2859. {
  2860. // char_id
  2861. SQL->GetData(inter->sql_handle, 0, &data, NULL);
  2862. chemist_fame_list[i].id = atoi(data);
  2863. // fame
  2864. SQL->GetData(inter->sql_handle, 1, &data, &len);
  2865. chemist_fame_list[i].fame = atoi(data);
  2866. // name
  2867. SQL->GetData(inter->sql_handle, 2, &data, &len);
  2868. memcpy(chemist_fame_list[i].name, data, min(len, NAME_LENGTH));
  2869. }
  2870. // Build Taekwon ranking list
  2871. if( SQL_ERROR == SQL->Query(inter->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) )
  2872. Sql_ShowDebug(inter->sql_handle);
  2873. for( i = 0; i < fame_list_size_taekwon && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i )
  2874. {
  2875. // char_id
  2876. SQL->GetData(inter->sql_handle, 0, &data, NULL);
  2877. taekwon_fame_list[i].id = atoi(data);
  2878. // fame
  2879. SQL->GetData(inter->sql_handle, 1, &data, &len);
  2880. taekwon_fame_list[i].fame = atoi(data);
  2881. // name
  2882. SQL->GetData(inter->sql_handle, 2, &data, &len);
  2883. memcpy(taekwon_fame_list[i].name, data, min(len, NAME_LENGTH));
  2884. }
  2885. SQL->FreeResult(inter->sql_handle);
  2886. }
  2887.  
  2888. // Send map-servers the fame ranking lists
  2889. int char_send_fame_list(int fd) {
  2890. int i, len = 8;
  2891. unsigned char buf[32000];
  2892.  
  2893. WBUFW(buf,0) = 0x2b1b;
  2894.  
  2895. for(i = 0; i < fame_list_size_smith && smith_fame_list[i].id; i++) {
  2896. memcpy(WBUFP(buf, len), &smith_fame_list[i], sizeof(struct fame_list));
  2897. len += sizeof(struct fame_list);
  2898. }
  2899. // add blacksmith's block length
  2900. WBUFW(buf, 6) = len;
  2901.  
  2902. for(i = 0; i < fame_list_size_chemist && chemist_fame_list[i].id; i++) {
  2903. memcpy(WBUFP(buf, len), &chemist_fame_list[i], sizeof(struct fame_list));
  2904. len += sizeof(struct fame_list);
  2905. }
  2906. // add alchemist's block length
  2907. WBUFW(buf, 4) = len;
  2908.  
  2909. for(i = 0; i < fame_list_size_taekwon && taekwon_fame_list[i].id; i++) {
  2910. memcpy(WBUFP(buf, len), &taekwon_fame_list[i], sizeof(struct fame_list));
  2911. len += sizeof(struct fame_list);
  2912. }
  2913. // add total packet length
  2914. WBUFW(buf, 2) = len;
  2915.  
  2916. if (fd != -1)
  2917. mapif->send(fd, buf, len);
  2918. else
  2919. mapif->sendall(buf, len);
  2920.  
  2921. return 0;
  2922. }
  2923.  
  2924. void char_update_fame_list(int type, int index, int fame) {
  2925. unsigned char buf[8];
  2926. WBUFW(buf,0) = 0x2b22;
  2927. WBUFB(buf,2) = type;
  2928. WBUFB(buf,3) = index;
  2929. WBUFL(buf,4) = fame;
  2930. mapif->sendall(buf, 8);
  2931. }
  2932.  
  2933. //Loads a character's name and stores it in the buffer given (must be NAME_LENGTH in size) and not NULL
  2934. //Returns 1 on found, 0 on not found (buffer is filled with Unknown char name)
  2935. int char_loadName(int char_id, char* name)
  2936. {
  2937. char* data;
  2938. size_t len;
  2939.  
  2940. if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `name` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) )
  2941. Sql_ShowDebug(inter->sql_handle);
  2942. else if( SQL_SUCCESS == SQL->NextRow(inter->sql_handle) )
  2943. {
  2944. SQL->GetData(inter->sql_handle, 0, &data, &len);
  2945. safestrncpy(name, data, NAME_LENGTH);
  2946. return 1;
  2947. }
  2948. else
  2949. {
  2950. safestrncpy(name, unknown_char_name, NAME_LENGTH);
  2951. }
  2952. return 0;
  2953. }
  2954.  
  2955. /// Initializes a server structure.
  2956. void mapif_server_init(int id)
  2957. {
  2958. //memset(&chr->server[id], 0, sizeof(server[id]));
  2959. chr->server[id].fd = -1;
  2960. }
  2961.  
  2962. /// Destroys a server structure.
  2963. void mapif_server_destroy(int id)
  2964. {
  2965. if( chr->server[id].fd == -1 )
  2966. {
  2967. sockt->close(chr->server[id].fd);
  2968. chr->server[id].fd = -1;
  2969. }
  2970. }
  2971.  
  2972.  
  2973. /// Resets all the data related to a server.
  2974. void mapif_server_reset(int id)
  2975. {
  2976. int i,j;
  2977. unsigned char buf[16384];
  2978. int fd = chr->server[id].fd;
  2979. //Notify other map servers that this one is gone. [Skotlex]
  2980. WBUFW(buf,0) = 0x2b20;
  2981. WBUFL(buf,4) = htonl(chr->server[id].ip);
  2982. WBUFW(buf,8) = htons(chr->server[id].port);
  2983. j = 0;
  2984. for (i = 0; i < VECTOR_LENGTH(chr->server[id].maps); i++) {
  2985. uint16 m = VECTOR_INDEX(chr->server[id].maps, i);
  2986. if (m != 0)
  2987. WBUFW(buf,10+(j++)*4) = m;
  2988. }
  2989. if (j > 0) {
  2990. WBUFW(buf,2) = j * 4 + 10;
  2991. mapif->sendallwos(fd, buf, WBUFW(buf,2));
  2992. }
  2993. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `index`='%d'", ragsrvinfo_db, chr->server[id].fd) )
  2994. Sql_ShowDebug(inter->sql_handle);
  2995. chr->online_char_db->foreach(chr->online_char_db,chr->db_setoffline,id); //Tag relevant chars as 'in disconnected' server.
  2996. mapif->server_destroy(id);
  2997. mapif->server_init(id);
  2998. }
  2999.  
  3000. /// Called when the connection to a Map Server is disconnected.
  3001. void mapif_on_disconnect(int id)
  3002. {
  3003. ShowStatus("Map-server #%d has disconnected.\n", id);
  3004. mapif->server_reset(id);
  3005. }
  3006.  
  3007. void mapif_on_parse_accinfo(int account_id, int u_fd, int u_aid, int u_group, int map_fd) {
  3008. Assert_retv(chr->login_fd > 0);
  3009. WFIFOHEAD(chr->login_fd,22);
  3010. WFIFOW(chr->login_fd,0) = 0x2740;
  3011. WFIFOL(chr->login_fd,2) = account_id;
  3012. WFIFOL(chr->login_fd,6) = u_fd;
  3013. WFIFOL(chr->login_fd,10) = u_aid;
  3014. WFIFOL(chr->login_fd,14) = u_group;
  3015. WFIFOL(chr->login_fd,18) = map_fd;
  3016. WFIFOSET(chr->login_fd,22);
  3017. }
  3018.  
  3019. void char_parse_frommap_datasync(int fd)
  3020. {
  3021. sockt->datasync(fd, false);
  3022. RFIFOSKIP(fd,RFIFOW(fd,2));
  3023. }
  3024.  
  3025. void char_parse_frommap_skillid2idx(int fd)
  3026. {
  3027. int i;
  3028. int j = RFIFOW(fd, 2) - 4;
  3029.  
  3030. memset(&skillid2idx, 0, sizeof(skillid2idx));
  3031. if( j )
  3032. j /= 4;
  3033. for(i = 0; i < j; i++) {
  3034. if( RFIFOW(fd, 4 + (i*4)) > MAX_SKILL_ID ) {
  3035. ShowWarning("Error skillid2dx[%d] = %d failed, %d is higher than MAX_SKILL_ID (%d)\n",RFIFOW(fd, 4 + (i*4)), RFIFOW(fd, 6 + (i*4)),RFIFOW(fd, 4 + (i*4)),MAX_SKILL_ID);
  3036. continue;
  3037. }
  3038. skillid2idx[RFIFOW(fd, 4 + (i*4))] = RFIFOW(fd, 6 + (i*4));
  3039. }
  3040. RFIFOSKIP(fd, RFIFOW(fd, 2));
  3041. }
  3042.  
  3043. void char_map_received_ok(int fd)
  3044. {
  3045. WFIFOHEAD(fd, 3 + NAME_LENGTH);
  3046. WFIFOW(fd,0) = 0x2afb;
  3047. WFIFOB(fd,2) = 0;
  3048. memcpy(WFIFOP(fd,3), wisp_server_name, NAME_LENGTH);
  3049. WFIFOSET(fd,3+NAME_LENGTH);
  3050. }
  3051.  
  3052. void char_send_maps(int fd, int id, int j)
  3053. {
  3054. int k,i;
  3055.  
  3056. if (j == 0) {
  3057. ShowWarning("Map-server %d has NO maps.\n", id);
  3058. } else {
  3059. unsigned char buf[16384];
  3060. // Transmitting maps information to the other map-servers
  3061. WBUFW(buf,0) = 0x2b04;
  3062. WBUFW(buf,2) = j * 4 + 10;
  3063. WBUFL(buf,4) = htonl(chr->server[id].ip);
  3064. WBUFW(buf,8) = htons(chr->server[id].port);
  3065. memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 4);
  3066. mapif->sendallwos(fd, buf, WBUFW(buf,2));
  3067. }
  3068. // Transmitting the maps of the other map-servers to the new map-server
  3069. for(k = 0; k < ARRAYLENGTH(chr->server); k++) {
  3070. if (chr->server[k].fd > 0 && k != id) {
  3071. WFIFOHEAD(fd,10 + 4 * VECTOR_LENGTH(chr->server[k].maps));
  3072. WFIFOW(fd,0) = 0x2b04;
  3073. WFIFOL(fd,4) = htonl(chr->server[k].ip);
  3074. WFIFOW(fd,8) = htons(chr->server[k].port);
  3075. j = 0;
  3076. for(i = 0; i < VECTOR_LENGTH(chr->server[k].maps); i++) {
  3077. uint16 m = VECTOR_INDEX(chr->server[k].maps, i);
  3078. if (m != 0)
  3079. WFIFOW(fd,10+(j++)*4) = m;
  3080. }
  3081. if (j > 0) {
  3082. WFIFOW(fd,2) = j * 4 + 10;
  3083. WFIFOSET(fd,WFIFOW(fd,2));
  3084. }
  3085. }
  3086. }
  3087. }
  3088.  
  3089. void char_parse_frommap_map_names(int fd, int id)
  3090. {
  3091. int i;
  3092.  
  3093. VECTOR_CLEAR(chr->server[id].maps);
  3094. VECTOR_ENSURE(chr->server[id].maps, (RFIFOW(fd, 2) - 4) / 4, 1);
  3095. for (i = 4; i < RFIFOW(fd,2); i += 4) {
  3096. VECTOR_PUSH(chr->server[id].maps, RFIFOW(fd,i));
  3097. }
  3098.  
  3099. ShowStatus("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n",
  3100. id, (int)VECTOR_LENGTH(chr->server[id].maps), CONVIP(chr->server[id].ip), chr->server[id].port);
  3101. ShowStatus("Map-server %d loading complete.\n", id);
  3102.  
  3103. // send name for wisp to player
  3104. chr->map_received_ok(fd);
  3105. chr->send_fame_list(fd); //Send fame list.
  3106. chr->send_maps(fd, id, (int)VECTOR_LENGTH(chr->server[id].maps));
  3107. RFIFOSKIP(fd,RFIFOW(fd,2));
  3108. }
  3109.  
  3110. void char_send_scdata(int fd, int aid, int cid)
  3111. {
  3112. #ifdef ENABLE_SC_SAVING
  3113. if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` "
  3114. "FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'",
  3115. scdata_db, aid, cid) )
  3116. {
  3117. Sql_ShowDebug(inter->sql_handle);
  3118. return;
  3119. }
  3120. if( SQL->NumRows(inter->sql_handle) > 0 ) {
  3121. struct status_change_data scdata;
  3122. int count;
  3123. char* data;
  3124.  
  3125. memset(&scdata, 0, sizeof(scdata));
  3126. WFIFOHEAD(fd,14+50*sizeof(struct status_change_data));
  3127. WFIFOW(fd,0) = 0x2b1d;
  3128. WFIFOL(fd,4) = aid;
  3129. WFIFOL(fd,8) = cid;
  3130. for( count = 0; count < 50 && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++count )
  3131. {
  3132. SQL->GetData(inter->sql_handle, 0, &data, NULL); scdata.type = atoi(data);
  3133. SQL->GetData(inter->sql_handle, 1, &data, NULL); scdata.tick = atoi(data);
  3134. SQL->GetData(inter->sql_handle, 2, &data, NULL); scdata.val1 = atoi(data);
  3135. SQL->GetData(inter->sql_handle, 3, &data, NULL); scdata.val2 = atoi(data);
  3136. SQL->GetData(inter->sql_handle, 4, &data, NULL); scdata.val3 = atoi(data);
  3137. SQL->GetData(inter->sql_handle, 5, &data, NULL); scdata.val4 = atoi(data);
  3138. memcpy(WFIFOP(fd, 14+count*sizeof(struct status_change_data)), &scdata, sizeof(struct status_change_data));
  3139. }
  3140. if (count >= 50)
  3141. ShowWarning("Too many status changes for %d:%d, some of them were not loaded.\n", aid, cid);
  3142. if (count > 0) {
  3143. WFIFOW(fd,2) = 14 + count*sizeof(struct status_change_data);
  3144. WFIFOW(fd,12) = count;
  3145. WFIFOSET(fd,WFIFOW(fd,2));
  3146.  
  3147. //Clear the data once loaded.
  3148. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid) )
  3149. Sql_ShowDebug(inter->sql_handle);
  3150. }
  3151. } else { //no sc (needs a response)
  3152. WFIFOHEAD(fd,14);
  3153. WFIFOW(fd,0) = 0x2b1d;
  3154. WFIFOW(fd,2) = 14;
  3155. WFIFOL(fd,4) = aid;
  3156. WFIFOL(fd,8) = cid;
  3157. WFIFOW(fd,12) = 0;
  3158. WFIFOSET(fd,WFIFOW(fd,2));
  3159. }
  3160. SQL->FreeResult(inter->sql_handle);
  3161. #endif
  3162. }
  3163.  
  3164. void char_parse_frommap_request_scdata(int fd)
  3165. {
  3166. #ifdef ENABLE_SC_SAVING
  3167. int aid = RFIFOL(fd,2);
  3168. int cid = RFIFOL(fd,6);
  3169. chr->send_scdata(fd, aid, cid);
  3170. #endif
  3171. RFIFOSKIP(fd, 10);
  3172. }
  3173.  
  3174. void char_parse_frommap_set_users_count(int fd, int id)
  3175. {
  3176. if (RFIFOW(fd,2) != chr->server[id].users) {
  3177. chr->server[id].users = RFIFOW(fd,2);
  3178. ShowInfo("User Count: %d (Server: %d)\n", chr->server[id].users, id);
  3179. }
  3180. RFIFOSKIP(fd, 4);
  3181. }
  3182.  
  3183. void char_parse_frommap_set_users(int fd, int id)
  3184. {
  3185. //TODO: When data mismatches memory, update guild/party online/offline states.
  3186. int i;
  3187.  
  3188. chr->server[id].users = RFIFOW(fd,4);
  3189. chr->online_char_db->foreach(chr->online_char_db,chr->db_setoffline,id); //Set all chars from this server as 'unknown'
  3190. for(i = 0; i < chr->server[id].users; i++) {
  3191. int aid = RFIFOL(fd,6+i*8);
  3192. int cid = RFIFOL(fd,6+i*8+4);
  3193. struct online_char_data *character = idb_ensure(chr->online_char_db, aid, chr->create_online_char_data);
  3194. if (character->server > -1 && character->server != id) {
  3195. ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n",
  3196. character->account_id, character->char_id, character->server, id, aid, cid);
  3197. mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); // 2: Already connected to server
  3198. }
  3199. character->server = id;
  3200. character->char_id = cid;
  3201. }
  3202. //If any chars remain in -2, they will be cleaned in the cleanup timer.
  3203. RFIFOSKIP(fd,RFIFOW(fd,2));
  3204. }
  3205.  
  3206. void char_save_character_ack(int fd, int aid, int cid)
  3207. {
  3208. WFIFOHEAD(fd,10);
  3209. WFIFOW(fd,0) = 0x2b21; //Save ack only needed on final save.
  3210. WFIFOL(fd,2) = aid;
  3211. WFIFOL(fd,6) = cid;
  3212. WFIFOSET(fd,10);
  3213. }
  3214.  
  3215. void char_parse_frommap_save_character(int fd, int id)
  3216. {
  3217. int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2);
  3218. struct online_char_data* character;
  3219.  
  3220. if (size - 13 != sizeof(struct mmo_charstatus)) {
  3221. ShowError("parse_from_map (save-char): Size mismatch! %d != %"PRIuS"\n", size-13, sizeof(struct mmo_charstatus));
  3222. RFIFOSKIP(fd,size);
  3223. return;
  3224. }
  3225. //Check account only if this ain't final save. Final-save goes through because of the char-map reconnect
  3226. if (RFIFOB(fd,12)
  3227. || ( (character = (struct online_char_data*)idb_get(chr->online_char_db, aid)) != NULL
  3228. && character->char_id == cid)
  3229. ) {
  3230. struct mmo_charstatus char_dat;
  3231. memcpy(&char_dat, RFIFOP(fd,13), sizeof(struct mmo_charstatus));
  3232. chr->mmo_char_tosql(cid, &char_dat);
  3233. } else {
  3234. //This may be valid on char-server reconnection, when re-sending characters that already logged off.
  3235. ShowError("parse_from_map (save-char): Received data for non-existing/offline character (%d:%d).\n", aid, cid);
  3236. chr->set_char_online(id, cid, aid);
  3237. }
  3238.  
  3239. if (RFIFOB(fd,12)) {
  3240. //Flag, set character offline after saving. [Skotlex]
  3241. chr->set_char_offline(cid, aid);
  3242. chr->save_character_ack(fd, aid, cid);
  3243. }
  3244. RFIFOSKIP(fd,size);
  3245. }
  3246.  
  3247. // 0 - not ok
  3248. // 1 - ok
  3249. void char_select_ack(int fd, int account_id, uint8 flag)
  3250. {
  3251. WFIFOHEAD(fd,7);
  3252. WFIFOW(fd,0) = 0x2b03;
  3253. WFIFOL(fd,2) = account_id;
  3254. WFIFOB(fd,6) = flag;
  3255. WFIFOSET(fd,7);
  3256. }
  3257.  
  3258. void char_parse_frommap_char_select_req(int fd)
  3259. {
  3260. int account_id = RFIFOL(fd,2);
  3261. uint32 login_id1 = RFIFOL(fd,6);
  3262. uint32 login_id2 = RFIFOL(fd,10);
  3263. uint32 ip = RFIFOL(fd,14);
  3264. int32 group_id = RFIFOL(fd, 18);
  3265. RFIFOSKIP(fd,22);
  3266.  
  3267. if( core->runflag != CHARSERVER_ST_RUNNING )
  3268. {
  3269. chr->select_ack(fd, account_id, 0);
  3270. }
  3271. else
  3272. {
  3273. struct char_auth_node* node;
  3274.  
  3275. // create temporary auth entry
  3276. CREATE(node, struct char_auth_node, 1);
  3277. node->account_id = account_id;
  3278. node->char_id = 0;
  3279. node->login_id1 = login_id1;
  3280. node->login_id2 = login_id2;
  3281. node->group_id = group_id;
  3282. //node->sex = 0;
  3283. node->ip = ntohl(ip);
  3284. /* sounds troublesome. */
  3285. //node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server)
  3286. //node->gmlevel = 0;
  3287. idb_put(auth_db, account_id, node);
  3288.  
  3289. //Set char to "@ char select" in online db [Kevin]
  3290. chr->set_char_charselect(account_id);
  3291. chr->select_ack(fd, account_id, 1);
  3292. }
  3293. }
  3294.  
  3295. void char_change_map_server_ack(int fd, uint8 *data, bool ok)
  3296. {
  3297. WFIFOHEAD(fd,30);
  3298. WFIFOW(fd,0) = 0x2b06;
  3299. memcpy(WFIFOP(fd,2), data, 28);
  3300. if (!ok)
  3301. WFIFOL(fd,6) = 0; //Set login1 to 0.
  3302. WFIFOSET(fd,30);
  3303. }
  3304.  
  3305. void char_parse_frommap_change_map_server(int fd)
  3306. {
  3307. int map_id, map_fd = -1;
  3308. struct mmo_charstatus* char_data;
  3309.  
  3310. map_id = chr->search_mapserver(RFIFOW(fd,18), ntohl(RFIFOL(fd,24)), ntohs(RFIFOW(fd,28))); //Locate mapserver by ip and port.
  3311. if (map_id >= 0)
  3312. map_fd = chr->server[map_id].fd;
  3313. //Char should just had been saved before this packet, so this should be safe. [Skotlex]
  3314. char_data = (struct mmo_charstatus*)uidb_get(chr->char_db_,RFIFOL(fd,14));
  3315. if (char_data == NULL) {
  3316. //Really shouldn't happen.
  3317. struct mmo_charstatus char_dat;
  3318. chr->mmo_char_fromsql(RFIFOL(fd,14), &char_dat, true);
  3319. char_data = (struct mmo_charstatus*)uidb_get(chr->char_db_,RFIFOL(fd,14));
  3320. }
  3321.  
  3322. if (core->runflag == CHARSERVER_ST_RUNNING && sockt->session_is_active(map_fd) && char_data) {
  3323. //Send the map server the auth of this player.
  3324. struct online_char_data* data;
  3325. struct char_auth_node* node;
  3326.  
  3327. //Update the "last map" as this is where the player must be spawned on the new map server.
  3328. char_data->last_point.map = RFIFOW(fd,18);
  3329. char_data->last_point.x = RFIFOW(fd,20);
  3330. char_data->last_point.y = RFIFOW(fd,22);
  3331. char_data->sex = RFIFOB(fd,30);
  3332.  
  3333. // create temporary auth entry
  3334. CREATE(node, struct char_auth_node, 1);
  3335. node->account_id = RFIFOL(fd,2);
  3336. node->char_id = RFIFOL(fd,14);
  3337. node->login_id1 = RFIFOL(fd,6);
  3338. node->login_id2 = RFIFOL(fd,10);
  3339. node->sex = RFIFOB(fd,30);
  3340. node->expiration_time = 0; // FIXME (this thing isn't really supported we could as well purge it instead of fixing)
  3341. node->ip = ntohl(RFIFOL(fd,31));
  3342. node->group_id = RFIFOL(fd,35);
  3343. node->changing_mapservers = 1;
  3344. idb_put(auth_db, RFIFOL(fd,2), node);
  3345.  
  3346. data = idb_ensure(chr->online_char_db, RFIFOL(fd,2), chr->create_online_char_data);
  3347. data->char_id = char_data->char_id;
  3348. data->server = map_id; //Update server where char is.
  3349.  
  3350. //Reply with an ack.
  3351. chr->change_map_server_ack(fd, RFIFOP(fd,2), true);
  3352. } else { //Reply with nak
  3353. chr->change_map_server_ack(fd, RFIFOP(fd,2), false);
  3354. }
  3355. RFIFOSKIP(fd,39);
  3356. }
  3357.  
  3358. void char_parse_frommap_remove_friend(int fd)
  3359. {
  3360. int char_id = RFIFOL(fd,2);
  3361. int friend_id = RFIFOL(fd,6);
  3362. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d' AND `friend_id`='%d' LIMIT 1",
  3363. friend_db, char_id, friend_id) ) {
  3364. Sql_ShowDebug(inter->sql_handle);
  3365. }
  3366. RFIFOSKIP(fd,10);
  3367. }
  3368.  
  3369. void char_char_name_ack(int fd, int char_id)
  3370. {
  3371. WFIFOHEAD(fd,30);
  3372. WFIFOW(fd,0) = 0x2b09;
  3373. WFIFOL(fd,2) = char_id;
  3374. chr->loadName(char_id, (char*)WFIFOP(fd,6));
  3375. WFIFOSET(fd,30);
  3376. }
  3377.  
  3378. void char_parse_frommap_char_name_request(int fd)
  3379. {
  3380. chr->char_name_ack(fd, RFIFOL(fd,2));
  3381. RFIFOSKIP(fd,6);
  3382. }
  3383.  
  3384. void char_parse_frommap_change_email(int fd)
  3385. {
  3386. if (chr->login_fd > 0) { // don't send request if no login-server
  3387. WFIFOHEAD(chr->login_fd,86);
  3388. memcpy(WFIFOP(chr->login_fd,0), RFIFOP(fd,0),86); // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B
  3389. WFIFOW(chr->login_fd,0) = 0x2722;
  3390. WFIFOSET(chr->login_fd,86);
  3391. }
  3392. RFIFOSKIP(fd, 86);
  3393. }
  3394.  
  3395. void mapif_char_ban(int char_id, time_t timestamp)
  3396. {
  3397. unsigned char buf[11];
  3398. WBUFW(buf,0) = 0x2b14;
  3399. WBUFL(buf,2) = char_id;
  3400. WBUFB(buf,6) = 2;
  3401. WBUFL(buf,7) = (unsigned int)timestamp;
  3402. mapif->sendall(buf, 11);
  3403. }
  3404.  
  3405. void char_ban(int account_id, int char_id, time_t *unban_time, short year, short month, short day, short hour, short minute, short second)
  3406. {
  3407. time_t timestamp;
  3408. struct tm *tmtime;
  3409. SqlStmt* stmt = SQL->StmtMalloc(inter->sql_handle);
  3410.  
  3411. nullpo_retv(unban_time);
  3412.  
  3413. if (*unban_time == 0 || *unban_time < time(NULL))
  3414. timestamp = time(NULL); // new ban
  3415. else
  3416. timestamp = *unban_time; // add to existing ban
  3417.  
  3418. tmtime = localtime(&timestamp);
  3419. tmtime->tm_year = tmtime->tm_year + year;
  3420. tmtime->tm_mon = tmtime->tm_mon + month;
  3421. tmtime->tm_mday = tmtime->tm_mday + day;
  3422. tmtime->tm_hour = tmtime->tm_hour + hour;
  3423. tmtime->tm_min = tmtime->tm_min + minute;
  3424. tmtime->tm_sec = tmtime->tm_sec + second;
  3425. timestamp = mktime(tmtime);
  3426.  
  3427. if( SQL_SUCCESS != SQL->StmtPrepare(stmt,
  3428. "UPDATE `%s` SET `unban_time` = ? WHERE `char_id` = ? LIMIT 1",
  3429. char_db)
  3430. || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_LONG, (void*)&timestamp, sizeof(timestamp))
  3431. || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_INT, (void*)&char_id, sizeof(char_id))
  3432. || SQL_SUCCESS != SQL->StmtExecute(stmt)
  3433. ) {
  3434. SqlStmt_ShowDebug(stmt);
  3435. }
  3436.  
  3437. SQL->StmtFree(stmt);
  3438.  
  3439. // condition applies; send to all map-servers to disconnect the player
  3440. if( timestamp > time(NULL) ) {
  3441. mapif->char_ban(char_id, timestamp);
  3442. // disconnect player if online on char-server
  3443. chr->disconnect_player(account_id);
  3444. }
  3445. }
  3446.  
  3447. void char_unban(int char_id, int *result)
  3448. {
  3449. /* handled by char server, so no redirection */
  3450. if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `unban_time` = '0' WHERE `char_id` = '%d' LIMIT 1", char_db, char_id) ) {
  3451. Sql_ShowDebug(inter->sql_handle);
  3452. if (result)
  3453. *result = 1;
  3454. }
  3455. }
  3456.  
  3457. void char_ask_name_ack(int fd, int acc, const char* name, int type, int result)
  3458. {
  3459. nullpo_retv(name);
  3460. WFIFOHEAD(fd,34);
  3461. WFIFOW(fd, 0) = 0x2b0f;
  3462. WFIFOL(fd, 2) = acc;
  3463. safestrncpy((char*)WFIFOP(fd,6), name, NAME_LENGTH);
  3464. WFIFOW(fd,30) = type;
  3465. WFIFOW(fd,32) = result;
  3466. WFIFOSET(fd,34);
  3467. }
  3468.  
  3469. /**
  3470. * Changes a character's sex.
  3471. * The information is updated on database, and the character is kicked if it
  3472. * currently is online.
  3473. *
  3474. * @param char_id The character's ID.
  3475. * @param sex The new sex.
  3476. * @retval 0 in case of success.
  3477. * @retval 1 in case of failure.
  3478. */
  3479. int char_changecharsex(int char_id, int sex)
  3480. {
  3481. int class_ = 0, guild_id = 0, account_id = 0;
  3482. char *data;
  3483.  
  3484. // get character data
  3485. if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`,`class`,`guild_id` FROM `%s` WHERE `char_id` = '%d'", char_db, char_id)) {
  3486. Sql_ShowDebug(inter->sql_handle);
  3487. return 1;
  3488. }
  3489. if (SQL->NumRows(inter->sql_handle) != 1 || SQL_ERROR == SQL->NextRow(inter->sql_handle)) {
  3490. SQL->FreeResult(inter->sql_handle);
  3491. return 1;
  3492. }
  3493. SQL->GetData(inter->sql_handle, 0, &data, NULL); account_id = atoi(data);
  3494. SQL->GetData(inter->sql_handle, 1, &data, NULL); class_ = atoi(data);
  3495. SQL->GetData(inter->sql_handle, 2, &data, NULL); guild_id = atoi(data);
  3496. SQL->FreeResult(inter->sql_handle);
  3497.  
  3498. if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `sex` = '%c' WHERE `char_id` = '%d'", char_db, sex == SEX_MALE ? 'M' : 'F', char_id)) {
  3499. Sql_ShowDebug(inter->sql_handle);
  3500. return 1;
  3501. }
  3502. char_change_sex_sub(sex, account_id, char_id, class_, guild_id);
  3503.  
  3504. // disconnect player if online on char-server
  3505. chr->disconnect_player(account_id);
  3506.  
  3507. // notify all mapservers about this change
  3508. chr->changesex(account_id, sex);
  3509. return 0;
  3510. }
  3511.  
  3512. void char_parse_frommap_change_account(int fd)
  3513. {
  3514. int result = 0; // 0-login-server request done, 1-player not found, 2-gm level too low, 3-login-server offline
  3515. char esc_name[NAME_LENGTH*2+1];
  3516.  
  3517. int acc = RFIFOL(fd,2); // account_id of who ask (-1 if server itself made this request)
  3518. const char* name = (char*)RFIFOP(fd,6); // name of the target character
  3519. int type = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5 changesex, 6 charban, 7 charunban
  3520. short year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
  3521. int sex = SEX_MALE;
  3522. if (type == 2 || type == 6) {
  3523. year = RFIFOW(fd,32);
  3524. month = RFIFOW(fd,34);
  3525. day = RFIFOW(fd,36);
  3526. hour = RFIFOW(fd,38);
  3527. minute = RFIFOW(fd,40);
  3528. second = RFIFOW(fd,42);
  3529. } else if (type == 8) {
  3530. sex = RFIFOB(fd, 32);
  3531. }
  3532. RFIFOSKIP(fd,44);
  3533.  
  3534. SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
  3535.  
  3536. if(SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`,`char_id`,`unban_time` FROM `%s` WHERE `name` = '%s'", char_db, esc_name)) {
  3537. Sql_ShowDebug(inter->sql_handle);
  3538. } else if (SQL->NumRows(inter->sql_handle) == 0) {
  3539. SQL->FreeResult(inter->sql_handle);
  3540. result = 1; // 1-player not found
  3541. } else if (SQL_SUCCESS != SQL->NextRow(inter->sql_handle)) {
  3542. Sql_ShowDebug(inter->sql_handle);
  3543. SQL->FreeResult(inter->sql_handle);
  3544. result = 1; // 1-player not found
  3545. } else {
  3546. int account_id, char_id;
  3547. char *data;
  3548. time_t unban_time;
  3549.  
  3550. SQL->GetData(inter->sql_handle, 0, &data, NULL); account_id = atoi(data);
  3551. SQL->GetData(inter->sql_handle, 1, &data, NULL); char_id = atoi(data);
  3552. SQL->GetData(inter->sql_handle, 2, &data, NULL); unban_time = atol(data);
  3553. SQL->FreeResult(inter->sql_handle);
  3554.  
  3555. if( chr->login_fd <= 0 ) {
  3556. result = 3; // 3-login-server offline
  3557. #if 0 //FIXME: need to move this check to login server [ultramage]
  3558. } else if( acc != -1 && isGM(acc) < isGM(account_id) ) {
  3559. result = 2; // 2-gm level too low
  3560. #endif // 0
  3561. } else {
  3562. switch (type) {
  3563. case CHAR_ASK_NAME_BLOCK:
  3564. loginif->block_account(account_id, 5);
  3565. break;
  3566. case CHAR_ASK_NAME_BAN:
  3567. loginif->ban_account(account_id, year, month, day, hour, minute, second);
  3568. break;
  3569. case CHAR_ASK_NAME_UNBLOCK:
  3570. loginif->block_account(account_id, 0);
  3571. break;
  3572. case CHAR_ASK_NAME_UNBAN:
  3573. loginif->unban_account(account_id);
  3574. break;
  3575. case CHAR_ASK_NAME_CHANGESEX:
  3576. loginif->changesex(account_id);
  3577. break;
  3578. case CHAR_ASK_NAME_CHARBAN:
  3579. /* handled by char server, so no redirection */
  3580. chr->ban(account_id, char_id, &unban_time, year, month, day, hour, minute, second);
  3581. break;
  3582. case CHAR_ASK_NAME_CHARUNBAN:
  3583. chr->unban(char_id, &result);
  3584. break;
  3585. case CHAR_ASK_NAME_CHANGECHARSEX:
  3586. result = chr->changecharsex(char_id, sex);
  3587. break;
  3588. }
  3589. }
  3590. }
  3591.  
  3592. // send answer if a player ask, not if the server ask
  3593. if (acc != -1 && type != CHAR_ASK_NAME_CHANGESEX && type != CHAR_ASK_NAME_CHANGECHARSEX) { // Don't send answer for changesex
  3594. chr->ask_name_ack(fd, acc, name, type, result);
  3595. }
  3596. }
  3597.  
  3598. void char_parse_frommap_fame_list(int fd)
  3599. {
  3600. int cid = RFIFOL(fd, 2);
  3601. int fame = RFIFOL(fd, 6);
  3602. char type = RFIFOB(fd, 10);
  3603. int size;
  3604. struct fame_list* list;
  3605. int player_pos;
  3606. int fame_pos;
  3607.  
  3608. switch(type) {
  3609. case RANKTYPE_BLACKSMITH: size = fame_list_size_smith; list = smith_fame_list; break;
  3610. case RANKTYPE_ALCHEMIST: size = fame_list_size_chemist; list = chemist_fame_list; break;
  3611. case RANKTYPE_TAEKWON: size = fame_list_size_taekwon; list = taekwon_fame_list; break;
  3612. default: size = 0; list = NULL; break;
  3613. }
  3614.  
  3615. if (!list) {
  3616. RFIFOSKIP(fd, 11);
  3617. return;
  3618. }
  3619. ARR_FIND(0, size, player_pos, list[player_pos].id == cid);// position of the player
  3620. ARR_FIND(0, size, fame_pos, list[fame_pos].fame <= fame);// where the player should be
  3621.  
  3622. if( player_pos == size && fame_pos == size )
  3623. ;// not on list and not enough fame to get on it
  3624. else if( fame_pos == player_pos ) {
  3625. // same position
  3626. list[player_pos].fame = fame;
  3627. chr->update_fame_list(type, player_pos, fame);
  3628. } else {
  3629. // move in the list
  3630. if( player_pos == size ) {
  3631. // new ranker - not in the list
  3632. ARR_MOVE(size - 1, fame_pos, list, struct fame_list);
  3633. list[fame_pos].id = cid;
  3634. list[fame_pos].fame = fame;
  3635. chr->loadName(cid, list[fame_pos].name);
  3636. } else {
  3637. // already in the list
  3638. if( fame_pos == size )
  3639. --fame_pos;// move to the end of the list
  3640. ARR_MOVE(player_pos, fame_pos, list, struct fame_list);
  3641. list[fame_pos].fame = fame;
  3642. }
  3643. chr->send_fame_list(-1);
  3644. }
  3645.  
  3646. RFIFOSKIP(fd,11);
  3647. }
  3648.  
  3649. void char_parse_frommap_divorce_char(int fd)
  3650. {
  3651. chr->divorce_char_sql(RFIFOL(fd,2), RFIFOL(fd,6));
  3652. RFIFOSKIP(fd,10);
  3653. }
  3654.  
  3655. void char_parse_frommap_ragsrvinfo(int fd)
  3656. {
  3657. char esc_server_name[sizeof(chr->server_name)*2+1];
  3658.  
  3659. SQL->EscapeString(inter->sql_handle, esc_server_name, chr->server_name);
  3660.  
  3661. if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` SET `index`='%d',`name`='%s',`exp`='%d',`jexp`='%d',`drop`='%d'",
  3662. ragsrvinfo_db, fd, esc_server_name, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)) )
  3663. {
  3664. Sql_ShowDebug(inter->sql_handle);
  3665. }
  3666. RFIFOSKIP(fd,14);
  3667. }
  3668.  
  3669. void char_parse_frommap_set_char_offline(int fd)
  3670. {
  3671. chr->set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6));
  3672. RFIFOSKIP(fd,10);
  3673. }
  3674.  
  3675. void char_parse_frommap_set_all_offline(int fd, int id)
  3676. {
  3677. chr->set_all_offline(id);
  3678. RFIFOSKIP(fd,2);
  3679. }
  3680.  
  3681. void char_parse_frommap_set_char_online(int fd, int id)
  3682. {
  3683. chr->set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6));
  3684. RFIFOSKIP(fd,10);
  3685. }
  3686.  
  3687. void char_parse_frommap_build_fame_list(int fd)
  3688. {
  3689. chr->read_fame_list();
  3690. chr->send_fame_list(-1);
  3691. RFIFOSKIP(fd,2);
  3692. }
  3693.  
  3694. void char_parse_frommap_save_status_change_data(int fd)
  3695. {
  3696. #ifdef ENABLE_SC_SAVING
  3697. int aid = RFIFOL(fd, 4);
  3698. int cid = RFIFOL(fd, 8);
  3699. int count = RFIFOW(fd, 12);
  3700.  
  3701. /* clear; ensure no left overs e.g. permanent */
  3702. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid) )
  3703. Sql_ShowDebug(inter->sql_handle);
  3704.  
  3705. if( count > 0 )
  3706. {
  3707. struct status_change_data data;
  3708. StringBuf buf;
  3709. int i;
  3710.  
  3711. StrBuf->Init(&buf);
  3712. StrBuf->Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ", scdata_db);
  3713. for( i = 0; i < count; ++i )
  3714. {
  3715. memcpy (&data, RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data));
  3716. if( i > 0 )
  3717. StrBuf->AppendStr(&buf, ", ");
  3718. StrBuf->Printf(&buf, "('%d','%d','%hu','%d','%d','%d','%d','%d')", aid, cid,
  3719. data.type, data.tick, data.val1, data.val2, data.val3, data.val4);
  3720. }
  3721. if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) )
  3722. Sql_ShowDebug(inter->sql_handle);
  3723. StrBuf->Destroy(&buf);
  3724. }
  3725. #endif
  3726. RFIFOSKIP(fd, RFIFOW(fd, 2));
  3727. }
  3728.  
  3729. void char_send_pong(int fd)
  3730. {
  3731. WFIFOHEAD(fd,2);
  3732. WFIFOW(fd,0) = 0x2b24;
  3733. WFIFOSET(fd,2);
  3734. }
  3735.  
  3736. void char_parse_frommap_ping(int fd)
  3737. {
  3738. chr->send_pong(fd);
  3739. RFIFOSKIP(fd,2);
  3740. }
  3741.  
  3742. void char_map_auth_ok(int fd, int account_id, struct char_auth_node* node, struct mmo_charstatus* cd)
  3743. {
  3744. nullpo_retv(cd);
  3745. WFIFOHEAD(fd,25 + sizeof(struct mmo_charstatus));
  3746. WFIFOW(fd,0) = 0x2afd;
  3747. WFIFOW(fd,2) = 25 + sizeof(struct mmo_charstatus);
  3748. WFIFOL(fd,4) = account_id;
  3749. if (node)
  3750. {
  3751. WFIFOL(fd,8) = node->login_id1;
  3752. WFIFOL(fd,12) = node->login_id2;
  3753. WFIFOL(fd,16) = (uint32)node->expiration_time; // FIXME: will wrap to negative after "19-Jan-2038, 03:14:07 AM GMT"
  3754. WFIFOL(fd,20) = node->group_id;
  3755. WFIFOB(fd,24) = node->changing_mapservers;
  3756. }
  3757. else
  3758. {
  3759. WFIFOL(fd,8) = 0;
  3760. WFIFOL(fd,12) = 0;
  3761. WFIFOL(fd,16) = 0;
  3762. WFIFOL(fd,20) = 0;
  3763. WFIFOB(fd,24) = 0;
  3764. }
  3765. memcpy(WFIFOP(fd,25), cd, sizeof(struct mmo_charstatus));
  3766. WFIFOSET(fd, WFIFOW(fd,2));
  3767. }
  3768.  
  3769. void char_map_auth_failed(int fd, int account_id, int char_id, int login_id1, char sex, uint32 ip)
  3770. {
  3771. WFIFOHEAD(fd,19);
  3772. WFIFOW(fd,0) = 0x2b27;
  3773. WFIFOL(fd,2) = account_id;
  3774. WFIFOL(fd,6) = char_id;
  3775. WFIFOL(fd,10) = login_id1;
  3776. WFIFOB(fd,14) = sex;
  3777. WFIFOL(fd,15) = htonl(ip);
  3778. WFIFOSET(fd,19);
  3779. }
  3780.  
  3781. void char_parse_frommap_auth_request(int fd, int id)
  3782. {
  3783. struct mmo_charstatus char_dat;
  3784. struct char_auth_node* node;
  3785. struct mmo_charstatus* cd;
  3786.  
  3787. int account_id = RFIFOL(fd,2);
  3788. int char_id = RFIFOL(fd,6);
  3789. int login_id1 = RFIFOL(fd,10);
  3790. char sex = RFIFOB(fd,14);
  3791. uint32 ip = ntohl(RFIFOL(fd,15));
  3792. char standalone = RFIFOB(fd, 19);
  3793. RFIFOSKIP(fd,20);
  3794.  
  3795. node = (struct char_auth_node*)idb_get(auth_db, account_id);
  3796. cd = (struct mmo_charstatus*)uidb_get(chr->char_db_,char_id);
  3797.  
  3798. if( cd == NULL ) { //Really shouldn't happen.
  3799. chr->mmo_char_fromsql(char_id, &char_dat, true);
  3800. cd = (struct mmo_charstatus*)uidb_get(chr->char_db_,char_id);
  3801. }
  3802.  
  3803. if( core->runflag == CHARSERVER_ST_RUNNING && cd && standalone ) {
  3804. cd->sex = sex;
  3805.  
  3806. chr->map_auth_ok(fd, account_id, NULL, cd);
  3807. chr->set_char_online(id, char_id, account_id);
  3808. return;
  3809. }
  3810.  
  3811. if( core->runflag == CHARSERVER_ST_RUNNING &&
  3812. cd != NULL &&
  3813. node != NULL &&
  3814. node->account_id == account_id &&
  3815. node->char_id == char_id &&
  3816. node->login_id1 == login_id1 /*&&
  3817. node->sex == sex &&
  3818. node->ip == ip*/ )
  3819. {// auth ok
  3820. if( cd->sex == 99 )
  3821. cd->sex = sex;
  3822.  
  3823. chr->map_auth_ok(fd, account_id, node, cd);
  3824. // only use the auth once and mark user online
  3825. idb_remove(auth_db, account_id);
  3826. chr->set_char_online(id, char_id, account_id);
  3827. }
  3828. else
  3829. {// auth failed
  3830. chr->map_auth_failed(fd, account_id, char_id, login_id1, sex, ip);
  3831. }
  3832. }
  3833.  
  3834. void char_parse_frommap_update_ip(int fd, int id)
  3835. {
  3836. chr->server[id].ip = ntohl(RFIFOL(fd, 2));
  3837. ShowInfo("Updated IP address of map-server #%d to %d.%d.%d.%d.\n", id, CONVIP(chr->server[id].ip));
  3838. RFIFOSKIP(fd,6);
  3839. }
  3840.  
  3841. void char_parse_frommap_request_stats_report(int fd)
  3842. {
  3843. int sfd;/* stat server fd */
  3844. struct hSockOpt opt;
  3845. RFIFOSKIP(fd, 2);/* we skip first 2 bytes which are the 0x3008, so we end up with a buffer equal to the one we send */
  3846.  
  3847. opt.silent = 1;
  3848. opt.setTimeo = 1;
  3849.  
  3850. if ((sfd = sockt->make_connection(sockt->host2ip("stats.herc.ws"),(uint16)25427,&opt) ) == -1) {
  3851. RFIFOSKIP(fd, RFIFOW(fd,2) );/* skip this packet */
  3852. RFIFOFLUSH(fd);
  3853. return;/* connection not possible, we drop the report */
  3854. }
  3855.  
  3856. sockt->session[sfd]->flag.server = 1;/* to ensure we won't drop our own packet */
  3857. sockt->realloc_fifo(sfd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
  3858.  
  3859. WFIFOHEAD(sfd, RFIFOW(fd,2) );
  3860.  
  3861. memcpy((char*)WFIFOP(sfd,0), (char*)RFIFOP(fd, 0), RFIFOW(fd,2));
  3862.  
  3863. WFIFOSET(sfd, RFIFOW(fd,2) );
  3864.  
  3865. do {
  3866. sockt->flush(sfd);
  3867. #ifdef WIN32
  3868. Sleep(1);
  3869. #else
  3870. sleep(1);
  3871. #endif
  3872. } while( !sockt->session[sfd]->flag.eof && sockt->session[sfd]->wdata_size );
  3873.  
  3874. sockt->close(sfd);
  3875.  
  3876. RFIFOSKIP(fd, RFIFOW(fd,2) );/* skip this packet */
  3877. RFIFOFLUSH(fd);
  3878. }
  3879.  
  3880. void char_parse_frommap_scdata_update(int fd)
  3881. {
  3882. int account_id = RFIFOL(fd, 2);
  3883. int char_id = RFIFOL(fd, 6);
  3884. int val1 = RFIFOL(fd, 12);
  3885. int val2 = RFIFOL(fd, 16);
  3886. int val3 = RFIFOL(fd, 20);
  3887. int val4 = RFIFOL(fd, 24);
  3888. short type = RFIFOW(fd, 10);
  3889.  
  3890. if( SQL_ERROR == SQL->Query(inter->sql_handle, "REPLACE INTO `%s` (`account_id`,`char_id`,`type`,`tick`,`val1`,`val2`,`val3`,`val4`) VALUES ('%d','%d','%d',-1,'%d','%d','%d','%d')",
  3891. scdata_db, account_id, char_id, type, val1, val2, val3, val4) )
  3892. {
  3893. Sql_ShowDebug(inter->sql_handle);
  3894. }
  3895. RFIFOSKIP(fd, 28);
  3896. }
  3897.  
  3898. void char_parse_frommap_scdata_delete(int fd)
  3899. {
  3900. int account_id = RFIFOL(fd, 2);
  3901. int char_id = RFIFOL(fd, 6);
  3902. short type = RFIFOW(fd, 10);
  3903.  
  3904. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id` = '%d' AND `type` = '%d' LIMIT 1",
  3905. scdata_db, account_id, char_id, type) )
  3906. {
  3907. Sql_ShowDebug(inter->sql_handle);
  3908. }
  3909. RFIFOSKIP(fd, 12);
  3910. }
  3911.  
  3912. int char_parse_frommap(int fd)
  3913. {
  3914. int id;
  3915.  
  3916. ARR_FIND( 0, ARRAYLENGTH(chr->server), id, chr->server[id].fd == fd );
  3917. if( id == ARRAYLENGTH(chr->server) ) {// not a map server
  3918. ShowDebug("chr->parse_frommap: Disconnecting invalid session #%d (is not a map-server)\n", fd);
  3919. sockt->close(fd);
  3920. return 0;
  3921. }
  3922. if( sockt->session[fd]->flag.eof ) {
  3923. sockt->close(fd);
  3924. chr->server[id].fd = -1;
  3925. mapif->on_disconnect(id);
  3926. return 0;
  3927. }
  3928.  
  3929. while(RFIFOREST(fd) >= 2) {
  3930. if (VECTOR_LENGTH(HPM->packets[hpParse_FromMap]) > 0) {
  3931. int result = HPM->parse_packets(fd,hpParse_FromMap);
  3932. if (result == 1)
  3933. continue;
  3934. if (result == 2)
  3935. return 0;
  3936. }
  3937.  
  3938. switch(RFIFOW(fd,0)) {
  3939. case 0x2b0a:
  3940. if( RFIFOREST(fd) < RFIFOW(fd, 2) )
  3941. return 0;
  3942. chr->parse_frommap_datasync(fd);
  3943. break;
  3944.  
  3945. case 0x2b0b:
  3946. if( RFIFOREST(fd) < RFIFOW(fd, 2) )
  3947. return 0;
  3948. chr->parse_frommap_skillid2idx(fd);
  3949. break;
  3950. case 0x2afa: // Receiving map names list from the map-server
  3951. if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
  3952. return 0;
  3953. chr->parse_frommap_map_names(fd, id);
  3954. break;
  3955.  
  3956. case 0x2afc: //Packet command is now used for sc_data request. [Skotlex]
  3957. if (RFIFOREST(fd) < 10)
  3958. return 0;
  3959. {
  3960. chr->parse_frommap_request_scdata(fd);
  3961. }
  3962. break;
  3963.  
  3964. case 0x2afe: //set MAP user count
  3965. if (RFIFOREST(fd) < 4)
  3966. return 0;
  3967. chr->parse_frommap_set_users_count(fd, id);
  3968. break;
  3969.  
  3970. case 0x2aff: //set MAP users
  3971. if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2))
  3972. return 0;
  3973. {
  3974. chr->parse_frommap_set_users(fd, id);
  3975. }
  3976. break;
  3977.  
  3978. case 0x2b01: // Receive character data from map-server for saving
  3979. if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
  3980. return 0;
  3981. {
  3982. chr->parse_frommap_save_character(fd, id);
  3983.  
  3984. }
  3985. break;
  3986.  
  3987. case 0x2b02: // req char selection
  3988. if( RFIFOREST(fd) < 22 )
  3989. return 0;
  3990. {
  3991. chr->parse_frommap_char_select_req(fd);
  3992. }
  3993. break;
  3994.  
  3995. case 0x2b05: // request "change map server"
  3996. if (RFIFOREST(fd) < 39)
  3997. return 0;
  3998. {
  3999. chr->parse_frommap_change_map_server(fd);
  4000. }
  4001. break;
  4002.  
  4003. case 0x2b07: // Remove RFIFOL(fd,6) (friend_id) from RFIFOL(fd,2) (char_id) friend list [Ind]
  4004. if (RFIFOREST(fd) < 10)
  4005. return 0;
  4006. {
  4007. chr->parse_frommap_remove_friend(fd);
  4008. }
  4009. break;
  4010.  
  4011. case 0x2b08: // char name request
  4012. if (RFIFOREST(fd) < 6)
  4013. return 0;
  4014.  
  4015. chr->parse_frommap_char_name_request(fd);
  4016. break;
  4017.  
  4018. case 0x2b0c: // Map server send information to change an email of an account -> login-server
  4019. if (RFIFOREST(fd) < 86)
  4020. return 0;
  4021. chr->parse_frommap_change_email(fd);
  4022. break;
  4023.  
  4024. case 0x2b0e: // Request from map-server to change an account's or character's status (accounts will just be forwarded to login server)
  4025. if (RFIFOREST(fd) < 44)
  4026. return 0;
  4027. {
  4028. chr->parse_frommap_change_account(fd);
  4029. }
  4030. break;
  4031.  
  4032. case 0x2b10: // Update and send fame ranking list
  4033. if (RFIFOREST(fd) < 11)
  4034. return 0;
  4035. {
  4036. chr->parse_frommap_fame_list(fd);
  4037. }
  4038. break;
  4039.  
  4040. // Divorce chars
  4041. case 0x2b11:
  4042. if( RFIFOREST(fd) < 10 )
  4043. return 0;
  4044.  
  4045. chr->parse_frommap_divorce_char(fd);
  4046. break;
  4047.  
  4048. case 0x2b16: // Receive rates [Wizputer]
  4049. if( RFIFOREST(fd) < 14 )
  4050. return 0;
  4051. {
  4052. chr->parse_frommap_ragsrvinfo(fd);
  4053. }
  4054. break;
  4055.  
  4056. case 0x2b17: // Character disconnected set online 0 [Wizputer]
  4057. if (RFIFOREST(fd) < 6)
  4058. return 0;
  4059. chr->parse_frommap_set_char_offline(fd);
  4060. break;
  4061.  
  4062. case 0x2b18: // Reset all chars to offline [Wizputer]
  4063. chr->parse_frommap_set_all_offline(fd, id);
  4064. break;
  4065.  
  4066. case 0x2b19: // Character set online [Wizputer]
  4067. if (RFIFOREST(fd) < 10)
  4068. return 0;
  4069. chr->parse_frommap_set_char_online(fd, id);
  4070. break;
  4071.  
  4072. case 0x2b1a: // Build and send fame ranking lists [DracoRPG]
  4073. if (RFIFOREST(fd) < 2)
  4074. return 0;
  4075. chr->parse_frommap_build_fame_list(fd);
  4076. break;
  4077.  
  4078. case 0x2b1c: //Request to save status change data. [Skotlex]
  4079. if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
  4080. return 0;
  4081. {
  4082. chr->parse_frommap_save_status_change_data(fd);
  4083. }
  4084. break;
  4085.  
  4086. case 0x2b23: // map-server alive packet
  4087. chr->parse_frommap_ping(fd);
  4088. break;
  4089.  
  4090. case 0x2b26: // auth request from map-server
  4091. if (RFIFOREST(fd) < 20)
  4092. return 0;
  4093.  
  4094. {
  4095. chr->parse_frommap_auth_request(fd, id);
  4096. }
  4097. break;
  4098.  
  4099. case 0x2736: // ip address update
  4100. if (RFIFOREST(fd) < 6) return 0;
  4101. chr->parse_frommap_update_ip(fd, id);
  4102. break;
  4103.  
  4104. case 0x3008:
  4105. if( RFIFOREST(fd) < RFIFOW(fd,4) )
  4106. return 0;/* packet wasn't fully received yet (still fragmented) */
  4107. else {
  4108. chr->parse_frommap_request_stats_report(fd);
  4109. }
  4110. break;
  4111.  
  4112. /* individual sc data insertion/update */
  4113. case 0x2740:
  4114. if( RFIFOREST(fd) < 28 )
  4115. return 0;
  4116. else {
  4117. chr->parse_frommap_scdata_update(fd);
  4118. }
  4119. break;
  4120.  
  4121. /* individual sc data delete */
  4122. case 0x2741:
  4123. if( RFIFOREST(fd) < 12 )
  4124. return 0;
  4125. else {
  4126. chr->parse_frommap_scdata_delete(fd);
  4127. }
  4128. break;
  4129.  
  4130. default:
  4131. {
  4132. // inter server - packet
  4133. int r = inter->parse_frommap(fd);
  4134. if (r == 1) break; // processed
  4135. if (r == 2) return 0; // need more packet
  4136.  
  4137. // no inter server packet. no char server packet -> disconnect
  4138. ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0));
  4139. sockt->eof(fd);
  4140. return 0;
  4141. }
  4142. } // switch
  4143. } // while
  4144.  
  4145. return 0;
  4146. }
  4147.  
  4148. void do_init_mapif(void)
  4149. {
  4150. int i;
  4151. for( i = 0; i < ARRAYLENGTH(chr->server); ++i )
  4152. mapif->server_init(i);
  4153. }
  4154.  
  4155. void do_final_mapif(void)
  4156. {
  4157. int i;
  4158. for( i = 0; i < ARRAYLENGTH(chr->server); ++i )
  4159. mapif->server_destroy(i);
  4160. }
  4161.  
  4162. // Searches for the mapserver that has a given map (and optionally ip/port, if not -1).
  4163. // If found, returns the server's index in the 'server' array (otherwise returns -1).
  4164. int char_search_mapserver(unsigned short map, uint32 ip, uint16 port)
  4165. {
  4166. int i, j;
  4167.  
  4168. for(i = 0; i < ARRAYLENGTH(chr->server); i++)
  4169. {
  4170. if (chr->server[i].fd > 0
  4171. && (ip == (uint32)-1 || chr->server[i].ip == ip)
  4172. && (port == (uint16)-1 || chr->server[i].port == port)
  4173. ) {
  4174. ARR_FIND(0, VECTOR_LENGTH(chr->server[i].maps), j, VECTOR_INDEX(chr->server[i].maps, j) == map);
  4175. if (j != VECTOR_LENGTH(chr->server[i].maps))
  4176. return i;
  4177. }
  4178. }
  4179.  
  4180. return -1;
  4181. }
  4182.  
  4183. // Initialization process (currently only initialization inter_mapif)
  4184. static int char_mapif_init(int fd)
  4185. {
  4186. return inter->mapif_init(fd);
  4187. }
  4188.  
  4189. /**
  4190. * Checks whether the given IP comes from LAN or WAN.
  4191. *
  4192. * @param ip IP address to check.
  4193. * @retval 0 if it is a WAN IP.
  4194. * @return the appropriate LAN server address to send, if it is a LAN IP.
  4195. */
  4196. uint32 char_lan_subnet_check(uint32 ip)
  4197. {
  4198. struct s_subnet lan = {0};
  4199. if (sockt->lan_subnet_check(ip, &lan)) {
  4200. ShowInfo("Subnet check [%u.%u.%u.%u]: Matches "CL_CYAN"%u.%u.%u.%u/%u.%u.%u.%u"CL_RESET"\n", CONVIP(ip), CONVIP(lan.ip & lan.mask), CONVIP(lan.mask));
  4201. return lan.ip;
  4202. }
  4203. ShowInfo("Subnet check [%u.%u.%u.%u]: "CL_CYAN"WAN"CL_RESET"\n", CONVIP(ip));
  4204. return 0;
  4205. }
  4206.  
  4207.  
  4208. /// Answers to deletion request (HC_DELETE_CHAR3_RESERVED)
  4209. /// @param result
  4210. /// 0 (0x718): An unknown error has occurred.
  4211. /// 1: none/success
  4212. /// 3 (0x719): A database error occurred.
  4213. /// 4 (0x71a): To delete a character you must withdraw from the guild.
  4214. /// 5 (0x71b): To delete a character you must withdraw from the party.
  4215. /// Any (0x718): An unknown error has occurred.
  4216. void char_delete2_ack(int fd, int char_id, uint32 result, time_t delete_date)
  4217. {// HC: <0828>.W <char id>.L <Msg:0-5>.L <deleteDate>.L
  4218. WFIFOHEAD(fd,14);
  4219. WFIFOW(fd,0) = 0x828;
  4220. WFIFOL(fd,2) = char_id;
  4221. WFIFOL(fd,6) = result;
  4222. #if PACKETVER >= 20130000
  4223. WFIFOL(fd,10) = (int)(delete_date - time(NULL));
  4224. #else
  4225. WFIFOL(fd,10) = (int)delete_date;
  4226.  
  4227. #endif
  4228. WFIFOSET(fd,14);
  4229. }
  4230.  
  4231. void char_delete2_accept_actual_ack(int fd, int char_id, uint32 result)
  4232. {
  4233. WFIFOHEAD(fd,10);
  4234. WFIFOW(fd,0) = 0x82a;
  4235. WFIFOL(fd,2) = char_id;
  4236. WFIFOL(fd,6) = result;
  4237. WFIFOSET(fd,10);
  4238. }
  4239.  
  4240. /// @param result
  4241. /// 0 (0x718): An unknown error has occurred.
  4242. /// 1: none/success
  4243. /// 2 (0x71c): Due to system settings can not be deleted.
  4244. /// 3 (0x719): A database error occurred.
  4245. /// 4 (0x71d): Deleting not yet possible time.
  4246. /// 5 (0x71e): Date of birth do not match.
  4247. /// Any (0x718): An unknown error has occurred.
  4248. void char_delete2_accept_ack(int fd, int char_id, uint32 result)
  4249. {// HC: <082a>.W <char id>.L <Msg:0-5>.L
  4250. #if PACKETVER >= 20130000 /* not sure the exact date -- must refresh or client gets stuck */
  4251. if( result == 1 ) {
  4252. struct char_session_data* sd = (struct char_session_data*)sockt->session[fd]->session_data;
  4253. chr->mmo_char_send099d(fd, sd);
  4254. }
  4255. #endif
  4256. chr->delete2_accept_actual_ack(fd, char_id, result);
  4257. }
  4258.  
  4259. /// @param result
  4260. /// 1 (0x718): none/success, (if char id not in deletion process): An unknown error has occurred.
  4261. /// 2 (0x719): A database error occurred.
  4262. /// Any (0x718): An unknown error has occurred.
  4263. void char_delete2_cancel_ack(int fd, int char_id, uint32 result)
  4264. {// HC: <082c>.W <char id>.L <Msg:1-2>.L
  4265. WFIFOHEAD(fd,10);
  4266. WFIFOW(fd,0) = 0x82c;
  4267. WFIFOL(fd,2) = char_id;
  4268. WFIFOL(fd,6) = result;
  4269. WFIFOSET(fd,10);
  4270. }
  4271.  
  4272. static void char_delete2_req(int fd, struct char_session_data* sd)
  4273. {// CH: <0827>.W <char id>.L
  4274. int char_id, i;
  4275. char* data;
  4276. time_t delete_date;
  4277.  
  4278. char_id = RFIFOL(fd,2);
  4279. nullpo_retv(sd);
  4280.  
  4281. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id );
  4282. if( i == MAX_CHARS )
  4283. {// character not found
  4284. chr->delete2_ack(fd, char_id, 3, 0); // 3: A database error occurred
  4285. return;
  4286. }
  4287.  
  4288. if( SQL_SUCCESS != SQL->Query(inter->sql_handle, "SELECT `delete_date` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) || SQL_SUCCESS != SQL->NextRow(inter->sql_handle) )
  4289. {
  4290. Sql_ShowDebug(inter->sql_handle);
  4291. chr->delete2_ack(fd, char_id, 3, 0); // 3: A database error occurred
  4292. return;
  4293. }
  4294.  
  4295. SQL->GetData(inter->sql_handle, 0, &data, NULL); delete_date = strtoul(data, NULL, 10);
  4296.  
  4297. if( delete_date ) {// character already queued for deletion
  4298. chr->delete2_ack(fd, char_id, 0, 0); // 0: An unknown error occurred
  4299. return;
  4300. }
  4301.  
  4302. // This check is imposed by Aegis to avoid dead entries in databases
  4303. // _it is not needed_ as we clear data properly
  4304. // see issue: 7338
  4305. if (char_aegis_delete) {
  4306. int party_id = 0, guild_id = 0;
  4307. if( SQL_SUCCESS != SQL->Query(inter->sql_handle, "SELECT `party_id`, `guild_id` FROM `%s` WHERE `char_id`='%d'", char_db, char_id)
  4308. || SQL_SUCCESS != SQL->NextRow(inter->sql_handle)
  4309. ) {
  4310. Sql_ShowDebug(inter->sql_handle);
  4311. chr->delete2_ack(fd, char_id, 3, 0); // 3: A database error occurred
  4312. return;
  4313. }
  4314. SQL->GetData(inter->sql_handle, 0, &data, NULL); party_id = atoi(data);
  4315. SQL->GetData(inter->sql_handle, 1, &data, NULL); guild_id = atoi(data);
  4316.  
  4317. if( guild_id )
  4318. {
  4319. chr->delete2_ack(fd, char_id, 4, 0); // 4: To delete a character you must withdraw from the guild
  4320. return;
  4321. }
  4322.  
  4323. if( party_id )
  4324. {
  4325. chr->delete2_ack(fd, char_id, 5, 0); // 5: To delete a character you must withdraw from the party
  4326. return;
  4327. }
  4328. }
  4329.  
  4330. // success
  4331. delete_date = time(NULL)+char_del_delay;
  4332.  
  4333. if( SQL_SUCCESS != SQL->Query(inter->sql_handle, "UPDATE `%s` SET `delete_date`='%lu' WHERE `char_id`='%d'", char_db, (unsigned long)delete_date, char_id) )
  4334. {
  4335. Sql_ShowDebug(inter->sql_handle);
  4336. chr->delete2_ack(fd, char_id, 3, 0); // 3: A database error occurred
  4337. return;
  4338. }
  4339.  
  4340. chr->delete2_ack(fd, char_id, 1, delete_date); // 1: success
  4341. }
  4342.  
  4343. static void char_delete2_accept(int fd, struct char_session_data* sd)
  4344. {// CH: <0829>.W <char id>.L <birth date:YYMMDD>.6B
  4345. char birthdate[8+1];
  4346. int char_id, i;
  4347. unsigned int base_level;
  4348. char* data;
  4349. time_t delete_date;
  4350.  
  4351. nullpo_retv(sd);
  4352. char_id = RFIFOL(fd,2);
  4353.  
  4354. ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, char_id);
  4355.  
  4356. // construct "YY-MM-DD"
  4357. birthdate[0] = RFIFOB(fd,6);
  4358. birthdate[1] = RFIFOB(fd,7);
  4359. birthdate[2] = '-';
  4360. birthdate[3] = RFIFOB(fd,8);
  4361. birthdate[4] = RFIFOB(fd,9);
  4362. birthdate[5] = '-';
  4363. birthdate[6] = RFIFOB(fd,10);
  4364. birthdate[7] = RFIFOB(fd,11);
  4365. birthdate[8] = 0;
  4366.  
  4367. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id );
  4368. if( i == MAX_CHARS )
  4369. {// character not found
  4370. chr->delete2_accept_ack(fd, char_id, 3); // 3: A database error occurred
  4371. return;
  4372. }
  4373.  
  4374. if( SQL_SUCCESS != SQL->Query(inter->sql_handle, "SELECT `base_level`,`delete_date` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) || SQL_SUCCESS != SQL->NextRow(inter->sql_handle) )
  4375. {// data error
  4376. Sql_ShowDebug(inter->sql_handle);
  4377. chr->delete2_accept_ack(fd, char_id, 3); // 3: A database error occurred
  4378. return;
  4379. }
  4380.  
  4381. SQL->GetData(inter->sql_handle, 0, &data, NULL); base_level = (unsigned int)strtoul(data, NULL, 10);
  4382. SQL->GetData(inter->sql_handle, 1, &data, NULL); delete_date = strtoul(data, NULL, 10);
  4383.  
  4384. if( !delete_date || delete_date>time(NULL) )
  4385. {// not queued or delay not yet passed
  4386. chr->delete2_accept_ack(fd, char_id, 4); // 4: Deleting not yet possible time
  4387. return;
  4388. }
  4389.  
  4390. if( strcmp(sd->birthdate+2, birthdate) ) // +2 to cut off the century
  4391. {// birth date is wrong
  4392. chr->delete2_accept_ack(fd, char_id, 5); // 5: Date of birth do not match
  4393. return;
  4394. }
  4395.  
  4396. if( ( char_del_level > 0 && base_level >= (unsigned int)char_del_level ) || ( char_del_level < 0 && base_level <= (unsigned int)(-char_del_level) ) )
  4397. {// character level config restriction
  4398. chr->delete2_accept_ack(fd, char_id, 2); // 2: Due to system settings can not be deleted
  4399. return;
  4400. }
  4401.  
  4402. // success
  4403. if( chr->delete_char_sql(char_id) < 0 )
  4404. {
  4405. chr->delete2_accept_ack(fd, char_id, 3); // 3: A database error occurred
  4406. return;
  4407. }
  4408.  
  4409. // refresh character list cache
  4410. sd->found_char[i] = -1;
  4411.  
  4412. chr->delete2_accept_ack(fd, char_id, 1); // 1: success
  4413. }
  4414.  
  4415. static void char_delete2_cancel(int fd, struct char_session_data* sd)
  4416. {// CH: <082b>.W <char id>.L
  4417. int char_id, i;
  4418.  
  4419. nullpo_retv(sd);
  4420. char_id = RFIFOL(fd,2);
  4421.  
  4422. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id );
  4423. if( i == MAX_CHARS )
  4424. {// character not found
  4425. chr->delete2_cancel_ack(fd, char_id, 2); // 2: A database error occurred
  4426. return;
  4427. }
  4428.  
  4429. // there is no need to check, whether or not the character was
  4430. // queued for deletion, as the client prints an error message by
  4431. // itself, if it was not the case (@see chr->delete2_cancel_ack)
  4432. if( SQL_SUCCESS != SQL->Query(inter->sql_handle, "UPDATE `%s` SET `delete_date`='0' WHERE `char_id`='%d'", char_db, char_id) )
  4433. {
  4434. Sql_ShowDebug(inter->sql_handle);
  4435. chr->delete2_cancel_ack(fd, char_id, 2); // 2: A database error occurred
  4436. return;
  4437. }
  4438.  
  4439. chr->delete2_cancel_ack(fd, char_id, 1); // 1: success
  4440. }
  4441.  
  4442. void char_send_account_id(int fd, int account_id)
  4443. {
  4444. WFIFOHEAD(fd,4);
  4445. WFIFOL(fd,0) = account_id;
  4446. WFIFOSET(fd,4);
  4447. }
  4448.  
  4449. void char_parse_char_connect(int fd, struct char_session_data* sd, uint32 ipl)
  4450. {
  4451. int account_id = RFIFOL(fd,2);
  4452. uint32 login_id1 = RFIFOL(fd,6);
  4453. uint32 login_id2 = RFIFOL(fd,10);
  4454. int sex = RFIFOB(fd,16);
  4455. struct char_auth_node* node;
  4456.  
  4457. RFIFOSKIP(fd,17);
  4458.  
  4459. ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", account_id, login_id1, login_id2);
  4460.  
  4461. if (sd) {
  4462. //Received again auth packet for already authenticated account?? Discard it.
  4463. //TODO: Perhaps log this as a hack attempt?
  4464. //TODO: and perhaps send back a reply?
  4465. return;
  4466. }
  4467.  
  4468. CREATE(sockt->session[fd]->session_data, struct char_session_data, 1);
  4469. sd = (struct char_session_data*)sockt->session[fd]->session_data;
  4470. sd->account_id = account_id;
  4471. sd->login_id1 = login_id1;
  4472. sd->login_id2 = login_id2;
  4473. sd->sex = sex;
  4474. sd->auth = false; // not authed yet
  4475.  
  4476. // send back account_id
  4477. chr->send_account_id(fd, account_id);
  4478.  
  4479. if( core->runflag != CHARSERVER_ST_RUNNING ) {
  4480. chr->auth_error(fd, 0);
  4481. return;
  4482. }
  4483.  
  4484. // search authentication
  4485. node = (struct char_auth_node*)idb_get(auth_db, account_id);
  4486. if( node != NULL &&
  4487. node->account_id == account_id &&
  4488. node->login_id1 == login_id1 &&
  4489. node->login_id2 == login_id2 /*&&
  4490. node->ip == ipl*/ )
  4491. {// authentication found (coming from map server)
  4492. /* restrictions apply */
  4493. if( chr->server_type == CST_MAINTENANCE && node->group_id < char_maintenance_min_group_id ) {
  4494. chr->auth_error(fd, 0);
  4495. return;
  4496. }
  4497. /* the client will already deny this request, this check is to avoid someone bypassing. */
  4498. if( chr->server_type == CST_PAYING && (time_t)node->expiration_time < time(NULL) ) {
  4499. chr->auth_error(fd, 0);
  4500. return;
  4501. }
  4502. idb_remove(auth_db, account_id);
  4503. chr->auth_ok(fd, sd);
  4504. }
  4505. else
  4506. {// authentication not found (coming from login server)
  4507. if (chr->login_fd > 0) { // don't send request if no login-server
  4508. loginif->auth(fd, sd, ipl);
  4509. } else { // if no login-server, we must refuse connection
  4510. chr->auth_error(fd, 0);
  4511. }
  4512. }
  4513. }
  4514.  
  4515. void char_send_map_info(int fd, int i, uint32 subnet_map_ip, struct mmo_charstatus *cd)
  4516. {
  4517. nullpo_retv(cd);
  4518. WFIFOHEAD(fd,28);
  4519. WFIFOW(fd,0) = 0x71;
  4520. WFIFOL(fd,2) = cd->char_id;
  4521. mapindex->getmapname_ext(mapindex_id2name(cd->last_point.map), (char*)WFIFOP(fd,6));
  4522. WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : chr->server[i].ip);
  4523. WFIFOW(fd,26) = sockt->ntows(htons(chr->server[i].port)); // [!] LE byte order here [!]
  4524. WFIFOSET(fd,28);
  4525. }
  4526.  
  4527. void char_send_wait_char_server(int fd)
  4528. {
  4529. WFIFOHEAD(fd, 24);
  4530. WFIFOW(fd, 0) = 0x840;
  4531. WFIFOW(fd, 2) = 24;
  4532. safestrncpy((char*)WFIFOP(fd,4), "0", 20);/* we can't send empty (otherwise the list will pop up) */
  4533. WFIFOSET(fd, 24);
  4534. }
  4535.  
  4536. int char_search_default_maps_mapserver(struct mmo_charstatus *cd)
  4537. {
  4538. int i;
  4539. int j;
  4540. nullpo_retr(-1, cd);
  4541. if ((i = chr->search_mapserver((j=mapindex->name2id(MAP_PRONTERA)),-1,-1)) >= 0) {
  4542. cd->last_point.x = 273;
  4543. cd->last_point.y = 354;
  4544. } else if ((i = chr->search_mapserver((j=mapindex->name2id(MAP_GEFFEN)),-1,-1)) >= 0) {
  4545. cd->last_point.x = 120;
  4546. cd->last_point.y = 100;
  4547. } else if ((i = chr->search_mapserver((j=mapindex->name2id(MAP_MORROC)),-1,-1)) >= 0) {
  4548. cd->last_point.x = 160;
  4549. cd->last_point.y = 94;
  4550. } else if ((i = chr->search_mapserver((j=mapindex->name2id(MAP_ALBERTA)),-1,-1)) >= 0) {
  4551. cd->last_point.x = 116;
  4552. cd->last_point.y = 57;
  4553. } else if ((i = chr->search_mapserver((j=mapindex->name2id(MAP_PAYON)),-1,-1)) >= 0) {
  4554. cd->last_point.x = 87;
  4555. cd->last_point.y = 117;
  4556. } else if ((i = chr->search_mapserver((j=mapindex->name2id(MAP_IZLUDE)),-1,-1)) >= 0) {
  4557. cd->last_point.x = 94;
  4558. cd->last_point.y = 103;
  4559. }
  4560. if (i >= 0)
  4561. {
  4562. cd->last_point.map = j;
  4563. ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(cd->last_point.map), mapindex_id2name(j));
  4564. }
  4565. return i;
  4566. }
  4567.  
  4568. void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) __attribute__((nonnull (2)));
  4569. void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl)
  4570. {
  4571. struct mmo_charstatus char_dat;
  4572. struct mmo_charstatus *cd;
  4573. struct char_auth_node* node;
  4574. char* data;
  4575. int char_id;
  4576. int server_id = 0;
  4577. int i;
  4578. int map_fd;
  4579. uint32 subnet_map_ip;
  4580. int slot = RFIFOB(fd,2);
  4581.  
  4582. RFIFOSKIP(fd,3);
  4583.  
  4584. #if PACKETVER >= 20110309
  4585. if( pincode->enabled ){ // hack check
  4586. struct online_char_data* character;
  4587. character = (struct online_char_data*)idb_get(chr->online_char_db, sd->account_id);
  4588. if( character && character->pincode_enable == -1){
  4589. chr->auth_error(fd, 0);
  4590. return;
  4591. }
  4592. }
  4593. #endif
  4594.  
  4595. ARR_FIND(0, ARRAYLENGTH(chr->server), server_id, chr->server[server_id].fd > 0 && VECTOR_LENGTH(chr->server[server_id].maps) > 0);
  4596. /* not available, tell it to wait (client wont close; char select will respawn).
  4597. * magic response found by Ind thanks to Yommy <3 */
  4598. if( server_id == ARRAYLENGTH(chr->server) ) {
  4599. chr->send_wait_char_server(fd);
  4600. return;
  4601. }
  4602.  
  4603. if (SQL_SUCCESS != SQL->Query(inter->sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'", char_db, sd->account_id, slot)
  4604. || SQL_SUCCESS != SQL->NextRow(inter->sql_handle)
  4605. || SQL_SUCCESS != SQL->GetData(inter->sql_handle, 0, &data, NULL)
  4606. ) {
  4607. //Not found?? May be forged packet.
  4608. Sql_ShowDebug(inter->sql_handle);
  4609. SQL->FreeResult(inter->sql_handle);
  4610. chr->auth_error(fd, 0); // rejected from server
  4611. return;
  4612. }
  4613.  
  4614. char_id = atoi(data);
  4615. SQL->FreeResult(inter->sql_handle);
  4616.  
  4617. /* client doesn't let it get to this point if you're banned, so its a forged packet */
  4618. if( sd->found_char[slot] == char_id && sd->unban_time[slot] > time(NULL) ) {
  4619. chr->auth_error(fd, 0); // rejected from server
  4620. return;
  4621. }
  4622.  
  4623. /* set char as online prior to loading its data so 3rd party applications will realize the sql data is not reliable */
  4624. chr->set_char_online(-2,char_id,sd->account_id);
  4625. if( !chr->mmo_char_fromsql(char_id, &char_dat, true) ) { /* failed? set it back offline */
  4626. chr->set_char_offline(char_id, sd->account_id);
  4627. /* failed to load something. REJECT! */
  4628. chr->auth_error(fd, 0); // rejected from server
  4629. return;/* jump off this boat */
  4630. }
  4631.  
  4632. //Have to switch over to the DB instance otherwise data won't propagate [Kevin]
  4633. cd = (struct mmo_charstatus *)idb_get(chr->char_db_, char_id);
  4634. nullpo_retv(cd);
  4635. if( cd->sex == 99 )
  4636. cd->sex = sd->sex;
  4637.  
  4638. if (log_char) {
  4639. char esc_name[NAME_LENGTH*2+1];
  4640. // FIXME: Why are we re-escaping the name if it was already escaped in rename/make_new_char? [Panikon]
  4641. SQL->EscapeStringLen(inter->sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH));
  4642. if( SQL_ERROR == SQL->Query(inter->sql_handle,
  4643. "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `name`) VALUES (NOW(), '%d', '%d', '%d', '%s')",
  4644. charlog_db, sd->account_id, cd->char_id, slot, esc_name) )
  4645. Sql_ShowDebug(inter->sql_handle);
  4646. }
  4647. ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name);
  4648.  
  4649. // searching map server
  4650. i = chr->search_mapserver(cd->last_point.map, -1, -1);
  4651.  
  4652. // if map is not found, we check major cities
  4653. if (i < 0 || !cd->last_point.map) {
  4654. unsigned short j;
  4655. //First check that there's actually a map server online.
  4656. ARR_FIND(0, ARRAYLENGTH(chr->server), j, chr->server[j].fd >= 0 && VECTOR_LENGTH(chr->server[j].maps) > 0);
  4657. if (j == ARRAYLENGTH(chr->server)) {
  4658. ShowInfo("Connection Closed. No map servers available.\n");
  4659. chr->authfail_fd(fd, 1); // 1 = Server closed
  4660. return;
  4661. }
  4662. i = chr->search_default_maps_mapserver(cd);
  4663. if (i < 0)
  4664. {
  4665. 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));
  4666. chr->authfail_fd(fd, 1); // 1 = Server closed
  4667. return;
  4668. }
  4669. }
  4670.  
  4671. //Send NEW auth packet [Kevin]
  4672. //FIXME: is this case even possible? [ultramage]
  4673. if ((map_fd = chr->server[i].fd) < 1 || sockt->session[map_fd] == NULL)
  4674. {
  4675. ShowError("chr->parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i);
  4676. chr->server[i].fd = -1;
  4677. memset(&chr->server[i], 0, sizeof(struct mmo_map_server));
  4678. chr->authfail_fd(fd, 1); // 1 = Server closed
  4679. return;
  4680. }
  4681.  
  4682. subnet_map_ip = chr->lan_subnet_check(ipl);
  4683. //Send player to map
  4684. chr->send_map_info(fd, i, subnet_map_ip, cd);
  4685.  
  4686. // create temporary auth entry
  4687. CREATE(node, struct char_auth_node, 1);
  4688. node->account_id = sd->account_id;
  4689. node->char_id = cd->char_id;
  4690. node->login_id1 = sd->login_id1;
  4691. node->login_id2 = sd->login_id2;
  4692. node->sex = sd->sex;
  4693. node->expiration_time = sd->expiration_time;
  4694. node->group_id = sd->group_id;
  4695. node->ip = ipl;
  4696. idb_put(auth_db, sd->account_id, node);
  4697. }
  4698.  
  4699. void char_creation_failed(int fd, int result)
  4700. {
  4701. WFIFOHEAD(fd,3);
  4702. WFIFOW(fd,0) = 0x6e;
  4703. /* Others I found [Ind] */
  4704. /* 0x02 = Symbols in Character Names are forbidden */
  4705. /* 0x03 = You are not eligible to open the Character Slot. */
  4706. /* 0x0B = This service is only available for premium users. */
  4707. switch (result) {
  4708. case -1: WFIFOB(fd,2) = 0x00; break; // 'Charname already exists'
  4709. case -2: WFIFOB(fd,2) = 0xFF; break; // 'Char creation denied'
  4710. case -3: WFIFOB(fd,2) = 0x01; break; // 'You are underaged'
  4711. case -4: WFIFOB(fd,2) = 0x03; break; // 'You are not eligible to open the Character Slot.'
  4712. case -5: WFIFOB(fd,2) = 0x02; break; // 'Symbols in Character Names are forbidden'
  4713.  
  4714. default:
  4715. ShowWarning("chr->parse_char: Unknown result received from chr->make_new_char_sql!\n");
  4716. WFIFOB(fd,2) = 0xFF;
  4717. break;
  4718. }
  4719. WFIFOSET(fd,3);
  4720. }
  4721.  
  4722. void char_creation_ok(int fd, struct mmo_charstatus *char_dat)
  4723. {
  4724. int len;
  4725.  
  4726. // send to player
  4727. WFIFOHEAD(fd,2+MAX_CHAR_BUF);
  4728. WFIFOW(fd,0) = 0x6d;
  4729. len = 2 + chr->mmo_char_tobuf(WFIFOP(fd,2), char_dat);
  4730. WFIFOSET(fd,len);
  4731. }
  4732.  
  4733. void char_parse_char_create_new_char(int fd, struct char_session_data* sd) __attribute__((nonnull (2)));
  4734. void char_parse_char_create_new_char(int fd, struct char_session_data* sd)
  4735. {
  4736. int result;
  4737. if( !char_new ) {
  4738. //turn character creation on/off [Kevin]
  4739. result = -2;
  4740. } else {
  4741. #if PACKETVER >= 20120307
  4742. result = chr->make_new_char_sql(sd, (char*)RFIFOP(fd,2), 1, 1, 1, 1, 1, 1, RFIFOB(fd,26),RFIFOW(fd,27),RFIFOW(fd,29));
  4743. #else
  4744. result = chr->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));
  4745. #endif
  4746. }
  4747.  
  4748. //'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3)
  4749. if (result < 0) {
  4750. chr->creation_failed(fd, result);
  4751. } else {
  4752. // retrieve data
  4753. struct mmo_charstatus char_dat;
  4754. chr->mmo_char_fromsql(result, &char_dat, false); //Only the short data is needed.
  4755. chr->creation_ok(fd, &char_dat);
  4756.  
  4757. // add new entry to the chars list
  4758. sd->found_char[char_dat.slot] = result; // the char_id of the new char
  4759. }
  4760. #if PACKETVER >= 20120307
  4761. RFIFOSKIP(fd,31);
  4762. #else
  4763. RFIFOSKIP(fd,37);
  4764. #endif
  4765. }
  4766.  
  4767. // flag:
  4768. // 0 = Incorrect Email address
  4769. void char_delete_char_failed(int fd, int flag)
  4770. {
  4771. WFIFOHEAD(fd,3);
  4772. WFIFOW(fd,0) = 0x70;
  4773. WFIFOB(fd,2) = flag;
  4774. WFIFOSET(fd,3);
  4775. }
  4776.  
  4777. void char_delete_char_ok(int fd)
  4778. {
  4779. WFIFOHEAD(fd,2);
  4780. WFIFOW(fd,0) = 0x6f;
  4781. WFIFOSET(fd,2);
  4782. }
  4783.  
  4784. void char_parse_char_delete_char(int fd, struct char_session_data* sd, unsigned short cmd) __attribute__((nonnull (2)));
  4785. void char_parse_char_delete_char(int fd, struct char_session_data* sd, unsigned short cmd)
  4786. {
  4787. char email[40];
  4788. int cid = RFIFOL(fd,2);
  4789. int i;
  4790.  
  4791. #if PACKETVER >= 20110309
  4792. if (pincode->enabled) { // hack check
  4793. struct online_char_data* character;
  4794. character = (struct online_char_data*)idb_get(chr->online_char_db, sd->account_id);
  4795. if( character && character->pincode_enable == -1 ){
  4796. chr->auth_error(fd, 0);
  4797. RFIFOSKIP(fd,( cmd == 0x68) ? 46 : 56);
  4798. return;
  4799. }
  4800. }
  4801. #endif
  4802. ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid);
  4803. memcpy(email, RFIFOP(fd,6), 40);
  4804. RFIFOSKIP(fd,( cmd == 0x68) ? 46 : 56);
  4805.  
  4806. // Check if e-mail is correct
  4807. if (strcmpi(email, sd->email) != 0 /* emails don't match */
  4808. && ( strcmp("a@a.com", sd->email) != 0 /* it's not the default email */
  4809. || (strcmp("a@a.com", email) != 0 && strcmp("", email) != 0) /* sent email isn't the default */
  4810. )) {
  4811. //Fail
  4812. chr->delete_char_failed(fd, 0);
  4813. return;
  4814. }
  4815.  
  4816. // check if this char exists
  4817. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
  4818. if( i == MAX_CHARS )
  4819. { // Such a character does not exist in the account
  4820. chr->delete_char_failed(fd, 0);
  4821. return;
  4822. }
  4823.  
  4824. // remove char from list and compact it
  4825. sd->found_char[i] = -1;
  4826.  
  4827. /* Delete character */
  4828. if(chr->delete_char_sql(cid)<0){
  4829. //can't delete the char
  4830. //either SQL error or can't delete by some CONFIG conditions
  4831. //del fail
  4832. chr->delete_char_failed(fd, 0);
  4833. return;
  4834. }
  4835. /* Char successfully deleted.*/
  4836. chr->delete_char_ok(fd);
  4837. }
  4838.  
  4839. void char_parse_char_ping(int fd)
  4840. {
  4841. RFIFOSKIP(fd,6);
  4842. }
  4843.  
  4844. void char_allow_rename(int fd, int flag)
  4845. {
  4846. WFIFOHEAD(fd, 4);
  4847. WFIFOW(fd,0) = 0x28e;
  4848. WFIFOW(fd,2) = flag;
  4849. WFIFOSET(fd,4);
  4850. }
  4851.  
  4852. void char_parse_char_rename_char(int fd, struct char_session_data* sd) __attribute__((nonnull (2)));
  4853. void char_parse_char_rename_char(int fd, struct char_session_data* sd)
  4854. {
  4855. int i, cid =RFIFOL(fd,2);
  4856. char name[NAME_LENGTH];
  4857. char esc_name[NAME_LENGTH*2+1];
  4858. safestrncpy(name, (char *)RFIFOP(fd,6), NAME_LENGTH);
  4859. RFIFOSKIP(fd,30);
  4860.  
  4861. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
  4862. if( i == MAX_CHARS )
  4863. return;
  4864.  
  4865. normalize_name(name,TRIM_CHARS);
  4866. SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
  4867. if( !chr->check_char_name(name,esc_name) ) {
  4868. i = 1;
  4869. safestrncpy(sd->new_name, name, NAME_LENGTH);
  4870. } else {
  4871. i = 0;
  4872. }
  4873.  
  4874. chr->allow_rename(fd, i);
  4875. }
  4876.  
  4877. void char_parse_char_rename_char2(int fd, struct char_session_data* sd) __attribute__((nonnull (2)));
  4878. void char_parse_char_rename_char2(int fd, struct char_session_data* sd)
  4879. {
  4880. int i, aid = RFIFOL(fd,2), cid =RFIFOL(fd,6);
  4881. char name[NAME_LENGTH];
  4882. char esc_name[NAME_LENGTH*2+1];
  4883. safestrncpy(name, (char *)RFIFOP(fd,10), NAME_LENGTH);
  4884. RFIFOSKIP(fd,34);
  4885.  
  4886. if( aid != sd->account_id )
  4887. return;
  4888. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
  4889. if( i == MAX_CHARS )
  4890. return;
  4891.  
  4892. normalize_name(name,TRIM_CHARS);
  4893. SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
  4894. if( !chr->check_char_name(name,esc_name) )
  4895. {
  4896. i = 1;
  4897. safestrncpy(sd->new_name, name, NAME_LENGTH);
  4898. }
  4899. else
  4900. i = 0;
  4901.  
  4902. chr->allow_rename(fd, i);
  4903. }
  4904.  
  4905. void char_rename_char_ack(int fd, int flag)
  4906. {
  4907. WFIFOHEAD(fd, 4);
  4908. WFIFOW(fd,0) = 0x290;
  4909. WFIFOW(fd,2) = flag;
  4910. WFIFOSET(fd,4);
  4911. }
  4912.  
  4913. void char_parse_char_rename_char_confirm(int fd, struct char_session_data* sd) __attribute__((nonnull (2)));
  4914. void char_parse_char_rename_char_confirm(int fd, struct char_session_data* sd)
  4915. {
  4916. int i;
  4917. int cid = RFIFOL(fd,2);
  4918. RFIFOSKIP(fd,6);
  4919.  
  4920. ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
  4921. if( i == MAX_CHARS )
  4922. return;
  4923. i = chr->rename_char_sql(sd, cid);
  4924.  
  4925. chr->rename_char_ack(fd, i);
  4926. }
  4927.  
  4928. void char_captcha_notsupported(int fd)
  4929. {
  4930. WFIFOHEAD(fd,5);
  4931. WFIFOW(fd,0) = 0x7e9;
  4932. WFIFOW(fd,2) = 5;
  4933. WFIFOB(fd,4) = 1;
  4934. WFIFOSET(fd,5);
  4935. }
  4936.  
  4937. void char_parse_char_request_captcha(int fd)
  4938. {
  4939. chr->captcha_notsupported(fd);
  4940. RFIFOSKIP(fd,8);
  4941. }
  4942.  
  4943. void char_parse_char_check_captcha(int fd)
  4944. {
  4945. chr->captcha_notsupported(fd);
  4946. RFIFOSKIP(fd,32);
  4947. }
  4948.  
  4949. void char_parse_char_delete2_req(int fd, struct char_session_data* sd)
  4950. {
  4951. chr->delete2_req(fd, sd);
  4952. RFIFOSKIP(fd,6);
  4953. }
  4954.  
  4955. void char_parse_char_delete2_accept(int fd, struct char_session_data* sd)
  4956. {
  4957. chr->delete2_accept(fd, sd);
  4958. RFIFOSKIP(fd,12);
  4959. }
  4960.  
  4961. void char_parse_char_delete2_cancel(int fd, struct char_session_data* sd)
  4962. {
  4963. chr->delete2_cancel(fd, sd);
  4964. RFIFOSKIP(fd,6);
  4965. }
  4966.  
  4967. // flag:
  4968. // 0 - ok
  4969. // 3 - error
  4970. void char_login_map_server_ack(int fd, uint8 flag)
  4971. {
  4972. WFIFOHEAD(fd,3);
  4973. WFIFOW(fd,0) = 0x2af9;
  4974. WFIFOB(fd,2) = flag;
  4975. WFIFOSET(fd,3);
  4976. }
  4977.  
  4978. void char_parse_char_login_map_server(int fd, uint32 ipl)
  4979. {
  4980. char* l_user = (char*)RFIFOP(fd,2);
  4981. char* l_pass = (char*)RFIFOP(fd,26);
  4982. int i;
  4983. l_user[23] = '\0';
  4984. l_pass[23] = '\0';
  4985.  
  4986. ARR_FIND( 0, ARRAYLENGTH(chr->server), i, chr->server[i].fd <= 0 );
  4987. if (core->runflag != CHARSERVER_ST_RUNNING ||
  4988. i == ARRAYLENGTH(chr->server) ||
  4989. strcmp(l_user, chr->userid) != 0 ||
  4990. strcmp(l_pass, chr->passwd) != 0 ||
  4991. !sockt->allowed_ip_check(ipl))
  4992. {
  4993. chr->login_map_server_ack(fd, 3); // Failure
  4994. } else {
  4995. chr->login_map_server_ack(fd, 0); // Success
  4996.  
  4997. chr->server[i].fd = fd;
  4998. chr->server[i].ip = ntohl(RFIFOL(fd,54));
  4999. chr->server[i].port = ntohs(RFIFOW(fd,58));
  5000. chr->server[i].users = 0;
  5001. sockt->session[fd]->func_parse = chr->parse_frommap;
  5002. sockt->session[fd]->flag.server = 1;
  5003. sockt->realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
  5004. chr->mapif_init(fd);
  5005. }
  5006. sockt->datasync(fd, true);
  5007.  
  5008. RFIFOSKIP(fd,60);
  5009. }
  5010.  
  5011. void char_parse_char_pincode_check(int fd, struct char_session_data* sd) __attribute__((nonnull (2)));
  5012. void char_parse_char_pincode_check(int fd, struct char_session_data* sd)
  5013. {
  5014. if (RFIFOL(fd,2) == sd->account_id)
  5015. pincode->check(fd, sd);
  5016.  
  5017. RFIFOSKIP(fd, 10);
  5018. }
  5019.  
  5020. void char_parse_char_pincode_window(int fd, struct char_session_data* sd) __attribute__((nonnull (2)));
  5021. void char_parse_char_pincode_window(int fd, struct char_session_data* sd)
  5022. {
  5023. if (RFIFOL(fd,2) == sd->account_id)
  5024. pincode->sendstate(fd, sd, PINCODE_NOTSET);
  5025.  
  5026. RFIFOSKIP(fd, 6);
  5027. }
  5028.  
  5029. void char_parse_char_pincode_change(int fd, struct char_session_data* sd) __attribute__((nonnull (2)));
  5030. void char_parse_char_pincode_change(int fd, struct char_session_data* sd)
  5031. {
  5032. if (RFIFOL(fd,2) == sd->account_id)
  5033. pincode->change(fd, sd);
  5034.  
  5035. RFIFOSKIP(fd, 14);
  5036. }
  5037.  
  5038. void char_parse_char_pincode_first_pin(int fd, struct char_session_data* sd) __attribute__((nonnull (2)));
  5039. void char_parse_char_pincode_first_pin(int fd, struct char_session_data* sd)
  5040. {
  5041. if (RFIFOL(fd,2) == sd->account_id)
  5042. pincode->setnew (fd, sd);
  5043. RFIFOSKIP(fd, 10);
  5044. }
  5045.  
  5046. void char_parse_char_request_chars(int fd, struct char_session_data* sd)
  5047. {
  5048. chr->mmo_char_send099d(fd, sd);
  5049. RFIFOSKIP(fd,2);
  5050. }
  5051.  
  5052. void char_change_character_slot_ack(int fd, bool ret)
  5053. {
  5054. WFIFOHEAD(fd, 8);
  5055. WFIFOW(fd, 0) = 0x8d5;
  5056. WFIFOW(fd, 2) = 8;
  5057. WFIFOW(fd, 4) = ret?0:1;
  5058. WFIFOW(fd, 6) = 0;/* we enforce it elsewhere, go 0 */
  5059. WFIFOSET(fd, 8);
  5060. }
  5061.  
  5062. void char_parse_char_move_character(int fd, struct char_session_data* sd)
  5063. {
  5064. bool ret = chr->char_slotchange(sd, fd, RFIFOW(fd, 2), RFIFOW(fd, 4));
  5065. chr->change_character_slot_ack(fd, ret);
  5066. /* for some stupid reason it requires the char data again (gravity -_-) */
  5067. if( ret )
  5068. #if PACKETVER >= 20130000
  5069. chr->mmo_char_send099d(fd, sd);
  5070. #else
  5071. chr->mmo_char_send_characters(fd, sd);
  5072. #endif
  5073. RFIFOSKIP(fd, 8);
  5074. }
  5075.  
  5076. int char_parse_char_unknown_packet(int fd, uint32 ipl)
  5077. {
  5078. ShowError("chr->parse_char: Received unknown packet "CL_WHITE"0x%x"CL_RESET" from ip '"CL_WHITE"%s"CL_RESET"'! Disconnecting!\n", RFIFOW(fd,0), sockt->ip2str(ipl, NULL));
  5079. sockt->eof(fd);
  5080. return 1;
  5081. }
  5082.  
  5083. int char_parse_char(int fd)
  5084. {
  5085. unsigned short cmd;
  5086. struct char_session_data* sd;
  5087. uint32 ipl = sockt->session[fd]->client_addr;
  5088.  
  5089. sd = (struct char_session_data*)sockt->session[fd]->session_data;
  5090.  
  5091. // disconnect any player if no login-server.
  5092. if(chr->login_fd < 0)
  5093. sockt->eof(fd);
  5094.  
  5095. if(sockt->session[fd]->flag.eof)
  5096. {
  5097. if( sd != NULL && sd->auth ) {
  5098. // already authed client
  5099. struct online_char_data* data = (struct online_char_data*)idb_get(chr->online_char_db, sd->account_id);
  5100. if( data != NULL && data->fd == fd)
  5101. data->fd = -1;
  5102. if( data == NULL || data->server == -1) //If it is not in any server, send it offline. [Skotlex]
  5103. chr->set_char_offline(-1,sd->account_id);
  5104. }
  5105. sockt->close(fd);
  5106. return 0;
  5107. }
  5108.  
  5109. while( RFIFOREST(fd) >= 2 ) {
  5110. //For use in packets that depend on an sd being present [Skotlex]
  5111. #define FIFOSD_CHECK(rest) do { if(RFIFOREST(fd) < (rest)) return 0; if (sd==NULL || !sd->auth) { RFIFOSKIP(fd,(rest)); return 0; } } while (0)
  5112.  
  5113. if (VECTOR_LENGTH(HPM->packets[hpParse_Char]) > 0) {
  5114. int result = HPM->parse_packets(fd,hpParse_Char);
  5115. if (result == 1)
  5116. continue;
  5117. if (result == 2)
  5118. return 0;
  5119. }
  5120.  
  5121. cmd = RFIFOW(fd,0);
  5122.  
  5123. switch( cmd ) {
  5124. // request to connect
  5125. // 0065 <account id>.L <login id1>.L <login id2>.L <???>.W <sex>.B
  5126. case 0x65:
  5127. if( RFIFOREST(fd) < 17 )
  5128. return 0;
  5129. {
  5130. chr->parse_char_connect(fd, sd, ipl);
  5131. }
  5132. break;
  5133.  
  5134. // char select
  5135. case 0x66:
  5136. FIFOSD_CHECK(3);
  5137. {
  5138. chr->parse_char_select(fd, sd, ipl);
  5139. }
  5140. break;
  5141.  
  5142. // create new char
  5143. #if PACKETVER >= 20120307
  5144. // S 0970 <name>.24B <slot>.B <hair color>.W <hair style>.W
  5145. case 0x970:
  5146. {
  5147. FIFOSD_CHECK(31);
  5148. #else
  5149. // S 0067 <name>.24B <str>.B <agi>.B <vit>.B <int>.B <dex>.B <luk>.B <slot>.B <hair color>.W <hair style>.W
  5150. case 0x67:
  5151. {
  5152. FIFOSD_CHECK(37);
  5153. #endif
  5154.  
  5155. chr->parse_char_create_new_char(fd, sd);
  5156. }
  5157. break;
  5158.  
  5159. // delete char
  5160. case 0x68:
  5161. // 2004-04-19aSakexe+ langtype 12 char deletion packet
  5162. case 0x1fb:
  5163. if (cmd == 0x68) FIFOSD_CHECK(46);
  5164. if (cmd == 0x1fb) FIFOSD_CHECK(56);
  5165. {
  5166. chr->parse_char_delete_char(fd, sd, cmd);
  5167. }
  5168. break;
  5169.  
  5170. // client keep-alive packet (every 12 seconds)
  5171. // R 0187 <account ID>.l
  5172. case 0x187:
  5173. if (RFIFOREST(fd) < 6)
  5174. return 0;
  5175. chr->parse_char_ping(fd);
  5176. break;
  5177. // char rename request
  5178. // R 08fc <char ID>.l <new name>.24B
  5179. case 0x8fc:
  5180. FIFOSD_CHECK(30);
  5181. {
  5182. chr->parse_char_rename_char(fd, sd);
  5183. }
  5184. break;
  5185.  
  5186. // char rename request
  5187. // R 028d <account ID>.l <char ID>.l <new name>.24B
  5188. case 0x28d:
  5189. FIFOSD_CHECK(34);
  5190. {
  5191. chr->parse_char_rename_char2(fd, sd);
  5192. }
  5193. break;
  5194. //Confirm change name.
  5195. // 0x28f <char_id>.L
  5196. case 0x28f:
  5197. // 0: Successful
  5198. // 1: This character's name has already been changed. You cannot change a character's name more than once.
  5199. // 2: User information is not correct.
  5200. // 3: You have failed to change this character's name.
  5201. // 4: Another user is using this character name, so please select another one.
  5202. FIFOSD_CHECK(6);
  5203. {
  5204. chr->parse_char_rename_char_confirm(fd, sd);
  5205. }
  5206. break;
  5207.  
  5208. // captcha code request (not implemented)
  5209. // R 07e5 <?>.w <aid>.l
  5210. case 0x7e5:
  5211. chr->parse_char_request_captcha(fd);
  5212. break;
  5213.  
  5214. // captcha code check (not implemented)
  5215. // R 07e7 <len>.w <aid>.l <code>.b10 <?>.b14
  5216. case 0x7e7:
  5217. chr->parse_char_check_captcha(fd);
  5218. break;
  5219.  
  5220. // deletion timer request
  5221. case 0x827:
  5222. FIFOSD_CHECK(6);
  5223. chr->parse_char_delete2_req(fd, sd);
  5224. break;
  5225.  
  5226. // deletion accept request
  5227. case 0x829:
  5228. FIFOSD_CHECK(12);
  5229. chr->parse_char_delete2_accept(fd, sd);
  5230. break;
  5231.  
  5232. // deletion cancel request
  5233. case 0x82b:
  5234. FIFOSD_CHECK(6);
  5235. chr->parse_char_delete2_cancel(fd, sd);
  5236. break;
  5237.  
  5238. // login as map-server
  5239. case 0x2af8:
  5240. if (RFIFOREST(fd) < 60)
  5241. return 0;
  5242. {
  5243. chr->parse_char_login_map_server(fd, ipl);
  5244. }
  5245. return 0; // avoid processing of follow-up packets here
  5246.  
  5247. // checks the entered pin
  5248. case 0x8b8:
  5249. FIFOSD_CHECK(10);
  5250. chr->parse_char_pincode_check(fd, sd);
  5251. break;
  5252.  
  5253. // request for PIN window
  5254. case 0x8c5:
  5255. FIFOSD_CHECK(6);
  5256. chr->parse_char_pincode_window(fd, sd);
  5257. break;
  5258.  
  5259. // pincode change request
  5260. case 0x8be:
  5261. FIFOSD_CHECK(14);
  5262. chr->parse_char_pincode_change(fd, sd);
  5263. break;
  5264.  
  5265. // activate PIN system and set first PIN
  5266. case 0x8ba:
  5267. FIFOSD_CHECK(10);
  5268. chr->parse_char_pincode_first_pin(fd, sd);
  5269. break;
  5270.  
  5271. case 0x9a1:
  5272. FIFOSD_CHECK(2);
  5273. chr->parse_char_request_chars(fd, sd);
  5274. break;
  5275.  
  5276. /* 0x8d4 <from>.W <to>.W <unused>.W (2+2+2+2) */
  5277. case 0x8d4:
  5278. FIFOSD_CHECK(8);
  5279. {
  5280. chr->parse_char_move_character(fd, sd);
  5281. }
  5282. break;
  5283.  
  5284. // unknown packet received
  5285. default:
  5286. if (chr->parse_char_unknown_packet(fd, ipl))
  5287. return 0;
  5288. }
  5289. }
  5290.  
  5291. RFIFOFLUSH(fd);
  5292. return 0;
  5293. }
  5294.  
  5295. int mapif_sendall(unsigned char *buf, unsigned int len)
  5296. {
  5297. int i, c;
  5298.  
  5299. nullpo_ret(buf);
  5300. c = 0;
  5301. for(i = 0; i < ARRAYLENGTH(chr->server); i++) {
  5302. int fd;
  5303. if ((fd = chr->server[i].fd) > 0) {
  5304. WFIFOHEAD(fd,len);
  5305. memcpy(WFIFOP(fd,0), buf, len);
  5306. WFIFOSET(fd,len);
  5307. c++;
  5308. }
  5309. }
  5310.  
  5311. return c;
  5312. }
  5313.  
  5314. int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len)
  5315. {
  5316. int i, c;
  5317.  
  5318. nullpo_ret(buf);
  5319. c = 0;
  5320. for(i = 0; i < ARRAYLENGTH(chr->server); i++) {
  5321. int fd;
  5322. if ((fd = chr->server[i].fd) > 0 && fd != sfd) {
  5323. WFIFOHEAD(fd,len);
  5324. memcpy(WFIFOP(fd,0), buf, len);
  5325. WFIFOSET(fd,len);
  5326. c++;
  5327. }
  5328. }
  5329.  
  5330. return c;
  5331. }
  5332.  
  5333. int mapif_send(int fd, unsigned char *buf, unsigned int len)
  5334. {
  5335. nullpo_ret(buf);
  5336. if (fd >= 0) {
  5337. int i;
  5338. ARR_FIND( 0, ARRAYLENGTH(chr->server), i, fd == chr->server[i].fd );
  5339. if( i < ARRAYLENGTH(chr->server) )
  5340. {
  5341. WFIFOHEAD(fd,len);
  5342. memcpy(WFIFOP(fd,0), buf, len);
  5343. WFIFOSET(fd,len);
  5344. return 1;
  5345. }
  5346. }
  5347. return 0;
  5348. }
  5349.  
  5350. void mapif_send_users_count(int users)
  5351. {
  5352. uint8 buf[6];
  5353. // send number of players to all map-servers
  5354. WBUFW(buf,0) = 0x2b00;
  5355. WBUFL(buf,2) = users;
  5356. mapif->sendall(buf,6);
  5357. }
  5358.  
  5359. int char_broadcast_user_count(int tid, int64 tick, int id, intptr_t data) {
  5360. int users = chr->count_users();
  5361.  
  5362. // only send an update when needed
  5363. static int prev_users = 0;
  5364. if( prev_users == users )
  5365. return 0;
  5366. prev_users = users;
  5367.  
  5368. if( chr->login_fd > 0 && sockt->session[chr->login_fd] )
  5369. {
  5370. // send number of user to login server
  5371. loginif->send_users_count(users);
  5372. }
  5373.  
  5374. mapif->send_users_count(users);
  5375.  
  5376. return 0;
  5377. }
  5378.  
  5379. /**
  5380. * Load this character's account id into the 'online accounts' packet
  5381. * @see DBApply
  5382. */
  5383. static int char_send_accounts_tologin_sub(DBKey key, DBData *data, va_list ap)
  5384. {
  5385. struct online_char_data* character = DB->data2ptr(data);
  5386. int* i = va_arg(ap, int*);
  5387.  
  5388. nullpo_ret(character);
  5389. if(character->server > -1)
  5390. {
  5391. WFIFOL(chr->login_fd,8+(*i)*4) = character->account_id;
  5392. (*i)++;
  5393. return 1;
  5394. }
  5395. return 0;
  5396. }
  5397.  
  5398. int char_send_accounts_tologin(int tid, int64 tick, int id, intptr_t data) {
  5399. if (chr->login_fd > 0 && sockt->session[chr->login_fd])
  5400. {
  5401. // send account list to login server
  5402. int users = chr->online_char_db->size(chr->online_char_db);
  5403. int i = 0;
  5404.  
  5405. WFIFOHEAD(chr->login_fd,8+users*4);
  5406. WFIFOW(chr->login_fd,0) = 0x272d;
  5407. chr->online_char_db->foreach(chr->online_char_db, chr->send_accounts_tologin_sub, &i, users);
  5408. WFIFOW(chr->login_fd,2) = 8+ i*4;
  5409. WFIFOL(chr->login_fd,4) = i;
  5410. WFIFOSET(chr->login_fd,WFIFOW(chr->login_fd,2));
  5411. }
  5412. return 0;
  5413. }
  5414.  
  5415. int char_check_connect_login_server(int tid, int64 tick, int id, intptr_t data) {
  5416. if (chr->login_fd > 0 && sockt->session[chr->login_fd] != NULL)
  5417. return 0;
  5418.  
  5419. ShowInfo("Attempt to connect to login-server...\n");
  5420.  
  5421. if ((chr->login_fd = sockt->make_connection(login_ip, login_port, NULL)) == -1) { //Try again later. [Skotlex]
  5422. chr->login_fd = 0;
  5423. return 0;
  5424. }
  5425.  
  5426. sockt->session[chr->login_fd]->func_parse = chr->parse_fromlogin;
  5427. sockt->session[chr->login_fd]->flag.server = 1;
  5428. sockt->realloc_fifo(chr->login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
  5429.  
  5430. loginif->connect_to_server();
  5431.  
  5432. return 1;
  5433. }
  5434.  
  5435. //------------------------------------------------
  5436. //Invoked 15 seconds after mapif->disconnectplayer in case the map server doesn't
  5437. //replies/disconnect the player we tried to kick. [Skotlex]
  5438. //------------------------------------------------
  5439. static int char_waiting_disconnect(int tid, int64 tick, int id, intptr_t data) {
  5440. struct online_char_data* character;
  5441. if ((character = (struct online_char_data*)idb_get(chr->online_char_db, id)) != NULL && character->waiting_disconnect == tid) {
  5442. //Mark it offline due to timeout.
  5443. character->waiting_disconnect = INVALID_TIMER;
  5444. chr->set_char_offline(character->char_id, character->account_id);
  5445. }
  5446. return 0;
  5447. }
  5448.  
  5449. /**
  5450. * @see DBApply
  5451. */
  5452. static int char_online_data_cleanup_sub(DBKey key, DBData *data, va_list ap)
  5453. {
  5454. struct online_char_data *character= DB->data2ptr(data);
  5455. nullpo_ret(character);
  5456. if (character->fd != -1)
  5457. return 0; //Character still connected
  5458. if (character->server == -2) //Unknown server.. set them offline
  5459. chr->set_char_offline(character->char_id, character->account_id);
  5460. if (character->server < 0)
  5461. //Free data from players that have not been online for a while.
  5462. db_remove(chr->online_char_db, key);
  5463. return 0;
  5464. }
  5465.  
  5466. static int char_online_data_cleanup(int tid, int64 tick, int id, intptr_t data) {
  5467. chr->online_char_db->foreach(chr->online_char_db, chr->online_data_cleanup_sub);
  5468. return 0;
  5469. }
  5470.  
  5471. void char_sql_config_read(const char* cfgName)
  5472. {
  5473. char line[1024], w1[1024], w2[1024];
  5474. FILE* fp;
  5475.  
  5476. if ((fp = fopen(cfgName, "r")) == NULL) {
  5477. ShowError("File not found: %s\n", cfgName);
  5478. return;
  5479. }
  5480.  
  5481. while(fgets(line, sizeof(line), fp))
  5482. {
  5483. if(line[0] == '/' && line[1] == '/')
  5484. continue;
  5485.  
  5486. if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2)
  5487. continue;
  5488.  
  5489. if(!strcmpi(w1,"char_db"))
  5490. safestrncpy(char_db, w2, sizeof(char_db));
  5491. else if(!strcmpi(w1,"scdata_db"))
  5492. safestrncpy(scdata_db, w2, sizeof(scdata_db));
  5493. else if(!strcmpi(w1,"cart_db"))
  5494. safestrncpy(cart_db, w2, sizeof(cart_db));
  5495. else if(!strcmpi(w1,"inventory_db"))
  5496. safestrncpy(inventory_db, w2, sizeof(inventory_db));
  5497. else if(!strcmpi(w1,"charlog_db"))
  5498. safestrncpy(charlog_db, w2, sizeof(charlog_db));
  5499. else if(!strcmpi(w1,"storage_db"))
  5500. safestrncpy(storage_db, w2, sizeof(storage_db));
  5501. else if(!strcmpi(w1,"skill_db"))
  5502. safestrncpy(skill_db, w2, sizeof(skill_db));
  5503. else if(!strcmpi(w1,"interlog_db"))
  5504. safestrncpy(interlog_db, w2, sizeof(interlog_db));
  5505. else if(!strcmpi(w1,"memo_db"))
  5506. safestrncpy(memo_db, w2, sizeof(memo_db));
  5507. else if(!strcmpi(w1,"guild_db"))
  5508. safestrncpy(guild_db, w2, sizeof(guild_db));
  5509. else if(!strcmpi(w1,"guild_alliance_db"))
  5510. safestrncpy(guild_alliance_db, w2, sizeof(guild_alliance_db));
  5511. else if(!strcmpi(w1,"guild_castle_db"))
  5512. safestrncpy(guild_castle_db, w2, sizeof(guild_castle_db));
  5513. else if(!strcmpi(w1,"guild_expulsion_db"))
  5514. safestrncpy(guild_expulsion_db, w2, sizeof(guild_expulsion_db));
  5515. else if(!strcmpi(w1,"guild_member_db"))
  5516. safestrncpy(guild_member_db, w2, sizeof(guild_member_db));
  5517. else if(!strcmpi(w1,"guild_skill_db"))
  5518. safestrncpy(guild_skill_db, w2, sizeof(guild_skill_db));
  5519. else if(!strcmpi(w1,"guild_position_db"))
  5520. safestrncpy(guild_position_db, w2, sizeof(guild_position_db));
  5521. else if(!strcmpi(w1,"guild_storage_db"))
  5522. safestrncpy(guild_storage_db, w2, sizeof(guild_storage_db));
  5523. else if(!strcmpi(w1,"party_db"))
  5524. safestrncpy(party_db, w2, sizeof(party_db));
  5525. else if(!strcmpi(w1,"pet_db"))
  5526. safestrncpy(pet_db, w2, sizeof(pet_db));
  5527. else if(!strcmpi(w1,"mail_db"))
  5528. safestrncpy(mail_db, w2, sizeof(mail_db));
  5529. else if(!strcmpi(w1,"auction_db"))
  5530. safestrncpy(auction_db, w2, sizeof(auction_db));
  5531. else if(!strcmpi(w1,"friend_db"))
  5532. safestrncpy(friend_db, w2, sizeof(friend_db));
  5533. else if(!strcmpi(w1,"hotkey_db"))
  5534. safestrncpy(hotkey_db, w2, sizeof(hotkey_db));
  5535. else if(!strcmpi(w1,"quest_db"))
  5536. safestrncpy(quest_db,w2,sizeof(quest_db));
  5537. else if(!strcmpi(w1,"homunculus_db"))
  5538. safestrncpy(homunculus_db,w2,sizeof(homunculus_db));
  5539. else if(!strcmpi(w1,"skill_homunculus_db"))
  5540. safestrncpy(skill_homunculus_db,w2,sizeof(skill_homunculus_db));
  5541. else if(!strcmpi(w1,"mercenary_db"))
  5542. safestrncpy(mercenary_db,w2,sizeof(mercenary_db));
  5543. else if(!strcmpi(w1,"mercenary_owner_db"))
  5544. safestrncpy(mercenary_owner_db,w2,sizeof(mercenary_owner_db));
  5545. else if(!strcmpi(w1,"ragsrvinfo_db"))
  5546. safestrncpy(ragsrvinfo_db,w2,sizeof(ragsrvinfo_db));
  5547. else if(!strcmpi(w1,"elemental_db"))
  5548. safestrncpy(elemental_db,w2,sizeof(elemental_db));
  5549. else if(!strcmpi(w1,"account_data_db"))
  5550. safestrncpy(account_data_db,w2,sizeof(account_data_db));
  5551. else if(!strcmpi(w1,"char_reg_num_db"))
  5552. safestrncpy(char_reg_num_db, w2, sizeof(char_reg_num_db));
  5553. else if(!strcmpi(w1,"char_reg_str_db"))
  5554. safestrncpy(char_reg_str_db, w2, sizeof(char_reg_str_db));
  5555. else if(!strcmpi(w1,"acc_reg_str_db"))
  5556. safestrncpy(acc_reg_str_db, w2, sizeof(acc_reg_str_db));
  5557. else if(!strcmpi(w1,"acc_reg_num_db"))
  5558. safestrncpy(acc_reg_num_db, w2, sizeof(acc_reg_num_db));
  5559. //support the import command, just like any other config
  5560. else if(!strcmpi(w1,"import"))
  5561. chr->sql_config_read(w2);
  5562. else
  5563. HPM->parseConf(w1, w2, HPCT_CHAR_INTER);
  5564. }
  5565. fclose(fp);
  5566. ShowInfo("Done reading %s.\n", cfgName);
  5567. }
  5568.  
  5569. void char_config_dispatch(char *w1, char *w2) {
  5570. bool (*dispatch_to[]) (char *w1, char *w2) = {
  5571. /* as many as it needs */
  5572. pincode->config_read
  5573. };
  5574. int i, len = ARRAYLENGTH(dispatch_to);
  5575. for(i = 0; i < len; i++) {
  5576. if( (*dispatch_to[i])(w1,w2) )
  5577. break;/* we found who this belongs to, can stop */
  5578. }
  5579. if (i == len)
  5580. HPM->parseConf(w1, w2, HPCT_CHAR);
  5581. }
  5582.  
  5583. int char_config_read(const char* cfgName)
  5584. {
  5585. char line[1024], w1[1024], w2[1024];
  5586. FILE* fp = fopen(cfgName, "r");
  5587.  
  5588. if (fp == NULL) {
  5589. ShowError("Configuration file not found: %s.\n", cfgName);
  5590. return 1;
  5591. }
  5592.  
  5593. while(fgets(line, sizeof(line), fp)) {
  5594. if (line[0] == '/' && line[1] == '/')
  5595. continue;
  5596.  
  5597. if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2)
  5598. continue;
  5599.  
  5600. remove_control_chars(w1);
  5601. remove_control_chars(w2);
  5602. if(strcmpi(w1,"timestamp_format") == 0) {
  5603. safestrncpy(showmsg->timestamp_format, w2, sizeof(showmsg->timestamp_format));
  5604. } else if(strcmpi(w1,"console_silent")==0){
  5605. showmsg->silent = atoi(w2);
  5606. if (showmsg->silent) /* only bother if its actually enabled */
  5607. ShowInfo("Console Silent Setting: %d\n", atoi(w2));
  5608. } else if(strcmpi(w1,"stdout_with_ansisequence")==0){
  5609. showmsg->stdout_with_ansisequence = config_switch(w2) ? true : false;
  5610. } else if (strcmpi(w1, "userid") == 0) {
  5611. safestrncpy(chr->userid, w2, sizeof(chr->userid));
  5612. } else if (strcmpi(w1, "passwd") == 0) {
  5613. safestrncpy(chr->passwd, w2, sizeof(chr->passwd));
  5614. } else if (strcmpi(w1, "server_name") == 0) {
  5615. safestrncpy(chr->server_name, w2, sizeof(chr->server_name));
  5616. } else if (strcmpi(w1, "wisp_server_name") == 0) {
  5617. if (strlen(w2) >= 4) {
  5618. safestrncpy(wisp_server_name, w2, sizeof(wisp_server_name));
  5619. }
  5620. } else if (strcmpi(w1, "login_ip") == 0) {
  5621. login_ip = sockt->host2ip(w2);
  5622. if (login_ip) {
  5623. char ip_str[16];
  5624. safestrncpy(login_ip_str, w2, sizeof(login_ip_str));
  5625. ShowStatus("Login server IP address : %s -> %s\n", w2, sockt->ip2str(login_ip, ip_str));
  5626. }
  5627. } else if (strcmpi(w1, "login_port") == 0) {
  5628. login_port = atoi(w2);
  5629. } else if (strcmpi(w1, "char_ip") == 0) {
  5630. chr->ip = sockt->host2ip(w2);
  5631. if (chr->ip) {
  5632. char ip_str[16];
  5633. safestrncpy(char_ip_str, w2, sizeof(char_ip_str));
  5634. ShowStatus("Character server IP address : %s -> %s\n", w2, sockt->ip2str(chr->ip, ip_str));
  5635. }
  5636. } else if (strcmpi(w1, "bind_ip") == 0) {
  5637. bind_ip = sockt->host2ip(w2);
  5638. if (bind_ip) {
  5639. char ip_str[16];
  5640. safestrncpy(bind_ip_str, w2, sizeof(bind_ip_str));
  5641. ShowStatus("Character server binding IP address : %s -> %s\n", w2, sockt->ip2str(bind_ip, ip_str));
  5642. }
  5643. } else if (strcmpi(w1, "char_port") == 0) {
  5644. chr->port = atoi(w2);
  5645. } else if (strcmpi(w1, "char_server_type") == 0) {
  5646. chr->server_type = atoi(w2);
  5647. } else if (strcmpi(w1, "char_new") == 0) {
  5648. char_new = (bool)atoi(w2);
  5649. } else if (strcmpi(w1, "char_new_display") == 0) {
  5650. chr->new_display = atoi(w2);
  5651. } else if (strcmpi(w1, "max_connect_user") == 0) {
  5652. max_connect_user = atoi(w2);
  5653. if (max_connect_user < -1)
  5654. max_connect_user = -1; // unlimited online players
  5655. } else if(strcmpi(w1, "gm_allow_group") == 0) {
  5656. gm_allow_group = atoi(w2);
  5657. } else if (strcmpi(w1, "autosave_time") == 0) {
  5658. autosave_interval = atoi(w2) * 1000;
  5659. if (autosave_interval <= 0)
  5660. autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
  5661. } else if (strcmpi(w1, "save_log") == 0) {
  5662. save_log = config_switch(w2);
  5663. }
  5664. #ifdef RENEWAL
  5665. else if (strcmpi(w1, "start_point_re") == 0) {
  5666. char map[MAP_NAME_LENGTH_EXT];
  5667. int x, y;
  5668. if (sscanf(w2, "%15[^,],%d,%d", map, &x, &y) < 3)
  5669. continue;
  5670. start_point.map = mapindex->name2id(map);
  5671. if (!start_point.map)
  5672. ShowError("Specified start_point_re '%s' not found in map-index cache.\n", map);
  5673. start_point.x = x;
  5674. start_point.y = y;
  5675. }
  5676. #else
  5677. else if (strcmpi(w1, "start_point_pre") == 0) {
  5678. char map[MAP_NAME_LENGTH_EXT];
  5679. int x, y;
  5680. if (sscanf(w2, "%15[^,],%d,%d", map, &x, &y) < 3)
  5681. continue;
  5682. start_point.map = mapindex->name2id(map);
  5683. if (!start_point.map)
  5684. ShowError("Specified start_point_pre '%s' not found in map-index cache.\n", map);
  5685. start_point.x = x;
  5686. start_point.y = y;
  5687. }
  5688. #endif
  5689. else if (strcmpi(w1, "start_items") == 0) {
  5690. int i;
  5691. char *split;
  5692.  
  5693. i = 0;
  5694. split = strtok(w2, ",");
  5695. while (split != NULL && i < MAX_START_ITEMS * 4) {
  5696. char *split2 = split;
  5697. split = strtok(NULL, ",");
  5698. start_items[i] = atoi(split2);
  5699.  
  5700. if (start_items[i] < 0)
  5701. start_items[i] = 0;
  5702.  
  5703. ++i;
  5704. }
  5705.  
  5706. // Format is: id1,quantity1,stackable1,slot1,idN,quantityN,stackableN,slotN
  5707. if( i%4 )
  5708. {
  5709. ShowWarning("chr->config_read: There are not enough parameters in start_items, ignoring last item...\n");
  5710. if( i%4 == 1 )
  5711. start_items[i-1] = 0;
  5712. else if( i%4 == 2 )
  5713. start_items[i-2] = 0;
  5714. else
  5715. start_items[i-3] = 0;
  5716. }
  5717. } else if (strcmpi(w1, "start_zeny") == 0) {
  5718. start_zeny = atoi(w2);
  5719. if (start_zeny < 0)
  5720. start_zeny = 0;
  5721. } else if(strcmpi(w1,"log_char")==0) {
  5722. log_char = atoi(w2); //log char or not [devil]
  5723. } else if (strcmpi(w1, "unknown_char_name") == 0) {
  5724. safestrncpy(unknown_char_name, w2, sizeof(unknown_char_name));
  5725. unknown_char_name[NAME_LENGTH-1] = '\0';
  5726. } else if (strcmpi(w1, "name_ignoring_case") == 0) {
  5727. name_ignoring_case = (bool)config_switch(w2);
  5728. } else if (strcmpi(w1, "char_name_option") == 0) {
  5729. char_name_option = atoi(w2);
  5730. } else if (strcmpi(w1, "char_name_letters") == 0) {
  5731. safestrncpy(char_name_letters, w2, sizeof(char_name_letters));
  5732. } else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus]
  5733. char_del_level = atoi(w2);
  5734. } else if (strcmpi(w1, "char_del_delay") == 0) {
  5735. char_del_delay = atoi(w2);
  5736. } else if (strcmpi(w1, "char_aegis_delete") == 0) {
  5737. char_aegis_delete = atoi(w2);
  5738. } else if(strcmpi(w1,"db_path")==0) {
  5739. safestrncpy(db_path, w2, sizeof(db_path));
  5740. } else if (strcmpi(w1, "fame_list_alchemist") == 0) {
  5741. fame_list_size_chemist = atoi(w2);
  5742. if (fame_list_size_chemist > MAX_FAME_LIST) {
  5743. ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST);
  5744. fame_list_size_chemist = MAX_FAME_LIST;
  5745. }
  5746. } else if (strcmpi(w1, "fame_list_blacksmith") == 0) {
  5747. fame_list_size_smith = atoi(w2);
  5748. if (fame_list_size_smith > MAX_FAME_LIST) {
  5749. ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST);
  5750. fame_list_size_smith = MAX_FAME_LIST;
  5751. }
  5752. } else if (strcmpi(w1, "fame_list_taekwon") == 0) {
  5753. fame_list_size_taekwon = atoi(w2);
  5754. if (fame_list_size_taekwon > MAX_FAME_LIST) {
  5755. ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST);
  5756. fame_list_size_taekwon = MAX_FAME_LIST;
  5757. }
  5758. } else if (strcmpi(w1, "guild_exp_rate") == 0) {
  5759. guild_exp_rate = atoi(w2);
  5760. } else if (strcmpi(w1, "char_maintenance_min_group_id") == 0) {
  5761. char_maintenance_min_group_id = atoi(w2);
  5762. } else if (strcmpi(w1, "import") == 0) {
  5763. chr->config_read(w2);
  5764. } else
  5765. chr->config_dispatch(w1,w2);
  5766. }
  5767. fclose(fp);
  5768.  
  5769. ShowInfo("Done reading %s.\n", cfgName);
  5770. return 0;
  5771. }
  5772.  
  5773. int do_final(void) {
  5774. int i;
  5775.  
  5776. ShowStatus("Terminating...\n");
  5777.  
  5778. HPM->event(HPET_FINAL);
  5779.  
  5780. chr->set_all_offline(-1);
  5781. chr->set_all_offline_sql();
  5782.  
  5783. inter->final();
  5784.  
  5785. sockt->flush_fifos();
  5786.  
  5787. do_final_mapif();
  5788. loginif->final();
  5789.  
  5790. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s`", ragsrvinfo_db) )
  5791. Sql_ShowDebug(inter->sql_handle);
  5792.  
  5793. chr->char_db_->destroy(chr->char_db_, NULL);
  5794. chr->online_char_db->destroy(chr->online_char_db, NULL);
  5795. auth_db->destroy(auth_db, NULL);
  5796.  
  5797. if( chr->char_fd != -1 ) {
  5798. sockt->close(chr->char_fd);
  5799. chr->char_fd = -1;
  5800. }
  5801.  
  5802. HPM_char_do_final();
  5803.  
  5804. SQL->Free(inter->sql_handle);
  5805. mapindex->final();
  5806.  
  5807. for (i = 0; i < MAX_MAP_SERVERS; i++)
  5808. VECTOR_CLEAR(chr->server[i].maps);
  5809.  
  5810. aFree(chr->CHAR_CONF_NAME);
  5811. aFree(chr->NET_CONF_NAME);
  5812. aFree(chr->SQL_CONF_NAME);
  5813. aFree(chr->INTER_CONF_NAME);
  5814.  
  5815. HPM->event(HPET_POST_FINAL);
  5816.  
  5817. ShowStatus("Finished.\n");
  5818. return EXIT_SUCCESS;
  5819. }
  5820.  
  5821. //------------------------------
  5822. // Function called when the server
  5823. // has received a crash signal.
  5824. //------------------------------
  5825. void do_abort(void)
  5826. {
  5827. }
  5828.  
  5829. void set_server_type(void) {
  5830. SERVER_TYPE = SERVER_TYPE_CHAR;
  5831. }
  5832.  
  5833. /// Called when a terminate signal is received.
  5834. void do_shutdown(void)
  5835. {
  5836. if( core->runflag != CHARSERVER_ST_SHUTDOWN )
  5837. {
  5838. int id;
  5839. core->runflag = CHARSERVER_ST_SHUTDOWN;
  5840. ShowStatus("Shutting down...\n");
  5841. // TODO proper shutdown procedure; wait for acks?, kick all characters, ... [FlavoJS]
  5842. for( id = 0; id < ARRAYLENGTH(chr->server); ++id )
  5843. mapif->server_reset(id);
  5844. loginif->check_shutdown();
  5845. sockt->flush_fifos();
  5846. core->runflag = CORE_ST_STOP;
  5847. }
  5848. }
  5849.  
  5850. /**
  5851. * --char-config handler
  5852. *
  5853. * Overrides the default char configuration file.
  5854. * @see cmdline->exec
  5855. */
  5856. static CMDLINEARG(charconfig)
  5857. {
  5858. aFree(chr->CHAR_CONF_NAME);
  5859. chr->CHAR_CONF_NAME = aStrdup(params);
  5860. return true;
  5861. }
  5862. /**
  5863. * --inter-config handler
  5864. *
  5865. * Overrides the default inter-server configuration file.
  5866. * @see cmdline->exec
  5867. */
  5868. static CMDLINEARG(interconfig)
  5869. {
  5870. aFree(chr->INTER_CONF_NAME);
  5871. chr->INTER_CONF_NAME = aStrdup(params);
  5872. return true;
  5873. }
  5874. /**
  5875. * --net-config handler
  5876. *
  5877. * Overrides the default network configuration file.
  5878. * @see cmdline->exec
  5879. */
  5880. static CMDLINEARG(netconfig)
  5881. {
  5882. aFree(chr->NET_CONF_NAME);
  5883. chr->NET_CONF_NAME = aStrdup(params);
  5884. return true;
  5885. }
  5886. /**
  5887. * Initializes the command line arguments handlers.
  5888. */
  5889. void cmdline_args_init_local(void)
  5890. {
  5891. CMDLINEARG_DEF2(char-config, charconfig, "Alternative char-server configuration.", CMDLINE_OPT_PARAM);
  5892. CMDLINEARG_DEF2(inter-config, interconfig, "Alternative inter-server configuration.", CMDLINE_OPT_PARAM);
  5893. CMDLINEARG_DEF2(net-config, netconfig, "Alternative network configuration.", CMDLINE_OPT_PARAM);
  5894. }
  5895.  
  5896. int do_init(int argc, char **argv) {
  5897. int i;
  5898. memset(&skillid2idx, 0, sizeof(skillid2idx));
  5899.  
  5900. char_load_defaults();
  5901.  
  5902. chr->CHAR_CONF_NAME = aStrdup("conf/char-server.conf");
  5903. chr->NET_CONF_NAME = aStrdup("conf/network.conf");
  5904. chr->SQL_CONF_NAME = aStrdup("conf/inter-server.conf");
  5905. chr->INTER_CONF_NAME = aStrdup("conf/inter-server.conf");
  5906.  
  5907. for (i = 0; i < MAX_MAP_SERVERS; i++)
  5908. VECTOR_INIT(chr->server[i].maps);
  5909.  
  5910. HPM_char_do_init();
  5911. cmdline->exec(argc, argv, CMDLINE_OPT_PREINIT);
  5912. HPM->config_read();
  5913. HPM->event(HPET_PRE_INIT);
  5914.  
  5915. //Read map indexes
  5916. mapindex->init();
  5917.  
  5918. #ifdef RENEWAL
  5919. start_point.map = mapindex->name2id("iz_int");
  5920. #else
  5921. start_point.map = mapindex->name2id("new_1-1");
  5922. #endif
  5923.  
  5924. cmdline->exec(argc, argv, CMDLINE_OPT_NORMAL);
  5925. chr->config_read(chr->CHAR_CONF_NAME);
  5926. sockt->net_config_read(chr->NET_CONF_NAME);
  5927. chr->sql_config_read(chr->SQL_CONF_NAME);
  5928.  
  5929. if (strcmp(chr->userid, "s1")==0 && strcmp(chr->passwd, "p1")==0) {
  5930. ShowWarning("Using the default user/password s1/p1 is NOT RECOMMENDED.\n");
  5931. ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n");
  5932. ShowNotice("And then change the user/password to use in conf/char-server.conf (or conf/import/char_conf.txt)\n");
  5933. }
  5934.  
  5935. inter->init_sql(chr->INTER_CONF_NAME); // inter server configuration
  5936.  
  5937. auth_db = idb_alloc(DB_OPT_RELEASE_DATA);
  5938. chr->online_char_db = idb_alloc(DB_OPT_RELEASE_DATA);
  5939.  
  5940. HPM->event(HPET_INIT);
  5941.  
  5942. chr->mmo_char_sql_init();
  5943. chr->read_fame_list(); //Read fame lists.
  5944.  
  5945. if ((sockt->naddr_ != 0) && (!login_ip || !chr->ip)) {
  5946. char ip_str[16];
  5947. sockt->ip2str(sockt->addr_[0], ip_str);
  5948.  
  5949. if (sockt->naddr_ > 1)
  5950. ShowStatus("Multiple interfaces detected.. using %s as our IP address\n", ip_str);
  5951. else
  5952. ShowStatus("Defaulting to %s as our IP address\n", ip_str);
  5953. if (!login_ip) {
  5954. safestrncpy(login_ip_str, ip_str, sizeof(login_ip_str));
  5955. login_ip = sockt->str2ip(login_ip_str);
  5956. }
  5957. if (!chr->ip) {
  5958. safestrncpy(char_ip_str, ip_str, sizeof(char_ip_str));
  5959. chr->ip = sockt->str2ip(char_ip_str);
  5960. }
  5961. }
  5962.  
  5963. loginif->init();
  5964. do_init_mapif();
  5965.  
  5966. // periodically update the overall user count on all mapservers + login server
  5967. timer->add_func_list(chr->broadcast_user_count, "chr->broadcast_user_count");
  5968. timer->add_interval(timer->gettick() + 1000, chr->broadcast_user_count, 0, 0, 5 * 1000);
  5969.  
  5970. // Timer to clear (chr->online_char_db)
  5971. timer->add_func_list(chr->waiting_disconnect, "chr->waiting_disconnect");
  5972.  
  5973. // Online Data timers (checking if char still connected)
  5974. timer->add_func_list(chr->online_data_cleanup, "chr->online_data_cleanup");
  5975. timer->add_interval(timer->gettick() + 1000, chr->online_data_cleanup, 0, 0, 600 * 1000);
  5976.  
  5977. //Cleaning the tables for NULL entries @ startup [Sirius]
  5978. //Chardb clean
  5979. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `account_id` = '0'", char_db) )
  5980. Sql_ShowDebug(inter->sql_handle);
  5981.  
  5982. //guilddb clean
  5983. if( SQL_ERROR == SQL->Query(inter->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) )
  5984. Sql_ShowDebug(inter->sql_handle);
  5985.  
  5986. //guildmemberdb clean
  5987. if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '0' AND `account_id` = '0' AND `char_id` = '0'", guild_member_db) )
  5988. Sql_ShowDebug(inter->sql_handle);
  5989.  
  5990. sockt->set_defaultparse(chr->parse_char);
  5991.  
  5992. if ((chr->char_fd = sockt->make_listen_bind(bind_ip,chr->port)) == -1) {
  5993. ShowFatalError("Failed to bind to port '"CL_WHITE"%d"CL_RESET"'\n",chr->port);
  5994. exit(EXIT_FAILURE);
  5995. }
  5996.  
  5997. Sql_HerculesUpdateCheck(inter->sql_handle);
  5998. #ifdef CONSOLE_INPUT
  5999. console->input->setSQL(inter->sql_handle);
  6000. console->display_gplnotice();
  6001. #endif
  6002. ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", chr->port);
  6003.  
  6004. if( core->runflag != CORE_ST_STOP )
  6005. {
  6006. core->shutdown_callback = do_shutdown;
  6007. core->runflag = CHARSERVER_ST_RUNNING;
  6008. }
  6009.  
  6010. HPM->event(HPET_READY);
  6011.  
  6012. return 0;
  6013. }
  6014.  
  6015. void char_load_defaults(void)
  6016. {
  6017. mapindex_defaults();
  6018. pincode_defaults();
  6019. char_defaults();
  6020. loginif_defaults();
  6021. mapif_defaults();
  6022. inter_auction_defaults();
  6023. inter_elemental_defaults();
  6024. inter_guild_defaults();
  6025. inter_homunculus_defaults();
  6026. inter_mail_defaults();
  6027. inter_mercenary_defaults();
  6028. inter_party_defaults();
  6029. inter_pet_defaults();
  6030. inter_quest_defaults();
  6031. inter_storage_defaults();
  6032. inter_defaults();
  6033. geoip_defaults();
  6034. }
  6035.  
  6036. void char_defaults(void)
  6037. {
  6038. chr = &char_s;
  6039.  
  6040. memset(chr->server, 0, sizeof(chr->server));
  6041.  
  6042. chr->login_fd = 0;
  6043. chr->char_fd = -1;
  6044. chr->online_char_db = NULL;
  6045. chr->char_db_ = NULL;
  6046.  
  6047. memset(chr->userid, 0, sizeof(chr->userid));
  6048. memset(chr->passwd, 0, sizeof(chr->passwd));
  6049. memset(chr->server_name, 0, sizeof(chr->server_name));
  6050.  
  6051. chr->ip = 0;
  6052. chr->port = 6121;
  6053. chr->server_type = 0;
  6054. chr->new_display = 0;
  6055.  
  6056. chr->waiting_disconnect = char_waiting_disconnect;
  6057. chr->delete_char_sql = char_delete_char_sql;
  6058. chr->create_online_char_data = char_create_online_char_data;
  6059. chr->set_account_online = char_set_account_online;
  6060. chr->set_account_offline = char_set_account_offline;
  6061. chr->set_char_charselect = char_set_char_charselect;
  6062. chr->set_char_online = char_set_char_online;
  6063. chr->set_char_offline = char_set_char_offline;
  6064. chr->db_setoffline = char_db_setoffline;
  6065. chr->db_kickoffline = char_db_kickoffline;
  6066. chr->set_login_all_offline = char_set_login_all_offline;
  6067. chr->set_all_offline = char_set_all_offline;
  6068. chr->set_all_offline_sql = char_set_all_offline_sql;
  6069. chr->create_charstatus = char_create_charstatus;
  6070. chr->mmo_char_tosql = char_mmo_char_tosql;
  6071. chr->memitemdata_to_sql = char_memitemdata_to_sql;
  6072. chr->inventory_to_sql = char_inventory_to_sql;
  6073. chr->mmo_gender = char_mmo_gender;
  6074. chr->mmo_chars_fromsql = char_mmo_chars_fromsql;
  6075. chr->mmo_char_fromsql = char_mmo_char_fromsql;
  6076. chr->mmo_char_sql_init = char_mmo_char_sql_init;
  6077. chr->char_slotchange = char_char_slotchange;
  6078. chr->rename_char_sql = char_rename_char_sql;
  6079. chr->check_char_name = char_check_char_name;
  6080. chr->make_new_char_sql = char_make_new_char_sql;
  6081. chr->divorce_char_sql = char_divorce_char_sql;
  6082. chr->count_users = char_count_users;
  6083. chr->mmo_char_tobuf = char_mmo_char_tobuf;
  6084. chr->mmo_char_send099d = char_mmo_char_send099d;
  6085. chr->mmo_char_send_ban_list = char_mmo_char_send_ban_list;
  6086. chr->mmo_char_send_slots_info = char_mmo_char_send_slots_info;
  6087. chr->mmo_char_send_characters = char_mmo_char_send_characters;
  6088. chr->char_married = char_char_married;
  6089. chr->char_child = char_char_child;
  6090. chr->char_family = char_char_family;
  6091. chr->disconnect_player = char_disconnect_player;
  6092. chr->authfail_fd = char_authfail_fd;
  6093. chr->request_account_data = char_request_account_data;
  6094. chr->auth_ok = char_auth_ok;
  6095. chr->ping_login_server = char_ping_login_server;
  6096. chr->parse_fromlogin_connection_state = char_parse_fromlogin_connection_state;
  6097. chr->auth_error = char_auth_error;
  6098. chr->parse_fromlogin_auth_state = char_parse_fromlogin_auth_state;
  6099. chr->parse_fromlogin_account_data = char_parse_fromlogin_account_data;
  6100. chr->parse_fromlogin_login_pong = char_parse_fromlogin_login_pong;
  6101. chr->changesex = char_changesex;
  6102. chr->parse_fromlogin_changesex_reply = char_parse_fromlogin_changesex_reply;
  6103. chr->parse_fromlogin_account_reg2 = char_parse_fromlogin_account_reg2;
  6104. chr->parse_fromlogin_ban = char_parse_fromlogin_ban;
  6105. chr->parse_fromlogin_kick = char_parse_fromlogin_kick;
  6106. chr->update_ip = char_update_ip;
  6107. chr->parse_fromlogin_update_ip = char_parse_fromlogin_update_ip;
  6108. chr->parse_fromlogin_accinfo2_failed = char_parse_fromlogin_accinfo2_failed;
  6109. chr->parse_fromlogin_accinfo2_ok = char_parse_fromlogin_accinfo2_ok;
  6110. chr->parse_fromlogin = char_parse_fromlogin;
  6111. chr->request_accreg2 = char_request_accreg2;
  6112. chr->global_accreg_to_login_start = char_global_accreg_to_login_start;
  6113. chr->global_accreg_to_login_send = char_global_accreg_to_login_send;
  6114. chr->global_accreg_to_login_add = char_global_accreg_to_login_add;
  6115. chr->read_fame_list = char_read_fame_list;
  6116. chr->send_fame_list = char_send_fame_list;
  6117. chr->update_fame_list = char_update_fame_list;
  6118. chr->loadName = char_loadName;
  6119. chr->parse_frommap_datasync = char_parse_frommap_datasync;
  6120. chr->parse_frommap_skillid2idx = char_parse_frommap_skillid2idx;
  6121. chr->map_received_ok = char_map_received_ok;
  6122. chr->send_maps = char_send_maps;
  6123. chr->parse_frommap_map_names = char_parse_frommap_map_names;
  6124. chr->send_scdata = char_send_scdata;
  6125. chr->parse_frommap_request_scdata = char_parse_frommap_request_scdata;
  6126. chr->parse_frommap_set_users_count = char_parse_frommap_set_users_count;
  6127. chr->parse_frommap_set_users = char_parse_frommap_set_users;
  6128. chr->save_character_ack = char_save_character_ack;
  6129. chr->parse_frommap_save_character = char_parse_frommap_save_character;
  6130. chr->select_ack = char_select_ack;
  6131. chr->parse_frommap_char_select_req = char_parse_frommap_char_select_req;
  6132. chr->change_map_server_ack = char_change_map_server_ack;
  6133. chr->parse_frommap_change_map_server = char_parse_frommap_change_map_server;
  6134. chr->parse_frommap_remove_friend = char_parse_frommap_remove_friend;
  6135. chr->char_name_ack = char_char_name_ack;
  6136. chr->parse_frommap_char_name_request = char_parse_frommap_char_name_request;
  6137. chr->parse_frommap_change_email = char_parse_frommap_change_email;
  6138. chr->ban = char_ban;
  6139. chr->unban = char_unban;
  6140. chr->ask_name_ack = char_ask_name_ack;
  6141. chr->changecharsex = char_changecharsex;
  6142. chr->parse_frommap_change_account = char_parse_frommap_change_account;
  6143. chr->parse_frommap_fame_list = char_parse_frommap_fame_list;
  6144. chr->parse_frommap_divorce_char = char_parse_frommap_divorce_char;
  6145. chr->parse_frommap_ragsrvinfo = char_parse_frommap_ragsrvinfo;
  6146. chr->parse_frommap_set_char_offline = char_parse_frommap_set_char_offline;
  6147. chr->parse_frommap_set_all_offline = char_parse_frommap_set_all_offline;
  6148. chr->parse_frommap_set_char_online = char_parse_frommap_set_char_online;
  6149. chr->parse_frommap_build_fame_list = char_parse_frommap_build_fame_list;
  6150. chr->parse_frommap_save_status_change_data = char_parse_frommap_save_status_change_data;
  6151. chr->send_pong = char_send_pong;
  6152. chr->parse_frommap_ping = char_parse_frommap_ping;
  6153. chr->map_auth_ok = char_map_auth_ok;
  6154. chr->map_auth_failed = char_map_auth_failed;
  6155. chr->parse_frommap_auth_request = char_parse_frommap_auth_request;
  6156. chr->parse_frommap_update_ip = char_parse_frommap_update_ip;
  6157. chr->parse_frommap_request_stats_report = char_parse_frommap_request_stats_report;
  6158. chr->parse_frommap_scdata_update = char_parse_frommap_scdata_update;
  6159. chr->parse_frommap_scdata_delete = char_parse_frommap_scdata_delete;
  6160. chr->parse_frommap = char_parse_frommap;
  6161. chr->search_mapserver = char_search_mapserver;
  6162. chr->mapif_init = char_mapif_init;
  6163. chr->lan_subnet_check = char_lan_subnet_check;
  6164. chr->delete2_ack = char_delete2_ack;
  6165. chr->delete2_accept_actual_ack = char_delete2_accept_actual_ack;
  6166. chr->delete2_accept_ack = char_delete2_accept_ack;
  6167. chr->delete2_cancel_ack = char_delete2_cancel_ack;
  6168. chr->delete2_req = char_delete2_req;
  6169. chr->delete2_accept = char_delete2_accept;
  6170. chr->delete2_cancel = char_delete2_cancel;
  6171. chr->send_account_id = char_send_account_id;
  6172. chr->parse_char_connect = char_parse_char_connect;
  6173. chr->send_map_info = char_send_map_info;
  6174. chr->send_wait_char_server = char_send_wait_char_server;
  6175. chr->search_default_maps_mapserver = char_search_default_maps_mapserver;
  6176. chr->parse_char_select = char_parse_char_select;
  6177. chr->creation_failed = char_creation_failed;
  6178. chr->creation_ok = char_creation_ok;
  6179. chr->parse_char_create_new_char = char_parse_char_create_new_char;
  6180. chr->delete_char_failed = char_delete_char_failed;
  6181. chr->delete_char_ok = char_delete_char_ok;
  6182. chr->parse_char_delete_char = char_parse_char_delete_char;
  6183. chr->parse_char_ping = char_parse_char_ping;
  6184. chr->allow_rename = char_allow_rename;
  6185. chr->parse_char_rename_char = char_parse_char_rename_char;
  6186. chr->parse_char_rename_char2 = char_parse_char_rename_char2;
  6187. chr->rename_char_ack = char_rename_char_ack;
  6188. chr->parse_char_rename_char_confirm = char_parse_char_rename_char_confirm;
  6189. chr->captcha_notsupported = char_captcha_notsupported;
  6190. chr->parse_char_request_captcha = char_parse_char_request_captcha;
  6191. chr->parse_char_check_captcha = char_parse_char_check_captcha;
  6192. chr->parse_char_delete2_req = char_parse_char_delete2_req;
  6193. chr->parse_char_delete2_accept = char_parse_char_delete2_accept;
  6194. chr->parse_char_delete2_cancel = char_parse_char_delete2_cancel;
  6195. chr->login_map_server_ack = char_login_map_server_ack;
  6196. chr->parse_char_login_map_server = char_parse_char_login_map_server;
  6197. chr->parse_char_pincode_check = char_parse_char_pincode_check;
  6198. chr->parse_char_pincode_window = char_parse_char_pincode_window;
  6199. chr->parse_char_pincode_change = char_parse_char_pincode_change;
  6200. chr->parse_char_pincode_first_pin = char_parse_char_pincode_first_pin;
  6201. chr->parse_char_request_chars = char_parse_char_request_chars;
  6202. chr->change_character_slot_ack = char_change_character_slot_ack;
  6203. chr->parse_char_move_character = char_parse_char_move_character;
  6204. chr->parse_char_unknown_packet = char_parse_char_unknown_packet;
  6205. chr->parse_char = char_parse_char;
  6206. chr->broadcast_user_count = char_broadcast_user_count;
  6207. chr->send_accounts_tologin_sub = char_send_accounts_tologin_sub;
  6208. chr->send_accounts_tologin = char_send_accounts_tologin;
  6209. chr->check_connect_login_server = char_check_connect_login_server;
  6210. chr->online_data_cleanup_sub = char_online_data_cleanup_sub;
  6211. chr->online_data_cleanup = char_online_data_cleanup;
  6212. chr->sql_config_read = char_sql_config_read;
  6213. chr->config_dispatch = char_config_dispatch;
  6214. chr->config_read = char_config_read;
  6215. }
Add Comment
Please, Sign In to add comment