diff options
Diffstat (limited to 'src/char')
42 files changed, 5717 insertions, 3386 deletions
diff --git a/src/char/HPMchar.c b/src/char/HPMchar.c index e0f81c61f..f3cf2cff4 100644 --- a/src/char/HPMchar.c +++ b/src/char/HPMchar.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2014-2015 Hercules Dev Team + * Copyright (C) 2014-2018 Hercules Dev Team * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,7 +27,9 @@ #include "char/char.h" #include "char/geoip.h" #include "char/inter.h" +#include "char/int_achievement.h" #include "char/int_auction.h" +#include "char/int_clan.h" #include "char/int_elemental.h" #include "char/int_guild.h" #include "char/int_homun.h" @@ -36,10 +38,12 @@ #include "char/int_party.h" #include "char/int_pet.h" #include "char/int_quest.h" +#include "char/int_rodex.h" #include "char/int_storage.h" #include "char/loginif.h" #include "char/mapif.h" #include "char/pincode.h" + #include "common/HPMi.h" #include "common/conf.h" #include "common/console.h" @@ -53,6 +57,7 @@ #include "common/mapindex.h" #include "common/mmo.h" #include "common/nullpo.h" +#include "common/packets.h" #include "common/random.h" #include "common/showmsg.h" #include "common/socket.h" diff --git a/src/char/HPMchar.h b/src/char/HPMchar.h index 2bf2820f8..0de3b88b8 100644 --- a/src/char/HPMchar.h +++ b/src/char/HPMchar.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2014-2015 Hercules Dev Team + * Copyright (C) 2014-2018 Hercules Dev Team * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/char/Makefile.in b/src/char/Makefile.in index 7806ce2bb..f159a443f 100644 --- a/src/char/Makefile.in +++ b/src/char/Makefile.in @@ -1,7 +1,7 @@ # This file is part of Hercules. # http://herc.ws - http://github.com/HerculesWS/Hercules # -# Copyright (C) 2012-2016 Hercules Dev Team +# Copyright (C) 2012-2018 Hercules Dev Team # Copyright (C) Athena Dev Teams # # Hercules is free software: you can redistribute it and/or modify @@ -40,13 +40,13 @@ MT19937AR_D = $(THIRDPARTY_D)/mt19937ar MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o MT19937AR_H = $(MT19937AR_D)/mt19937ar.h -CHAR_C = char.c HPMchar.c loginif.c mapif.c geoip.c inter.c int_auction.c int_elemental.c int_guild.c \ - int_homun.c int_mail.c int_mercenary.c int_party.c int_pet.c \ - int_quest.c int_storage.c pincode.c +CHAR_C = char.c HPMchar.c loginif.c mapif.c geoip.c inter.c int_achievement.c int_auction.c int_clan.c int_elemental.c \ + int_guild.c int_homun.c int_mail.c int_mercenary.c int_party.c int_pet.c \ + int_quest.c int_rodex.c int_storage.c pincode.c CHAR_OBJ = $(addprefix obj_sql/, $(patsubst %.c,%.o,$(CHAR_C))) -CHAR_H = char.h HPMchar.h loginif.h mapif.h geoip.h inter.h int_auction.h int_elemental.h int_guild.h \ - int_homun.h int_mail.h int_mercenary.h int_party.h int_pet.h \ - int_quest.h int_storage.h pincode.h +CHAR_H = char.h HPMchar.h loginif.h mapif.h geoip.h inter.h int_achievement.h int_auction.h int_clan.h int_elemental.h \ + int_guild.h int_homun.h int_mail.h int_mercenary.h int_party.h int_pet.h \ + int_quest.h int_rodex.h int_storage.h pincode.h packets_hc_struct.h CHAR_PH = HAVE_MYSQL=@HAVE_MYSQL@ diff --git a/src/char/char.c b/src/char/char.c index 9314e8c81..330b0639f 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -21,11 +21,12 @@ #define HERCULES_CORE #include "config/core.h" // CONSOLE_INPUT -#include "char.h" +#include "char/char.h" #include "char/HPMchar.h" #include "char/geoip.h" #include "char/int_auction.h" +#include "char/int_clan.h" #include "char/int_elemental.h" #include "char/int_guild.h" #include "char/int_homun.h" @@ -34,10 +35,13 @@ #include "char/int_party.h" #include "char/int_pet.h" #include "char/int_quest.h" +#include "char/int_rodex.h" #include "char/int_storage.h" +#include "char/int_achievement.h" #include "char/inter.h" #include "char/loginif.h" #include "char/mapif.h" +#include "char/packets_hc_struct.h" #include "char/pincode.h" #include "common/HPM.h" @@ -50,6 +54,7 @@ #include "common/mapindex.h" #include "common/mmo.h" #include "common/nullpo.h" +#include "common/packetsstatic_len.h" #include "common/showmsg.h" #include "common/socket.h" #include "common/strlib.h" @@ -94,50 +99,54 @@ char party_db[256] = "party"; char pet_db[256] = "pet"; char mail_db[256] = "mail"; // MAIL SYSTEM char auction_db[256] = "auction"; // Auctions System -char friend_db[256] = "friends"; -char hotkey_db[256] = "hotkey"; +static char friend_db[256] = "friends"; +static char hotkey_db[256] = "hotkey"; char quest_db[256] = "quest"; +char rodex_db[256] = "rodex_mail"; +char rodex_item_db[256] = "rodex_items"; char homunculus_db[256] = "homunculus"; char skill_homunculus_db[256] = "skill_homunculus"; char mercenary_db[256] = "mercenary"; char mercenary_owner_db[256] = "mercenary_owner"; char ragsrvinfo_db[256] = "ragsrvinfo"; char elemental_db[256] = "elemental"; -char account_data_db[256] = "account_data"; +static char account_data_db[256] = "account_data"; char acc_reg_num_db[32] = "acc_reg_num_db"; char acc_reg_str_db[32] = "acc_reg_str_db"; char char_reg_str_db[32] = "char_reg_str_db"; char char_reg_num_db[32] = "char_reg_num_db"; +char char_achievement_db[256] = "char_achievements"; -struct char_interface char_s; +static struct char_interface char_s; struct char_interface *chr; char db_path[1024] = "db"; -char wisp_server_name[NAME_LENGTH] = "Server"; -char login_ip_str[128]; -uint32 login_ip = 0; -uint16 login_port = 6900; -char char_ip_str[128]; -char bind_ip_str[128]; -uint32 bind_ip = INADDR_ANY; -int char_maintenance_min_group_id = 0; -bool enable_char_creation = true; ///< Whether to allow character creation. - -bool name_ignoring_case = false; // Allow or not identical name for characters but with a different case by [Yor] +static char wisp_server_name[NAME_LENGTH] = "Server"; +static char login_ip_str[128]; +static uint32 login_ip = 0; +static uint16 login_port = 6900; +static char char_ip_str[128]; +static char bind_ip_str[128]; +static uint32 bind_ip = INADDR_ANY; +static int char_maintenance_min_group_id = 0; +static bool enable_char_creation = true; ///< Whether to allow character creation. + +static bool name_ignoring_case = false; // Allow or not identical name for characters but with a different case by [Yor] 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] -char unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the requested name cannot be determined +static char unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the requested name cannot be determined #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] char char_name_letters[1024] = ""; // list of letters/symbols allowed (or not) in a character name. by [Yor] -int char_del_level = 0; ///< From which level you can delete character [Lupus] -int char_del_delay = 86400; -bool char_aegis_delete = false; ///< Verify if char is in guild/party or char and reacts as Aegis does (disallow deletion), @see chr->delete2_req. +static int char_del_level = 0; ///< From which level you can delete character [Lupus] +static int char_del_delay = 86400; +static bool char_aegis_delete = false; ///< Verify if char is in guild/party or char and reacts as Aegis does (disallow deletion), @see chr->delete2_req. +static bool char_aegis_rename = false; // whether or not the player can be renamed while in party/guild -int max_connect_user = -1; -int gm_allow_group = -1; +static int max_connect_user = -1; +static int gm_allow_group = -1; int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; -int start_zeny = 0; +static int start_zeny = 0; /// Start items for new characters struct start_item_s { @@ -146,28 +155,28 @@ struct start_item_s { int loc; bool stackable; }; -VECTOR_DECL(struct start_item_s) start_items; +static VECTOR_DECL(struct start_item_s) start_items; int guild_exp_rate = 100; //Custom limits for the fame lists. [Skotlex] -int fame_list_size_chemist = MAX_FAME_LIST; -int fame_list_size_smith = MAX_FAME_LIST; -int fame_list_size_taekwon = MAX_FAME_LIST; +static int fame_list_size_chemist = MAX_FAME_LIST; +static int fame_list_size_smith = MAX_FAME_LIST; +static int fame_list_size_taekwon = MAX_FAME_LIST; // Char-server-side stored fame lists [DracoRPG] -struct fame_list smith_fame_list[MAX_FAME_LIST]; -struct fame_list chemist_fame_list[MAX_FAME_LIST]; -struct fame_list taekwon_fame_list[MAX_FAME_LIST]; +static struct fame_list smith_fame_list[MAX_FAME_LIST]; +static struct fame_list chemist_fame_list[MAX_FAME_LIST]; +static struct fame_list taekwon_fame_list[MAX_FAME_LIST]; // Initial position (it's possible to set it in conf file) #ifdef RENEWAL - struct point start_point = { 0, 97, 90 }; +static struct point start_point = { 0, 97, 90 }; #else - struct point start_point = { 0, 53, 111 }; +static struct point start_point = { 0, 53, 111 }; #endif -unsigned short skillid2idx[MAX_SKILL_ID]; +static unsigned short skillid2idx[MAX_SKILL_ID]; //----------------------------------------------------- // Auth database @@ -196,7 +205,7 @@ static struct DBData char_create_online_char_data(union DBKey key, va_list args) return DB->ptr2data(character); } -void char_set_account_online(int account_id) +static void char_set_account_online(int account_id) { WFIFOHEAD(chr->login_fd,6); WFIFOW(chr->login_fd,0) = 0x272b; @@ -204,7 +213,7 @@ void char_set_account_online(int account_id) WFIFOSET(chr->login_fd,6); } -void char_set_account_offline(int account_id) +static void char_set_account_offline(int account_id) { WFIFOHEAD(chr->login_fd,6); WFIFOW(chr->login_fd,0) = 0x272c; @@ -212,7 +221,7 @@ void char_set_account_offline(int account_id) WFIFOSET(chr->login_fd,6); } -void char_set_char_charselect(int account_id) +static void char_set_char_charselect(int account_id) { struct online_char_data* character; @@ -236,7 +245,7 @@ void char_set_char_charselect(int account_id) chr->set_account_online(account_id); } -void char_set_char_online(int map_id, int char_id, int account_id) +static void char_set_char_online(int map_id, int char_id, int account_id) { struct online_char_data* character; struct mmo_charstatus *cp; @@ -276,7 +285,7 @@ void char_set_char_online(int map_id, int char_id, int account_id) chr->set_account_online(account_id); } -void char_set_char_offline(int char_id, int account_id) +static void char_set_char_offline(int char_id, int account_id) { struct online_char_data* character; @@ -287,10 +296,18 @@ void char_set_char_offline(int char_id, int account_id) } else { - struct mmo_charstatus* cp = (struct mmo_charstatus*)idb_get(chr->char_db_,char_id); + struct mmo_charstatus *cp = (struct mmo_charstatus*) idb_get(chr->char_db_, char_id); + /* Character Achievements */ + struct char_achievements *c_ach = (struct char_achievements *) idb_get(inter_achievement->char_achievements, char_id); + inter_guild->CharOffline(char_id, cp?cp->guild_id:-1); - if (cp) + + if (cp != NULL) idb_remove(chr->char_db_,char_id); + if (c_ach != NULL) { + VECTOR_CLEAR(*c_ach); + idb_remove(inter_achievement->char_achievements, char_id); + } if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online`='0' WHERE `char_id`='%d' LIMIT 1", char_db, char_id) ) Sql_ShowDebug(inter->sql_handle); @@ -365,7 +382,7 @@ static int char_db_kickoffline(union DBKey key, struct DBData *data, va_list ap) return 1; } -void char_set_login_all_offline(void) +static void char_set_login_all_offline(void) { //Tell login-server to also mark all our characters as offline. WFIFOHEAD(chr->login_fd,2); @@ -373,7 +390,7 @@ void char_set_login_all_offline(void) WFIFOSET(chr->login_fd,2); } -void char_set_all_offline(int id) +static void char_set_all_offline(int id) { if (id < 0) ShowNotice("Sending all users offline.\n"); @@ -386,7 +403,7 @@ void char_set_all_offline(int id) chr->set_login_all_offline(); } -void char_set_all_offline_sql(void) +static void char_set_all_offline_sql(void) { //Set all players to 'OFFLINE' if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online` = '0'", char_db) ) @@ -408,7 +425,7 @@ static struct DBData char_create_charstatus(union DBKey key, va_list args) return DB->ptr2data(cp); } -int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) +static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) { int i = 0; int count = 0; @@ -428,7 +445,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) //map inventory data if( memcmp(p->inventory, cp->inventory, sizeof(p->inventory)) ) { - if (!chr->memitemdata_to_sql(p->inventory, MAX_INVENTORY, p->char_id, TABLE_INVENTORY)) + if (!chr->memitemdata_to_sql(p->inventory, p->char_id, TABLE_INVENTORY)) strcat(save_status, " inventory"); else errors++; @@ -436,20 +453,12 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) //map cart data if( memcmp(p->cart, cp->cart, sizeof(p->cart)) ) { - if (!chr->memitemdata_to_sql(p->cart, MAX_CART, p->char_id, TABLE_CART)) + if (!chr->memitemdata_to_sql(p->cart, p->char_id, TABLE_CART)) strcat(save_status, " cart"); else errors++; } - //map storage data - if( memcmp(p->storage.items, cp->storage.items, sizeof(p->storage.items)) ) { - if (!chr->memitemdata_to_sql(p->storage.items, MAX_STORAGE, p->account_id, TABLE_STORAGE)) - strcat(save_status, " storage"); - else - errors++; - } - if ( (p->base_exp != cp->base_exp) || (p->base_level != cp->base_level) || (p->job_level != cp->job_level) || (p->job_exp != cp->job_exp) || @@ -463,42 +472,53 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) (p->int_ != cp->int_) || (p->dex != cp->dex) || (p->luk != cp->luk) || (p->option != cp->option) || (p->party_id != cp->party_id) || (p->guild_id != cp->guild_id) || - (p->pet_id != cp->pet_id) || (p->weapon != cp->weapon) || (p->hom_id != cp->hom_id) || - (p->ele_id != cp->ele_id) || (p->shield != cp->shield) || (p->head_top != cp->head_top) || - (p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) || (p->delete_date != cp->delete_date) || - (p->rename != cp->rename) || (p->slotchange != cp->slotchange) || (p->robe != cp->robe) || + (p->pet_id != cp->pet_id) || (p->look.weapon != cp->look.weapon) || (p->hom_id != cp->hom_id) || + (p->ele_id != cp->ele_id) || (p->look.shield != cp->look.shield) || (p->look.head_top != cp->look.head_top) || + (p->look.head_mid != cp->look.head_mid) || (p->look.head_bottom != cp->look.head_bottom) || (p->delete_date != cp->delete_date) || + (p->rename != cp->rename) || (p->slotchange != cp->slotchange) || (p->look.robe != cp->look.robe) || (p->show_equip != cp->show_equip) || (p->allow_party != cp->allow_party) || (p->font != cp->font) || - (p->uniqueitem_counter != cp->uniqueitem_counter) || (p->hotkey_rowshift != cp->hotkey_rowshift) + (p->uniqueitem_counter != cp->uniqueitem_counter) || (p->hotkey_rowshift != cp->hotkey_rowshift) || + (p->clan_id != cp->clan_id) || (p->last_login != cp->last_login) || (p->attendance_count != cp->attendance_count) || + (p->attendance_timer != cp->attendance_timer) || (p->title_id != cp->title_id) || (p->inventorySize != cp->inventorySize) ) { //Save status unsigned int opt = 0; + if (p->inventorySize <= 0 || p->inventorySize > MAX_INVENTORY) { + ShowError("Wrong inventorySize field: %d. Must be in range 1 to %d. Character %s (CID: %d, AID: %d)\n", + p->inventorySize, MAX_INVENTORY, p->name, p->char_id, p->account_id); + Assert_report(0); + p->inventorySize = FIXED_INVENTORY_SIZE; + } + if( p->allow_party ) opt |= OPT_ALLOW_PARTY; if( p->show_equip ) opt |= OPT_SHOW_EQUIP; if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d'," - "`base_exp`='%u', `job_exp`='%u', `zeny`='%d'," + "`base_exp`='%"PRIu64"', `job_exp`='%"PRIu64"', `zeny`='%d'," "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d'," "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d'," "`option`='%u',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',`elemental_id`='%d'," "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d'," "`delete_date`='%lu',`robe`='%d',`slotchange`='%d', `char_opt`='%u', `font`='%u', `uniqueitem_counter` ='%u'," - "`hotkey_rowshift`='%d'" + "`hotkey_rowshift`='%d',`clan_id`='%d',`last_login`='%"PRId64"',`attendance_count`='%d',`attendance_timer`='%"PRId64"'," + "`title_id`='%d', `inventory_size`='%d'" " WHERE `account_id`='%d' AND `char_id` = '%d'", char_db, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, p->max_hp, p->hp, p->max_sp, p->sp, p->status_point, p->skill_point, p->str, p->agi, p->vit, p->int_, p->dex, p->luk, p->option, p->party_id, p->guild_id, p->pet_id, p->hom_id, p->ele_id, - p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, + p->look.weapon, p->look.shield, p->look.head_top, p->look.head_mid, p->look.head_bottom, mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y, mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename, (unsigned long)p->delete_date, // FIXME: platform-dependent size - p->robe,p->slotchange,opt,p->font,p->uniqueitem_counter, - p->hotkey_rowshift, + p->look.robe,p->slotchange,opt,p->font,p->uniqueitem_counter, + p->hotkey_rowshift,p->clan_id,p->last_login, p->attendance_count, p->attendance_timer, + p->title_id, p->inventorySize, p->account_id, p->char_id) ) { Sql_ShowDebug(inter->sql_handle); @@ -603,7 +623,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) StrBuf->Clear(&buf); StrBuf->Printf(&buf, "INSERT INTO `%s`(`char_id`,`id`,`lv`,`flag`) VALUES ", skill_db); //insert here. - for( i = 0, count = 0; i < MAX_SKILL; ++i ) { + for (i = 0, count = 0; i < MAX_SKILL_DB; ++i) { if( p->skill[i].id != 0 && p->skill[i].flag != SKILL_FLAG_TEMPORARY ) { if( p->skill[i].lv == 0 && ( p->skill[i].flag == SKILL_FLAG_PERM_GRANTED || p->skill[i].flag == SKILL_FLAG_PERMANENT ) ) continue; @@ -702,176 +722,290 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) } /** - * Saves an array of 'item' entries into the specified table. - * - * @param items The items array. - * @param max The array size. - * @param id The character/account/guild ID (depending on tableswitch). - * @param tableswitch The type of table (@see enum inventory_table_type). - * @return Error code. - * @retval 0 in case of success. + * Gets a player object's item data from an sql table. [Smokexyz/Hercules] + * @param[in|out] items reference to the item list of a character/account/guild. + * @param[in] max Max amount of items to be pulled into the list. + * @param[in] guid Unique ID of the player object (account_id, char_id, guild_id). + * @param[in] table Table to be used for the transaction. + * @return -1 on failure or number of items added to the list if successful. */ -int char_memitemdata_to_sql(const struct item items[], int max, int id, int tableswitch) +static int char_getitemdata_from_sql(struct item *items, int max, int guid, enum inventory_table_type table) { - StringBuf buf; + int i = 0; struct SqlStmt *stmt = NULL; - int i, j; const char *tablename = NULL; const char *selectoption = NULL; bool has_favorite = false; + StringBuf buf; struct item item = { 0 }; // temp storage variable - bool *flag = NULL; // bit array for inventory matching - bool found; - int errors = 0; - nullpo_ret(items); - - switch (tableswitch) { - case TABLE_INVENTORY: tablename = inventory_db; selectoption = "char_id"; has_favorite = true; break; - case TABLE_CART: tablename = cart_db; selectoption = "char_id"; break; - case TABLE_STORAGE: tablename = storage_db; selectoption = "account_id"; break; - case TABLE_GUILD_STORAGE: tablename = guild_storage_db; selectoption = "guild_id"; break; + nullpo_retr(-1, items); + Assert_retr(-1, guid > 0); + Assert_retr(-1, max > 0); + + // Initialize the array. + memset(items, 0x0, sizeof(struct item) * max); + + switch (table) { + case TABLE_INVENTORY: + tablename = inventory_db; + selectoption = "char_id"; + has_favorite = true; + break; + case TABLE_CART: + tablename = cart_db; + selectoption = "char_id"; + break; + case TABLE_GUILD_STORAGE: + tablename = guild_storage_db; + selectoption = "guild_id"; + break; default: - ShowError("Invalid table name!\n"); - Assert_retr(1, tableswitch); + ShowError("char_getitemdata_from_sql: Invalid table type %d!\n", (int) table); + Assert_retr(-1, table); + return -1; } - // The following code compares inventory with current database values - // and performs modification/deletion/insertion only on relevant rows. - // This approach is more complicated than a trivial delete&insert, but - // it significantly reduces cpu load on the database server. - StrBuf->Init(&buf); StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`"); - for (j = 0; j < MAX_SLOTS; ++j) - StrBuf->Printf(&buf, ", `card%d`", j); + for(i = 0; i < MAX_SLOTS; i++) + StrBuf->Printf(&buf, ", `card%d`", i); + for(i = 0; i < MAX_ITEM_OPTIONS; i++) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", i, i); if (has_favorite) StrBuf->AppendStr(&buf, ", `favorite`"); - StrBuf->Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id); + StrBuf->Printf(&buf, " FROM `%s` WHERE `%s`=?", tablename, selectoption); stmt = SQL->StmtMalloc(inter->sql_handle); if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) - || SQL_ERROR == SQL->StmtExecute(stmt)) { + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &guid, sizeof guid) + || SQL_ERROR == SQL->StmtExecute(stmt)) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); StrBuf->Destroy(&buf); - return 1; + return -1; } - SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &item.equip, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &item.identify, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &item.bound, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &item.unique_id, 0, NULL, NULL); - for (j = 0; j < MAX_SLOTS; ++j) - SQL->StmtBindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); - if (has_favorite) - SQL->StmtBindColumn(stmt, 10+MAX_SLOTS, SQLDT_UCHAR, &item.favorite, 0, NULL, NULL); + if (SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &item.id, sizeof item.id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &item.nameid, sizeof item.nameid, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &item.amount, sizeof item.amount, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &item.equip, sizeof item.equip, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &item.identify, sizeof item.identify, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, sizeof item.refine, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, sizeof item.attribute, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, sizeof item.expire_time, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &item.bound, sizeof item.bound, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &item.unique_id, sizeof item.unique_id, NULL, NULL) + ) { + SqlStmt_ShowDebug(stmt); + } - // bit array indicating which inventory items have already been matched - flag = aCalloc(max, sizeof(bool)); + for (i = 0; i < MAX_SLOTS; i++) { + if (SQL_ERROR == SQL->StmtBindColumn(stmt, 10 + i, SQLDT_INT, &item.card[i], sizeof item.card[i], NULL, NULL)) + SqlStmt_ShowDebug(stmt); + } - while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { - found = false; - // search for the presence of the item in the char's inventory - for (i = 0; i < max; ++i) { - // skip empty and already matched entries - if (items[i].nameid == 0 || flag[i]) - continue; + for (i = 0; i < MAX_ITEM_OPTIONS; i++) { + if (SQL_ERROR == SQL->StmtBindColumn(stmt, 10 + MAX_SLOTS + i * 2, SQLDT_INT16, &item.option[i].index, sizeof item.option[i].index, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 11 + MAX_SLOTS + i * 2, SQLDT_INT16, &item.option[i].value, sizeof item.option[i].index, NULL, NULL)) + SqlStmt_ShowDebug(stmt); + } - if (items[i].nameid == item.nameid - && items[i].unique_id == item.unique_id - && items[i].card[0] == item.card[0] - && items[i].card[2] == item.card[2] - && items[i].card[3] == item.card[3] - ) { - // They are the same item. - ARR_FIND(0, MAX_SLOTS, j, items[i].card[j] != item.card[j]); - if (j == MAX_SLOTS - && items[i].amount == item.amount - && items[i].equip == item.equip - && items[i].identify == item.identify - && items[i].refine == item.refine - && items[i].attribute == item.attribute - && items[i].expire_time == item.expire_time - && items[i].bound == item.bound - && (!has_favorite || items[i].favorite == item.favorite) - ) { - ; //Do nothing. - } else { - // update all fields. - StrBuf->Clear(&buf); - StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%u', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `bound`='%d'", - tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].bound); - for (j = 0; j < MAX_SLOTS; ++j) - StrBuf->Printf(&buf, ", `card%d`='%d'", j, items[i].card[j]); - if (has_favorite) - StrBuf->Printf(&buf, ", `favorite`='%d'", items[i].favorite); - StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id); + if (has_favorite) { + if (SQL_ERROR == SQL->StmtBindColumn(stmt, 10 + MAX_SLOTS + MAX_ITEM_OPTIONS * 2, SQLDT_CHAR, &item.favorite, sizeof item.favorite, NULL, NULL)) + SqlStmt_ShowDebug(stmt); + } + + if (SQL->StmtNumRows(stmt) > 0 ) { + i = 0; + while (SQL_SUCCESS == SQL->StmtNextRow(stmt) && i < max) { + items[i++] = item; + } + } + + SQL->StmtFree(stmt); + StrBuf->Destroy(&buf); + + return i; +} + +/** + * Saves an array of 'item' entries into the specified table. [Smokexyz/Hercules] + * @param[in] items The items array. + * @param[in] guid The character/account/guild ID (depending on table). + * @param[in] tableswitch The type of table (@see enum inventory_table_type). + * @retval -1 in case of failure, or number of changes made within the table. + */ +static int char_memitemdata_to_sql(const struct item *p_items, int guid, enum inventory_table_type table) +{ + StringBuf buf; + int i = 0, j = 0; + const char *tablename = NULL; + const char *selectoption = NULL; + bool has_favorite = false; + struct item *cp_items = NULL; // temp item storage variable + bool *matched_p = NULL; + int total_updates = 0, total_deletes = 0, total_inserts = 0, total_changes = 0; + int item_count = 0, db_max = 0; + + nullpo_ret(p_items); + + switch (table) { + case TABLE_INVENTORY: + tablename = inventory_db; + selectoption = "char_id"; + has_favorite = true; + item_count = MAX_INVENTORY; + break; + case TABLE_CART: + tablename = cart_db; + selectoption = "char_id"; + item_count = MAX_CART; + break; + case TABLE_GUILD_STORAGE: + tablename = guild_storage_db; + selectoption = "guild_id"; + item_count = MAX_GUILD_STORAGE; + break; + default: + ShowError("Invalid table type %d!\n", (int) table); + Assert_retr(-1, table); + return -1; + } + + cp_items = aCalloc(item_count, sizeof(struct item)); + matched_p = aCalloc(item_count, sizeof(bool)); + + StrBuf->Init(&buf); + + /** + * If the storage table is not empty, check for items and replace or delete where needed. + */ + if ((db_max = chr->getitemdata_from_sql(cp_items, item_count, guid, table)) > 0) { + int *deletes = aCalloc(db_max, sizeof(struct item)); + + for (i = 0; i < db_max; i++) { + struct item *cp_it = &cp_items[i]; + + ARR_FIND(0, item_count, j, + matched_p[j] != true + && p_items[j].nameid != 0 + && cp_it->nameid == p_items[j].nameid + && cp_it->unique_id == p_items[j].unique_id + && memcmp(p_items[j].card, cp_it->card, sizeof(int) * MAX_SLOTS) == 0 + && memcmp(p_items[j].option, cp_it->option, 5 * MAX_ITEM_OPTIONS) == 0); + + if (j < item_count) { // Item found. + matched_p[j] = true; // Mark the item as matched. + + // If the amount has changed, set for replacement with current item properties. + if (memcmp(cp_it, &p_items[j], sizeof(struct item)) != 0) { + int k = 0; + + if (total_updates == 0) { + StrBuf->Clear(&buf); + StrBuf->Printf(&buf, "REPLACE INTO `%s` (`id`, `%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`", tablename, selectoption); + for (k = 0; k < MAX_SLOTS; k++) + StrBuf->Printf(&buf, ", `card%d`", k); + for (k = 0; k < MAX_ITEM_OPTIONS; k++) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", k, k); + StrBuf->AppendStr(&buf, ", `expire_time`, `bound`, `unique_id`"); + if (has_favorite) + StrBuf->AppendStr(&buf, ", `favorite`"); + + StrBuf->AppendStr(&buf, ") VALUES "); - if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { - Sql_ShowDebug(inter->sql_handle); - errors++; } - } - found = flag[i] = true; //Item dealt with, - break; //skip to next item in the db. + StrBuf->Printf(&buf, "%s('%d', '%d', '%d', '%d', '%u', '%d', '%d', '%d'", + total_updates > 0 ? ", " : "", cp_it->id, guid, p_items[j].nameid, p_items[j].amount, p_items[j].equip, p_items[j].identify, p_items[j].refine, p_items[j].attribute); + for (k = 0; k < MAX_SLOTS; k++) + StrBuf->Printf(&buf, ", '%d'", p_items[j].card[k]); + for (k = 0; k < MAX_ITEM_OPTIONS; ++k) + StrBuf->Printf(&buf, ", '%d', '%d'", p_items[j].option[k].index, p_items[j].option[k].value); + StrBuf->Printf(&buf, ", '%u', '%d', '%"PRIu64"'", p_items[j].expire_time, p_items[j].bound, p_items[j].unique_id); + if (has_favorite) + StrBuf->Printf(&buf, ", %d", p_items[j].favorite); + + StrBuf->AppendStr(&buf, ")"); + + total_updates++; + } + } else { // Doesn't exist in the table, set for deletion. + deletes[total_deletes++] = cp_it->id; } } - if (!found) { - // Item not present in inventory, remove it. - if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE from `%s` where `id`='%d' LIMIT 1", tablename, item.id)) { + + if (total_updates > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) + Sql_ShowDebug(inter->sql_handle); + + /** + * Handle deletions, if any. + */ + if (total_deletes > 0) { + StrBuf->Clear(&buf); + StrBuf->Printf(&buf, "DELETE FROM `%s` WHERE `id` IN (", tablename); + for (i = 0; i < total_deletes; i++) + StrBuf->Printf(&buf, "%s'%d'", i == 0 ? "" : ", ", deletes[i]); + + StrBuf->AppendStr(&buf, ");"); + + if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) Sql_ShowDebug(inter->sql_handle); - errors++; - } } + + aFree(deletes); } - SQL->StmtFree(stmt); - StrBuf->Clear(&buf); - StrBuf->Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", tablename, selectoption); - for (j = 0; j < MAX_SLOTS; ++j) - StrBuf->Printf(&buf, ", `card%d`", j); - if (has_favorite) - StrBuf->AppendStr(&buf, ", `favorite`"); - StrBuf->AppendStr(&buf, ") VALUES "); + /** + * Check for new items and add if required. + */ + for (i = 0; i < item_count; i++) { + const struct item *p_it = &p_items[i]; - found = false; - // insert non-matched items into the db as new items - for (i = 0; i < max; ++i) { - // skip empty and already matched entries - if (items[i].nameid == 0 || flag[i]) + if (matched_p[i] || p_it->nameid == 0) continue; - if (found) - StrBuf->AppendStr(&buf, ","); - else - found = true; + if (total_inserts == 0) { + StrBuf->Clear(&buf); + StrBuf->Printf(&buf, "INSERT INTO `%s` (`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", tablename, selectoption); + for (j = 0; j < MAX_SLOTS; ++j) + StrBuf->Printf(&buf, ", `card%d`", j); + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j); + if (has_favorite) + StrBuf->AppendStr(&buf, ", `favorite`"); + StrBuf->AppendStr(&buf, ") VALUES "); + } + + StrBuf->Printf(&buf, "%s('%d', '%d', '%d', '%u', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'", + total_inserts > 0 ? ", " : "", guid, p_it->nameid, p_it->amount, p_it->equip, p_it->identify, p_it->refine, + p_it->attribute, p_it->expire_time, p_it->bound, p_it->unique_id); - StrBuf->Printf(&buf, "('%d', '%d', '%d', '%u', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'", - 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); for (j = 0; j < MAX_SLOTS; ++j) - StrBuf->Printf(&buf, ", '%d'", items[i].card[j]); + StrBuf->Printf(&buf, ", '%d'", p_it->card[j]); + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) + StrBuf->Printf(&buf, ", '%d', '%d'", p_it->option[j].index, p_it->option[j].value); + if (has_favorite) - StrBuf->Printf(&buf, ", '%d'", items[i].favorite); + StrBuf->Printf(&buf, ", '%d'", p_it->favorite); + StrBuf->AppendStr(&buf, ")"); + + total_inserts++; } - if (found && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { + if (total_inserts > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) Sql_ShowDebug(inter->sql_handle); - errors++; - } StrBuf->Destroy(&buf); - aFree(flag); - return errors; + aFree(cp_items); + aFree(matched_p); + + ShowInfo("%s save complete - guid: %d (replace: %d, insert: %d, delete: %d)\n", tablename, guid, total_updates, total_inserts, total_deletes); + + return total_changes; } /** @@ -887,7 +1021,7 @@ int char_memitemdata_to_sql(const struct item items[], int max, int id, int tabl * @retval SEX_FEMALE if the per-character sex is female * @retval 99 if the per-character sex is not defined or the current PACKETVER doesn't support it. */ -int char_mmo_gender(const struct char_session_data *sd, const struct mmo_charstatus *p, char sex) +static int char_mmo_gender(const struct char_session_data *sd, const struct mmo_charstatus *p, char sex) { #if PACKETVER >= 20141016 (void)sd; (void)p; // Unused @@ -925,7 +1059,7 @@ int char_mmo_gender(const struct char_session_data *sd, const struct mmo_charsta //===================================================================================================== // Loads the basic character rooster for the given account. Returns total buffer used. -int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) +static int char_mmo_chars_fromsql(struct char_session_data *sd, uint8 *buf, int *count) { struct SqlStmt *stmt; struct mmo_charstatus p; @@ -934,6 +1068,9 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) time_t unban_time = 0; char sex[2]; + if (count) + *count = 0; + nullpo_ret(sd); nullpo_ret(buf); @@ -955,84 +1092,94 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`," "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`," "`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`," - "`robe`,`slotchange`,`unban_time`,`sex`" + "`robe`,`slotchange`,`unban_time`,`sex`,`title_id`,`inventory_size`" " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR, &p.slot, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &p.name, sizeof(p.name), NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_SHORT, &p.class, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_INT, &p.base_level, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &p.job_level, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_UINT, &p.base_exp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &p.job_exp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_INT, &p.zeny, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_SHORT, &p.str, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_SHORT, &p.agi, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_SHORT, &p.vit, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_SHORT, &p.int_, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_SHORT, &p.dex, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 14, SQLDT_SHORT, &p.luk, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 15, SQLDT_INT, &p.max_hp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 16, SQLDT_INT, &p.hp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 17, SQLDT_INT, &p.max_sp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 18, SQLDT_INT, &p.sp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 19, SQLDT_INT, &p.status_point, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_INT, &p.skill_point, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 21, SQLDT_UINT, &p.option, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 22, SQLDT_UCHAR, &p.karma, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 23, SQLDT_SHORT, &p.manner, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 24, SQLDT_SHORT, &p.hair, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 25, SQLDT_SHORT, &p.hair_color, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 26, SQLDT_SHORT, &p.clothes_color, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 27, SQLDT_SHORT, &p.body, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 28, SQLDT_SHORT, &p.weapon, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 29, SQLDT_SHORT, &p.shield, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 30, SQLDT_SHORT, &p.head_top, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 31, SQLDT_SHORT, &p.head_mid, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 32, SQLDT_SHORT, &p.head_bottom, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 33, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 34, SQLDT_USHORT, &p.rename, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 35, SQLDT_UINT32, &p.delete_date, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 36, SQLDT_SHORT, &p.robe, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_USHORT, &p.slotchange, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_LONG, &unban_time, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 39, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p.char_id, sizeof p.char_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR, &p.slot, sizeof p.slot, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &p.name, sizeof p.name, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_INT16, &p.class, sizeof p.class, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_INT, &p.base_level, sizeof p.base_level, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &p.job_level, sizeof p.job_level, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_UINT64, &p.base_exp, sizeof p.base_exp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT64, &p.job_exp, sizeof p.job_exp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_INT, &p.zeny, sizeof p.zeny, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_SHORT, &p.str, sizeof p.str, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_SHORT, &p.agi, sizeof p.agi, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_SHORT, &p.vit, sizeof p.vit, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_SHORT, &p.int_, sizeof p.int_, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_SHORT, &p.dex, sizeof p.dex, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 14, SQLDT_SHORT, &p.luk, sizeof p.luk, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 15, SQLDT_INT, &p.max_hp, sizeof p.max_hp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 16, SQLDT_INT, &p.hp, sizeof p.hp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 17, SQLDT_INT, &p.max_sp, sizeof p.max_sp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 18, SQLDT_INT, &p.sp, sizeof p.sp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 19, SQLDT_INT, &p.status_point, sizeof p.status_point, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_INT, &p.skill_point, sizeof p.skill_point, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 21, SQLDT_UINT, &p.option, sizeof p.option, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 22, SQLDT_UCHAR, &p.karma, sizeof p.karma, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 23, SQLDT_SHORT, &p.manner, sizeof p.manner, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 24, SQLDT_SHORT, &p.hair, sizeof p.hair, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 25, SQLDT_SHORT, &p.hair_color, sizeof p.hair_color, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 26, SQLDT_SHORT, &p.clothes_color, sizeof p.clothes_color, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 27, SQLDT_INT, &p.body, sizeof p.body, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 28, SQLDT_INT, &p.look.weapon, sizeof p.look.weapon, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 29, SQLDT_INT, &p.look.shield, sizeof p.look.shield, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 30, SQLDT_INT, &p.look.head_top, sizeof p.look.head_top, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 31, SQLDT_INT, &p.look.head_mid, sizeof p.look.head_mid, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 32, SQLDT_INT, &p.look.head_bottom, sizeof p.look.head_bottom, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 33, SQLDT_STRING, &last_map, sizeof last_map, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 34, SQLDT_USHORT, &p.rename, sizeof p.rename, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 35, SQLDT_TIME, &p.delete_date, sizeof p.delete_date, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 36, SQLDT_INT, &p.look.robe, sizeof p.look.robe, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_USHORT, &p.slotchange, sizeof p.slotchange, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_TIME, &unban_time, sizeof unban_time, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 39, SQLDT_ENUM, &sex, sizeof sex, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 40, SQLDT_INT, &p.title_id, sizeof p.title_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 41, SQLDT_INT, &p.inventorySize, sizeof p.inventorySize, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); return 0; } - for( i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->StmtNextRow(stmt); i++ ) { + int tmpCount = 0; + for (i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->StmtNextRow(stmt); i++) { if (p.slot >= MAX_CHARS) continue; + if (p.inventorySize <= 0 || p.inventorySize > MAX_INVENTORY) { + ShowError("Wrong inventorySize field: %d. Must be in range 1 to %d. Character %s (CID: %d, AID: %d)\n", + p.inventorySize, MAX_INVENTORY, p.name, p.char_id, p.account_id); + Assert_report(0); + p.inventorySize = FIXED_INVENTORY_SIZE; + } p.last_point.map = mapindex->name2id(last_map); sd->found_char[p.slot] = p.char_id; sd->unban_time[p.slot] = unban_time; p.sex = chr->mmo_gender(sd, &p, sex[0]); j += chr->mmo_char_tobuf(WBUFP(buf, j), &p); + tmpCount ++; } - memset(sd->new_name,0,sizeof(sd->new_name)); + memset(sd->new_name, 0, sizeof(sd->new_name)); SQL->StmtFree(stmt); + if (count) + *count = tmpCount; return j; } //===================================================================================================== -int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything) +static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool load_everything) { - int i,j; + int i = 0; char t_msg[128] = ""; struct mmo_charstatus* cp; - StringBuf buf; struct SqlStmt *stmt; char last_map[MAP_NAME_LENGTH_EXT]; char save_map[MAP_NAME_LENGTH_EXT]; char point_map[MAP_NAME_LENGTH_EXT]; struct point tmp_point; - struct item tmp_item; struct s_skill tmp_skill; struct s_friend tmp_friend; #ifdef HOTKEY_SAVING @@ -1046,6 +1193,7 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every nullpo_ret(p); memset(p, 0, sizeof(struct mmo_charstatus)); + p->inventorySize = FIXED_INVENTORY_SIZE; if (chr->show_save_log) ShowInfo("Char load request (%d)\n", char_id); @@ -1064,69 +1212,76 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`," "`hair_color`,`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`," "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`,`slotchange`," - "`char_opt`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`" + "`char_opt`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`, `attendance_count`, `attendance_timer`," + "`title_id`, `inventory_size`" " FROM `%s` WHERE `char_id`=? LIMIT 1", char_db) - || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p->char_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &p->account_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UCHAR, &p->slot, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_STRING, &p->name, sizeof(p->name), NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_SHORT, &p->class, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &p->base_level, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_INT, &p->job_level, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &p->base_exp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_UINT, &p->job_exp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_INT, &p->zeny, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_SHORT, &p->str, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_SHORT, &p->agi, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_SHORT, &p->vit, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_SHORT, &p->int_, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 14, SQLDT_SHORT, &p->dex, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 15, SQLDT_SHORT, &p->luk, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 16, SQLDT_INT, &p->max_hp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 17, SQLDT_INT, &p->hp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 18, SQLDT_INT, &p->max_sp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 19, SQLDT_INT, &p->sp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_INT, &p->status_point, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 21, SQLDT_INT, &p->skill_point, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 22, SQLDT_UINT, &p->option, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 23, SQLDT_UCHAR, &p->karma, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 24, SQLDT_SHORT, &p->manner, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 25, SQLDT_INT, &p->party_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 26, SQLDT_INT, &p->guild_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 27, SQLDT_INT, &p->pet_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 28, SQLDT_INT, &p->hom_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 29, SQLDT_INT, &p->ele_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 30, SQLDT_SHORT, &p->hair, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 31, SQLDT_SHORT, &p->hair_color, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 32, SQLDT_SHORT, &p->clothes_color, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 33, SQLDT_SHORT, &p->body, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 34, SQLDT_SHORT, &p->weapon, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 35, SQLDT_SHORT, &p->shield, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 36, SQLDT_SHORT, &p->head_top, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_SHORT, &p->head_mid, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_SHORT, &p->head_bottom, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 39, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 40, SQLDT_SHORT, &p->last_point.x, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 41, SQLDT_SHORT, &p->last_point.y, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 42, SQLDT_STRING, &save_map, sizeof(save_map), NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 43, SQLDT_SHORT, &p->save_point.x, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 44, SQLDT_SHORT, &p->save_point.y, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 45, SQLDT_INT, &p->partner_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 46, SQLDT_INT, &p->father, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 47, SQLDT_INT, &p->mother, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 48, SQLDT_INT, &p->child, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 49, SQLDT_INT, &p->fame, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 50, SQLDT_USHORT, &p->rename, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 51, SQLDT_UINT32, &p->delete_date, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 52, SQLDT_SHORT, &p->robe, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 53, SQLDT_USHORT, &p->slotchange, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 54, SQLDT_UINT, &opt, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 55, SQLDT_UCHAR, &p->font, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 56, SQLDT_UINT, &p->uniqueitem_counter, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 57, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 58, SQLDT_UCHAR, &p->hotkey_rowshift, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p->char_id, sizeof p->char_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &p->account_id, sizeof p->account_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UCHAR, &p->slot, sizeof p->slot, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_STRING, &p->name, sizeof p->name, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_INT16, &p->class, sizeof p->class, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &p->base_level, sizeof p->base_level, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_INT, &p->job_level, sizeof p->job_level, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT64, &p->base_exp, sizeof p->base_exp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_UINT64, &p->job_exp, sizeof p->job_exp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_INT, &p->zeny, sizeof p->zeny, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_SHORT, &p->str, sizeof p->str, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_SHORT, &p->agi, sizeof p->agi, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_SHORT, &p->vit, sizeof p->vit, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_SHORT, &p->int_, sizeof p->int_, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 14, SQLDT_SHORT, &p->dex, sizeof p->dex, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 15, SQLDT_SHORT, &p->luk, sizeof p->luk, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 16, SQLDT_INT, &p->max_hp, sizeof p->max_hp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 17, SQLDT_INT, &p->hp, sizeof p->hp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 18, SQLDT_INT, &p->max_sp, sizeof p->max_sp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 19, SQLDT_INT, &p->sp, sizeof p->sp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_INT, &p->status_point, sizeof p->status_point, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 21, SQLDT_INT, &p->skill_point, sizeof p->skill_point, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 22, SQLDT_UINT, &p->option, sizeof p->option, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 23, SQLDT_UCHAR, &p->karma, sizeof p->karma, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 24, SQLDT_SHORT, &p->manner, sizeof p->manner, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 25, SQLDT_INT, &p->party_id, sizeof p->party_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 26, SQLDT_INT, &p->guild_id, sizeof p->guild_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 27, SQLDT_INT, &p->pet_id, sizeof p->pet_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 28, SQLDT_INT, &p->hom_id, sizeof p->hom_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 29, SQLDT_INT, &p->ele_id, sizeof p->ele_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 30, SQLDT_SHORT, &p->hair, sizeof p->hair, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 31, SQLDT_SHORT, &p->hair_color, sizeof p->hair_color, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 32, SQLDT_SHORT, &p->clothes_color, sizeof p->clothes_color, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 33, SQLDT_INT, &p->body, sizeof p->body, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 34, SQLDT_INT, &p->look.weapon, sizeof p->look.weapon, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 35, SQLDT_INT, &p->look.shield, sizeof p->look.shield, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 36, SQLDT_INT, &p->look.head_top, sizeof p->look.head_top, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_INT, &p->look.head_mid, sizeof p->look.head_mid, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_INT, &p->look.head_bottom, sizeof p->look.head_bottom, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 39, SQLDT_STRING, &last_map, sizeof last_map, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 40, SQLDT_INT16, &p->last_point.x, sizeof p->last_point.x, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 41, SQLDT_INT16, &p->last_point.y, sizeof p->last_point.y, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 42, SQLDT_STRING, &save_map, sizeof save_map, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 43, SQLDT_INT16, &p->save_point.x, sizeof p->save_point.x, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 44, SQLDT_INT16, &p->save_point.y, sizeof p->save_point.y, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 45, SQLDT_INT, &p->partner_id, sizeof p->partner_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 46, SQLDT_INT, &p->father, sizeof p->father, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 47, SQLDT_INT, &p->mother, sizeof p->mother, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 48, SQLDT_INT, &p->child, sizeof p->child, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 49, SQLDT_INT, &p->fame, sizeof p->fame, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 50, SQLDT_USHORT, &p->rename, sizeof p->rename, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 51, SQLDT_TIME, &p->delete_date, sizeof p->delete_date, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 52, SQLDT_INT, &p->look.robe, sizeof p->look.robe, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 53, SQLDT_USHORT, &p->slotchange, sizeof p->slotchange, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 54, SQLDT_UINT, &opt, sizeof opt, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 55, SQLDT_UCHAR, &p->font, sizeof p->font, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 56, SQLDT_UINT32, &p->uniqueitem_counter, sizeof p->uniqueitem_counter, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 57, SQLDT_ENUM, &sex, sizeof sex, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 58, SQLDT_UCHAR, &p->hotkey_rowshift, sizeof p->hotkey_rowshift, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 59, SQLDT_INT, &p->clan_id, sizeof p->clan_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 60, SQLDT_INT64, &p->last_login, sizeof p->last_login, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 61, SQLDT_SHORT, &p->attendance_count, sizeof p->attendance_count, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 62, SQLDT_INT64, &p->attendance_timer, sizeof p->attendance_timer, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 63, SQLDT_INT, &p->title_id, sizeof p->title_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 64, SQLDT_INT, &p->inventorySize, sizeof p->inventorySize, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); @@ -1158,6 +1313,13 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every p->save_point.y = mapindex->default_y; } + if (p->inventorySize <= 0 || p->inventorySize > MAX_INVENTORY) { + ShowError("Wrong inventorySize field: %d. Must be in range 1 to %d. Character %s (CID: %d, AID: %d)\n", + p->inventorySize, MAX_INVENTORY, p->name, p->char_id, p->account_id); + Assert_report(0); + p->inventorySize = FIXED_INVENTORY_SIZE; + } + strcat(t_msg, " status"); if (!load_everything) // For quick selection of data when displaying the char menu @@ -1170,11 +1332,11 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`) memset(&tmp_point, 0, sizeof(tmp_point)); 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) - || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &point_map, sizeof(point_map), NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_point.x, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_point.y, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &point_map, sizeof point_map, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT16, &tmp_point.x, sizeof tmp_point.x, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_INT16, &tmp_point.y, sizeof tmp_point.y, NULL, NULL) ) SqlStmt_ShowDebug(stmt); @@ -1184,86 +1346,23 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every } strcat(t_msg, " memo"); - //read inventory - //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `bound`, `unique_id`) - StrBuf->Init(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`"); - for( i = 0; i < MAX_SLOTS; ++i ) - StrBuf->Printf(&buf, ", `card%d`", i); - StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY); + /* read inventory [Smokexyz/Hercules] */ + if (chr->getitemdata_from_sql(p->inventory, MAX_INVENTORY, p->char_id, TABLE_INVENTORY) > 0) + strcat(t_msg, " inventory"); - memset(&tmp_item, 0, sizeof(tmp_item)); - if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) - || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) - || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_item.id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_item.nameid, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_item.amount, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &tmp_item.equip, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UCHAR, &tmp_item.bound, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_UINT64, &tmp_item.unique_id, 0, NULL, NULL) - ) - SqlStmt_ShowDebug(stmt); - for( i = 0; i < MAX_SLOTS; ++i ) - if( SQL_ERROR == SQL->StmtBindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) - SqlStmt_ShowDebug(stmt); - - for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) - memcpy(&p->inventory[i], &tmp_item, sizeof(tmp_item)); - - strcat(t_msg, " inventory"); - - //read cart - //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `bound`, `unique_id`) - StrBuf->Clear(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`"); - for( j = 0; j < MAX_SLOTS; ++j ) - StrBuf->Printf(&buf, ", `card%d`", j); - StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART); - - memset(&tmp_item, 0, sizeof(tmp_item)); - if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) - || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) - || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_item.id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_item.nameid, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_item.amount, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &tmp_item.equip, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &tmp_item.bound, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &tmp_item.unique_id, 0, NULL, NULL) - ) { - SqlStmt_ShowDebug(stmt); - } - for( i = 0; i < MAX_SLOTS; ++i ) - if( SQL_ERROR == SQL->StmtBindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) - SqlStmt_ShowDebug(stmt); - - for( i = 0; i < MAX_CART && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) - memcpy(&p->cart[i], &tmp_item, sizeof(tmp_item)); - strcat(t_msg, " cart"); - - //read storage - inter_storage->fromsql(p->account_id, &p->storage); - strcat(t_msg, " storage"); + /* read cart [Smokexyz/Hercules] */ + if (chr->getitemdata_from_sql(p->cart, MAX_CART, p->char_id, TABLE_CART) > 0) + strcat(t_msg, " cart"); //read skill //`skill` (`char_id`, `id`, `lv`) memset(&tmp_skill, 0, sizeof(tmp_skill)); - if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `id`, `lv`,`flag` FROM `%s` WHERE `char_id`=? LIMIT %d", skill_db, MAX_SKILL) - || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) + if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `id`, `lv`,`flag` FROM `%s` WHERE `char_id`=? LIMIT %d", skill_db, MAX_SKILL_DB) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_USHORT, &tmp_skill.id , 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR , &tmp_skill.lv , 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UCHAR , &tmp_skill.flag, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_USHORT, &tmp_skill.id, sizeof tmp_skill.id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR, &tmp_skill.lv, sizeof tmp_skill.lv, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UCHAR, &tmp_skill.flag, sizeof tmp_skill.flag, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); } @@ -1271,7 +1370,7 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every if( tmp_skill.flag != SKILL_FLAG_PERM_GRANTED ) tmp_skill.flag = SKILL_FLAG_PERMANENT; - for( i = 0; i < MAX_SKILL && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) { + for (i = 0; i < MAX_SKILL_DB && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i) { if( skillid2idx[tmp_skill.id] ) memcpy(&p->skill[skillid2idx[tmp_skill.id]], &tmp_skill, sizeof(tmp_skill)); else @@ -1283,11 +1382,11 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every //`friends` (`char_id`, `friend_account`, `friend_id`) memset(&tmp_friend, 0, sizeof(tmp_friend)); 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) - || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_friend.account_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &tmp_friend.char_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &tmp_friend.name, sizeof(tmp_friend.name), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_friend.account_id, sizeof tmp_friend.account_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &tmp_friend.char_id, sizeof tmp_friend.char_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &tmp_friend.name, sizeof tmp_friend.name, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); } @@ -1301,13 +1400,15 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every //`hotkey` (`char_id`, `hotkey`, `type`, `itemskill_id`, `skill_lvl` memset(&tmp_hotkey, 0, sizeof(tmp_hotkey)); if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `hotkey`, `type`, `itemskill_id`, `skill_lvl` FROM `%s` WHERE `char_id`=?", hotkey_db) - || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &hotkey_num, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR, &tmp_hotkey.type, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UINT, &tmp_hotkey.id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &tmp_hotkey.lv, 0, NULL, NULL) ) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &hotkey_num, sizeof hotkey_num, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR, &tmp_hotkey.type, sizeof tmp_hotkey.type, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UINT, &tmp_hotkey.id, sizeof tmp_hotkey.id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &tmp_hotkey.lv, sizeof tmp_hotkey.lv, NULL, NULL) + ) { SqlStmt_ShowDebug(stmt); + } while( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) { @@ -1328,12 +1429,12 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every //`account_data` (`account_id`,`bank_vault`,`base_exp`,`base_drop`,`base_death`) 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) - || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &account_id, 0) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &account_id, sizeof account_id) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p->bank_vault, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_USHORT, &p->mod_exp, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_USHORT, &p->mod_drop, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &p->mod_death, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p->bank_vault, sizeof p->bank_vault, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_USHORT, &p->mod_exp, sizeof p->mod_exp, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_USHORT, &p->mod_drop, sizeof p->mod_drop, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &p->mod_death, sizeof p->mod_death, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); } @@ -1344,7 +1445,6 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every if (chr->show_save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfully! SQL->StmtFree(stmt); - StrBuf->Destroy(&buf); /* load options into proper vars */ if( opt & OPT_ALLOW_PARTY ) @@ -1358,7 +1458,7 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every } //========================================================================================================== -int char_mmo_char_sql_init(void) +static int char_mmo_char_sql_init(void) { chr->char_db_= idb_alloc(DB_OPT_RELEASE_DATA); @@ -1375,7 +1475,8 @@ int char_mmo_char_sql_init(void) } /* [Ind/Hercules] - special thanks to Yommy for providing the packet structure/data */ -bool char_char_slotchange(struct char_session_data *sd, int fd, unsigned short from, unsigned short to) { +static bool char_char_slotchange(struct char_session_data *sd, int fd, unsigned short from, unsigned short to) +{ struct mmo_charstatus char_dat; int from_id = 0; @@ -1427,7 +1528,7 @@ bool char_char_slotchange(struct char_session_data *sd, int fd, unsigned short f //----------------------------------- // Function to change character's names //----------------------------------- -int char_rename_char_sql(struct char_session_data *sd, int char_id) +static int char_rename_char_sql(struct char_session_data *sd, int char_id) { struct mmo_charstatus char_dat; char esc_name[NAME_LENGTH*2+1]; @@ -1446,6 +1547,14 @@ int char_rename_char_sql(struct char_session_data *sd, int char_id) if( char_dat.rename == 0 ) return 1; + if (char_aegis_rename) { + if (char_dat.guild_id > 0) { + return 5; // MSG_FAILED_RENAME_BELONGS_TO_GUILD + } else if (char_dat.party_id > 0) { + return 6; // MSG_FAILED_RENAME_BELONGS_TO_PARTY + } + } + SQL->EscapeStringLen(inter->sql_handle, esc_name, sd->new_name, strnlen(sd->new_name, NAME_LENGTH)); // check if the char exist @@ -1471,21 +1580,76 @@ int char_rename_char_sql(struct char_session_data *sd, int char_id) // log change if (chr->enable_logs) { 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`)" - "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '0', '0', '0', '0', '0', '0', '0', '0')", - charlog_db, "change char name", sd->account_id, char_dat.char_id, char_dat.slot, esc_name)) + "INSERT INTO `%s` (" + " `time`, `char_msg`, `account_id`, `char_id`, `char_num`, `class`, `name`," + " `str`, `agi`, `vit`, `int`, `dex`, `luk`," + " `hair`, `hair_color`" + ") VALUES (" + " NOW(), 'change char name', '%d', '%d', '%d', '%d', '%s'," + " '%d', '%d', '%d', '%d', '%d', '%d'," + " '%d', '%d'" + ")", + charlog_db, + sd->account_id, char_dat.char_id, char_dat.slot, char_dat.class, esc_name, + char_dat.str, char_dat.agi, char_dat.vit, char_dat.int_, char_dat.dex, char_dat.luk, + char_dat.hair, char_dat.hair_color + )) Sql_ShowDebug(inter->sql_handle); } return 0; } -int char_check_char_name(char * name, char * esc_name) +/** + * Checks if the given name exists in the database. + * + * @param name The name to check. + * @param esc_name Escaped version of the name, optional for faster processing. + * @retval true if the character name already exists. + */ +static bool char_name_exists(const char *name, const char *esc_name) +{ + char esc_name2[NAME_LENGTH * 2 + 1]; + + nullpo_retr(true, name); + + if (esc_name == NULL) { + SQL->EscapeStringLen(inter->sql_handle, esc_name2, name, strnlen(name, NAME_LENGTH)); + esc_name = esc_name2; + } + + if (name_ignoring_case) { + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT 1 FROM `%s` WHERE BINARY `name` = '%s' LIMIT 1", char_db, esc_name)) { + Sql_ShowDebug(inter->sql_handle); + return true; + } + } else { + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT 1 FROM `%s` WHERE `name` = '%s' LIMIT 1", char_db, esc_name)) { + Sql_ShowDebug(inter->sql_handle); + return true; + } + } + if (SQL->NumRows(inter->sql_handle) > 0) + return true; + + return false; +} + +/** + * Checks if the given name is valid for a new character. + * + * @param name The name to check. + * @param esc_name Escaped version of the name, optional for faster processing. + * @retval 0 if the name is valid. + * @retval -1 if the name already exists or is reserved + * @retval -2 if the name is too short or contains special characters. + * @retval -5 if the name contains forbidden characters. + */ +static int char_check_char_name(const char *name, const char *esc_name) { int i; nullpo_retr(-2, name); - nullpo_retr(-2, esc_name); // check length of character name if (name[0] == '\0') @@ -1496,9 +1660,16 @@ int char_check_char_name(char * name, char * esc_name) **/ if( strlen( name ) < 4 ) return -2; - // check content of character name - if( remove_control_chars(name) ) - return -2; // control chars in name + + { + // check content of character name + char *name_copy = aStrdup(name); + if (remove_control_chars(name_copy)) { + aFree(name_copy); + return -2; // control chars in name + } + aFree(name_copy); + } // check for reserved names if( strcmpi(name, wisp_server_name) == 0 ) @@ -1517,19 +1688,9 @@ int char_check_char_name(char * name, char * esc_name) if( strchr(char_name_letters, name[i]) != NULL ) return -5; } - if( name_ignoring_case ) { - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT 1 FROM `%s` WHERE BINARY `name` = '%s' LIMIT 1", char_db, esc_name) ) { - Sql_ShowDebug(inter->sql_handle); - return -2; - } - } else { - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT 1 FROM `%s` WHERE `name` = '%s' LIMIT 1", char_db, esc_name) ) { - Sql_ShowDebug(inter->sql_handle); - return -2; - } - } - if( SQL->NumRows(inter->sql_handle) > 0 ) - return -1; // name already exists + + if (chr->name_exists(name, esc_name)) + return -1; return 0; } @@ -1544,7 +1705,7 @@ int char_check_char_name(char * name, char * esc_name) * -5: 'Symbols in Character Names are forbidden' * char_id: Success **/ -int char_make_new_char_sql(struct char_session_data *sd, const char *name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style, int16 starting_class) +static int char_make_new_char_sql(struct char_session_data *sd, const char *name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style, int16 starting_class, uint8 sex) { char name[NAME_LENGTH]; char esc_name[NAME_LENGTH*2+1]; @@ -1565,7 +1726,7 @@ int char_make_new_char_sql(struct char_session_data *sd, const char *name_, int case JOB_NOVICE: break; default: - return -2; // Char Creation Denied + return -2; // Char Creation Denied } //check other inputs @@ -1587,17 +1748,17 @@ int char_make_new_char_sql(struct char_session_data *sd, const char *name_, int if( sd->found_char[slot] != -1 ) return -2; /* character account limit exceeded */ + #if PACKETVER >= 20120307 // Insert the new char entry to the database if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `class`, `zeny`, `status_point`,`str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`," - "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES (" - "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')", + "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`, `sex`) VALUES (" + "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d', '%c')", char_db, sd->account_id , slot, esc_name, starting_class, start_zeny, 48, str, agi, vit, int_, dex, luk, (40 * (100 + vit)/100) , (40 * (100 + vit)/100 ), (11 * (100 + int_)/100), (11 * (100 + int_)/100), hair_style, hair_color, - mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y) ) - { - Sql_ShowDebug(inter->sql_handle); - return -2; //No, stop the procedure! + mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y, sex)) { + Sql_ShowDebug(inter->sql_handle); + return -2; //No, stop the procedure! } #else //Insert the new char entry to the database @@ -1647,14 +1808,14 @@ int char_make_new_char_sql(struct char_session_data *sd, const char *name_, int } } - ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, slot, name); + ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s, sex: %c\n", sd->account_id, char_id, slot, name, sex); return char_id; } /*----------------------------------------------------------------------------------------------------------*/ /* Divorce Players */ /*----------------------------------------------------------------------------------------------------------*/ -int char_divorce_char_sql(int partner_id1, int partner_id2) +static int char_divorce_char_sql(int partner_id1, int partner_id2) { unsigned char buf[64]; @@ -1677,7 +1838,7 @@ int char_divorce_char_sql(int partner_id1, int partner_id2) /* Returns 0 if successful * Returns < 0 for error */ -int char_delete_char_sql(int char_id) +static int char_delete_char_sql(int char_id) { char name[NAME_LENGTH]; char esc_name[NAME_LENGTH*2+1]; //Name needs be escaped. @@ -1757,11 +1918,11 @@ int char_delete_char_sql(int char_id) /* remove homunculus */ if( hom_id ) - mapif->homunculus_delete(hom_id); + inter_homunculus->delete(hom_id); /* remove elemental */ if (elemental_id) - mapif->elemental_delete(elemental_id); + inter_elemental->delete(elemental_id); /* remove mercenary data */ inter_mercenary->owner_delete(char_id); @@ -1833,16 +1994,16 @@ int char_delete_char_sql(int char_id) if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `guild_id` FROM `%s` WHERE `char_id` = '%d'", guild_db, char_id) ) Sql_ShowDebug(inter->sql_handle); else if( SQL->NumRows(inter->sql_handle) > 0 ) - mapif->parse_BreakGuild(0,guild_id); + inter_guild->disband(guild_id); else if( guild_id ) - inter_guild->leave(guild_id, account_id, char_id);// Leave your guild. + inter_guild->leave(guild_id, account_id, char_id, 0, "** Character Deleted **", -1);// Leave your guild. return 0; } //--------------------------------------------------------------------- // This function return the number of online players in all map-servers //--------------------------------------------------------------------- -int char_count_users(void) +static int char_count_users(void) { int i, users; @@ -1858,8 +2019,9 @@ int char_count_users(void) // Writes char data to the buffer in the format used by the client. // Used in packets 0x6b (chars info) and 0x6d (new char info) // Returns the size -#define MAX_CHAR_BUF 150 //Max size (for WFIFOHEAD calls) -int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { +#define MAX_CHAR_BUF (PACKET_LEN_0x006d - 2) +static int char_mmo_char_tobuf(uint8 *buffer, struct mmo_charstatus *p) +{ unsigned short offset = 0; uint8* buf; @@ -1867,10 +2029,23 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { return 0; buf = WBUFP(buffer,0); + WBUFL(buf,0) = p->char_id; - WBUFL(buf,4) = min(p->base_exp, INT32_MAX); +#if PACKETVER >= 20170830 + WBUFQ(buf,4) = min(p->base_exp, INT64_MAX); + offset += 4; + buf = WBUFP(buffer, offset); +#else + WBUFL(buf,4) = min((uint32)(p->base_exp), INT32_MAX); +#endif WBUFL(buf,8) = p->zeny; - WBUFL(buf,12) = min(p->job_exp, INT32_MAX); +#if PACKETVER >= 20170830 + WBUFQ(buf,12) = min(p->job_exp, INT64_MAX); + offset += 4; + buf = WBUFP(buffer, offset); +#else + WBUFL(buf,12) = min((uint32)(p->job_exp), INT32_MAX); +#endif WBUFL(buf,16) = p->job_level; WBUFL(buf,20) = 0; // probably opt1 WBUFL(buf,24) = 0; // probably opt2 @@ -1900,14 +2075,14 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { //When the weapon is sent and your option is riding, the client crashes on login!? // FIXME[Haru]: is OPTION_HANBOK intended to be part of this list? And if it is, should the list also include other OPTION_ costumes? - WBUFW(buf,56) = (p->option&(OPTION_RIDING|OPTION_DRAGON|OPTION_WUG|OPTION_WUGRIDER|OPTION_MADOGEAR|OPTION_HANBOK)) ? 0 : p->weapon; + WBUFW(buf,56) = (p->option&(OPTION_RIDING|OPTION_DRAGON|OPTION_WUG|OPTION_WUGRIDER|OPTION_MADOGEAR|OPTION_HANBOK)) ? 0 : p->look.weapon; WBUFW(buf,58) = p->base_level; WBUFW(buf,60) = min(p->skill_point, INT16_MAX); - WBUFW(buf,62) = p->head_bottom; - WBUFW(buf,64) = p->shield; - WBUFW(buf,66) = p->head_top; - WBUFW(buf,68) = p->head_mid; + WBUFW(buf,62) = p->look.head_bottom; + WBUFW(buf,64) = p->look.shield; + WBUFW(buf,66) = p->look.head_top; + WBUFW(buf,68) = p->look.head_mid; WBUFW(buf,70) = p->hair_color; WBUFW(buf,72) = p->clothes_color; memcpy(WBUFP(buf,74), p->name, NAME_LENGTH); @@ -1931,7 +2106,7 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { offset += 4; #endif #if PACKETVER >= 20110111 - WBUFL(buf,128) = p->robe; + WBUFL(buf,128) = p->look.robe; offset += 4; #endif #if PACKETVER != 20111116 //2011-11-16 wants 136, ask gravity. @@ -1949,24 +2124,36 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { #endif #endif - return 106+offset; + if (106 + offset != MAX_CHAR_BUF) + Assert_report("Wrong buffer size in char_mmo_char_tobuf"); + return 106 + offset; } /* Made Possible by Yommy~! <3 */ -void char_mmo_char_send099d(int fd, struct char_session_data *sd) -{ -// support added for client between 20121010 and 20130320 -#if PACKETVER > 20120418 - WFIFOHEAD(fd,4 + (MAX_CHARS*MAX_CHAR_BUF)); - WFIFOW(fd,0) = 0x99d; - WFIFOW(fd,2) = chr->mmo_chars_fromsql(sd, WFIFOP(fd,4)) + 4; - WFIFOSET(fd,WFIFOW(fd,2)); +static void char_send_HC_ACK_CHARINFO_PER_PAGE(int fd, struct char_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20130522 || PACKETVER_RE_NUM >= 20130327 || defined(PACKETVER_ZERO) + WFIFOHEAD(fd, sizeof(struct PACKET_HC_ACK_CHARINFO_PER_PAGE) + (MAX_CHARS * MAX_CHAR_BUF)); + struct PACKET_HC_ACK_CHARINFO_PER_PAGE *p = WFIFOP(fd, 0); + int count = 0; + p->packetId = HEADER_HC_ACK_CHARINFO_PER_PAGE; + p->packetLen = chr->mmo_chars_fromsql(sd, WFIFOP(fd, 4), &count) + sizeof(struct PACKET_HC_ACK_CHARINFO_PER_PAGE); + WFIFOSET(fd, p->packetLen); + // send empty packet if chars count is 3, for trigger final code in client + if (count == 3) { + WFIFOHEAD(fd, sizeof(struct PACKET_HC_ACK_CHARINFO_PER_PAGE)); + p = WFIFOP(fd, 0); + p->packetId = HEADER_HC_ACK_CHARINFO_PER_PAGE; + p->packetLen = sizeof(struct PACKET_HC_ACK_CHARINFO_PER_PAGE); + WFIFOSET(fd, p->packetLen); + } #endif } /* Sends character ban list */ /* Made Possible by Yommy~! <3 */ -void char_mmo_char_send_ban_list(int fd, struct char_session_data *sd) { +static void char_mmo_char_send_ban_list(int fd, struct char_session_data *sd) +{ int i; time_t now = time(NULL); @@ -2006,23 +2193,27 @@ void char_mmo_char_send_ban_list(int fd, struct char_session_data *sd) { //---------------------------------------- // [Ind/Hercules] notify client about charselect window data //---------------------------------------- -void char_mmo_char_send_slots_info(int fd, struct char_session_data* sd) { +static void char_mmo_char_send_slots_info(int fd, struct char_session_data *sd) +{ +// also probably supported client 2013-02-15aRagexe but not 2013-02-15bRagexe [4144] +#if PACKETVER_MAIN_NUM >= 20130612 || PACKETVER_RE_NUM >= 20130115 || defined(PACKETVER_ZERO) nullpo_retv(sd); - WFIFOHEAD(fd,29); - WFIFOW(fd,0) = 0x82d; - WFIFOW(fd,2) = 29; - WFIFOB(fd,4) = sd->char_slots; - WFIFOB(fd,5) = MAX_CHARS - sd->char_slots; - WFIFOB(fd,6) = 0; - WFIFOB(fd,7) = sd->char_slots; - WFIFOB(fd,8) = sd->char_slots; - memset(WFIFOP(fd,9), 0, 20); // unused bytes - WFIFOSET(fd,29); + WFIFOHEAD(fd, 29); + WFIFOW(fd, 0) = 0x82d; + WFIFOW(fd, 2) = 29; + WFIFOB(fd, 4) = sd->char_slots; + WFIFOB(fd, 5) = MAX_CHARS - sd->char_slots; + WFIFOB(fd, 6) = 0; + WFIFOB(fd, 7) = sd->char_slots; + WFIFOB(fd, 8) = sd->char_slots; + memset(WFIFOP(fd, 9), 0, 20); // unused bytes + WFIFOSET(fd, 29); +#endif } //---------------------------------------- // Function to send characters to a player //---------------------------------------- -int char_mmo_char_send_characters(int fd, struct char_session_data* sd) +static int char_mmo_char_send_characters(int fd, struct char_session_data *sd) { int j, offset = 0; nullpo_ret(sd); @@ -2041,14 +2232,14 @@ int char_mmo_char_send_characters(int fd, struct char_session_data* sd) 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 #endif memset(WFIFOP(fd,4 + offset), 0, 20); // unknown bytes - j+=chr->mmo_chars_fromsql(sd, WFIFOP(fd,j)); + j += chr->mmo_chars_fromsql(sd, WFIFOP(fd, j), NULL); WFIFOW(fd,2) = j; // packet len WFIFOSET(fd,j); return 0; } -int char_char_married(int pl1, int pl2) +static int char_char_married(int pl1, int pl2) { if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `partner_id` FROM `%s` WHERE `char_id` = '%d'", char_db, pl1) ) Sql_ShowDebug(inter->sql_handle); @@ -2067,7 +2258,7 @@ int char_char_married(int pl1, int pl2) return 0; } -int char_char_child(int parent_id, int child_id) +static int char_char_child(int parent_id, int child_id) { if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `child` FROM `%s` WHERE `char_id` = '%d'", char_db, parent_id) ) Sql_ShowDebug(inter->sql_handle); @@ -2086,7 +2277,7 @@ int char_char_child(int parent_id, int child_id) return 0; } -int char_char_family(int cid1, int cid2, int cid3) +static int char_char_family(int cid1, int cid2, int cid3) { 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) ) Sql_ShowDebug(inter->sql_handle); @@ -2116,7 +2307,7 @@ int char_char_family(int cid1, int cid2, int cid3) //---------------------------------------------------------------------- // Force disconnection of an online player (with account value) by [Yor] //---------------------------------------------------------------------- -void char_disconnect_player(int account_id) +static void char_disconnect_player(int account_id) { int i; struct char_session_data* sd; @@ -2127,7 +2318,7 @@ void char_disconnect_player(int account_id) sockt->eof(i); } -void char_authfail_fd(int fd, int type) +static void char_authfail_fd(int fd, int type) { WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x81; @@ -2135,7 +2326,7 @@ void char_authfail_fd(int fd, int type) WFIFOSET(fd,3); } -void char_request_account_data(int account_id) +static void char_request_account_data(int account_id) { WFIFOHEAD(chr->login_fd,6); WFIFOW(chr->login_fd,0) = 0x2716; @@ -2181,21 +2372,21 @@ static void char_auth_ok(int fd, struct char_session_data *sd) // continues when account data is received... } -void char_ping_login_server(int fd) +static void char_ping_login_server(int fd) { WFIFOHEAD(fd,2);// sends a ping packet to login server (will receive pong 0x2718) WFIFOW(fd,0) = 0x2719; WFIFOSET(fd,2); } -int char_parse_fromlogin_connection_state(int fd) +static int char_parse_fromlogin_connection_state(int fd) { if (RFIFOB(fd,2)) { //printf("connect login server error : %d\n", RFIFOB(fd,2)); ShowError("Can not connect to login-server.\n"); ShowError("The server communication passwords (default s1/p1) are probably invalid.\n"); ShowError("Also, please make sure your login db has the correct communication username/passwords and the gender of the account is S.\n"); - ShowError("The communication passwords are set in /conf/map-server.conf and /conf/char/char-server.conf\n"); + ShowError("The communication passwords are set in /conf/map/map-server.conf and /conf/char/char-server.conf\n"); sockt->eof(fd); return 1; } else { @@ -2208,7 +2399,7 @@ int char_parse_fromlogin_connection_state(int fd) // 0 - rejected from server // -void char_auth_error(int fd, unsigned char flag) +static void char_auth_error(int fd, unsigned char flag) { WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x6c; @@ -2216,7 +2407,7 @@ void char_auth_error(int fd, unsigned char flag) WFIFOSET(fd,3); } -void char_parse_fromlogin_auth_state(int fd) +static void char_parse_fromlogin_auth_state(int fd) { struct char_session_data* sd = NULL; int account_id = RFIFOL(fd,2); @@ -2258,7 +2449,7 @@ void char_parse_fromlogin_auth_state(int fd) } } -void char_parse_fromlogin_account_data(int fd) +static void char_parse_fromlogin_account_data(int fd) { struct char_session_data* sd = (struct char_session_data*)sockt->session[fd]->session_data; int i; @@ -2284,12 +2475,8 @@ void char_parse_fromlogin_account_data(int fd) chr->auth_error(i, 0); } else { // send characters to player - #if PACKETVER >= 20130000 chr->mmo_char_send_slots_info(i, sd); chr->mmo_char_send_characters(i, sd); - #else - chr->mmo_char_send_characters(i, sd); - #endif #if PACKETVER >= 20060819 chr->mmo_char_send_ban_list(i, sd); #endif @@ -2301,14 +2488,14 @@ void char_parse_fromlogin_account_data(int fd) RFIFOSKIP(fd,72); } -void char_parse_fromlogin_login_pong(int fd) +static void char_parse_fromlogin_login_pong(int fd) { RFIFOSKIP(fd,2); if (sockt->session[fd]) sockt->session[fd]->flag.ping = 0; } -void char_changesex(int account_id, int sex) +static void char_changesex(int account_id, int sex) { unsigned char buf[7]; @@ -2329,7 +2516,7 @@ void char_changesex(int account_id, int sex) * @param class The character's current job class. * @param guild_id The character's guild ID. */ -void char_change_sex_sub(int sex, int acc, int char_id, int class, int guild_id) +static void char_change_sex_sub(int sex, int acc, int char_id, int class, int guild_id) { // job modification if (class == JOB_BARD || class == JOB_DANCER) @@ -2358,7 +2545,7 @@ void char_change_sex_sub(int sex, int acc, int char_id, int class, int guild_id) inter_guild->sex_changed(guild_id, acc, char_id, sex); } -int char_parse_fromlogin_changesex_reply(int fd) +static int char_parse_fromlogin_changesex_reply(int fd) { int char_id = 0, class = 0, guild_id = 0; int i; @@ -2384,13 +2571,13 @@ int char_parse_fromlogin_changesex_reply(int fd) stmt = SQL->StmtMalloc(inter->sql_handle); if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `char_id`,`class`,`guild_id` FROM `%s` WHERE `account_id` = '%d'", char_db, acc) || SQL_ERROR == SQL->StmtExecute(stmt) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &char_id, sizeof char_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &class, sizeof class, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &guild_id, sizeof guild_id, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); } - SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &char_id, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &class, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &guild_id, 0, NULL, NULL); for (i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i) { char_change_sex_sub(sex, acc, char_id, class, guild_id); @@ -2405,25 +2592,14 @@ int char_parse_fromlogin_changesex_reply(int fd) return 0; } -void char_parse_fromlogin_account_reg2(int fd) +static void char_parse_fromlogin_account_reg2(int fd) { //Receive account_reg2 registry, forward to map servers. mapif->sendall(RFIFOP(fd, 0), RFIFOW(fd,2)); RFIFOSKIP(fd, RFIFOW(fd,2)); } -void mapif_ban(int id, unsigned int flag, int status) -{ - // send to all map-servers to disconnect the player - unsigned char buf[11]; - WBUFW(buf,0) = 0x2b14; - WBUFL(buf,2) = id; - WBUFB(buf,6) = flag; // 0: change of status, 1: ban - WBUFL(buf,7) = status; // status or final date of a banishment - mapif->sendall(buf, 11); -} - -void char_parse_fromlogin_ban(int fd) +static void char_parse_fromlogin_ban(int fd) { mapif->ban(RFIFOL(fd,2), RFIFOB(fd,6), RFIFOL(fd,7)); // disconnect player if online on char-server @@ -2431,7 +2607,7 @@ void char_parse_fromlogin_ban(int fd) RFIFOSKIP(fd,11); } -void char_parse_fromlogin_kick(int fd) +static void char_parse_fromlogin_kick(int fd) { int aid = RFIFOL(fd,2); struct online_char_data* character = (struct online_char_data*)idb_get(chr->online_char_db, aid); @@ -2461,7 +2637,7 @@ void char_parse_fromlogin_kick(int fd) idb_remove(auth_db, aid);// reject auth attempts from map-server } -void char_update_ip(int fd) +static void char_update_ip(int fd) { WFIFOHEAD(fd,6); WFIFOW(fd,0) = 0x2736; @@ -2469,7 +2645,7 @@ void char_update_ip(int fd) WFIFOSET(fd,6); } -void char_parse_fromlogin_update_ip(int fd) +static void char_parse_fromlogin_update_ip(int fd) { unsigned char buf[2]; uint32 new_ip = 0; @@ -2492,22 +2668,23 @@ void char_parse_fromlogin_update_ip(int fd) RFIFOSKIP(fd,2); } -void char_parse_fromlogin_accinfo2_failed(int fd) +static void char_parse_fromlogin_accinfo2_failed(int fd) { - mapif->parse_accinfo2(false, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), + inter->accinfo2(false, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), NULL, NULL, NULL, NULL, NULL, NULL, NULL, -1, 0, 0); RFIFOSKIP(fd,18); } -void char_parse_fromlogin_accinfo2_ok(int fd) +static void char_parse_fromlogin_accinfo2_ok(int fd) { - mapif->parse_accinfo2(true, RFIFOL(fd,167), RFIFOL(fd,171), RFIFOL(fd,175), RFIFOL(fd,179), + inter->accinfo2(true, RFIFOL(fd,167), RFIFOL(fd,171), RFIFOL(fd,175), RFIFOL(fd,179), RFIFOP(fd,2), RFIFOP(fd,26), RFIFOP(fd,59), RFIFOP(fd,99), RFIFOP(fd,119), RFIFOP(fd,151), RFIFOP(fd,156), RFIFOL(fd,115), RFIFOL(fd,143), RFIFOL(fd,147)); RFIFOSKIP(fd,183); } -int char_parse_fromlogin(int fd) { +static int char_parse_fromlogin(int fd) +{ // only process data from the login-server if( fd != chr->login_fd ) { ShowDebug("chr->parse_fromlogin: Disconnecting invalid session #%d (is not the login-server)\n", fd); @@ -2639,7 +2816,7 @@ int char_parse_fromlogin(int fd) { return 0; } -int char_request_accreg2(int account_id, int char_id) +static int char_request_accreg2(int account_id, int char_id) { if (chr->login_fd > 0) { WFIFOHEAD(chr->login_fd,10); @@ -2655,7 +2832,8 @@ int char_request_accreg2(int account_id, int char_id) /** * Handles global account reg saving that continues with chr->global_accreg_to_login_add and global_accreg_to_send **/ -void char_global_accreg_to_login_start (int account_id, int char_id) { +static void char_global_accreg_to_login_start(int account_id, int char_id) +{ WFIFOHEAD(chr->login_fd, 60000 + 300); WFIFOW(chr->login_fd,0) = 0x2728; WFIFOW(chr->login_fd,2) = 14; @@ -2667,14 +2845,16 @@ void char_global_accreg_to_login_start (int account_id, int char_id) { /** * Completes global account reg saving that starts chr->global_accreg_to_login_start and continues with chr->global_accreg_to_login_add **/ -void char_global_accreg_to_login_send (void) { +static void char_global_accreg_to_login_send(void) +{ WFIFOSET(chr->login_fd, WFIFOW(chr->login_fd,2)); } /** * Handles global account reg saving that starts chr->global_accreg_to_login_start and ends with global_accreg_to_send **/ -void char_global_accreg_to_login_add (const char *key, unsigned int index, intptr_t val, bool is_string) { +static void char_global_accreg_to_login_add(const char *key, unsigned int index, intptr_t val, bool is_string) +{ int nlen = WFIFOW(chr->login_fd, 2); size_t len = strlen(key)+1; @@ -2721,7 +2901,8 @@ void char_global_accreg_to_login_add (const char *key, unsigned int index, intpt } } -void char_read_fame_list(void) { +static void char_read_fame_list(void) +{ int i; char* data; size_t len; @@ -2779,7 +2960,8 @@ void char_read_fame_list(void) { } // Send map-servers the fame ranking lists -int char_send_fame_list(int fd) { +static int char_send_fame_list(int fd) +{ int i, len = 8; unsigned char buf[32000]; @@ -2814,7 +2996,8 @@ int char_send_fame_list(int fd) { return 0; } -void char_update_fame_list(int type, int index, int fame) { +static void char_update_fame_list(int type, int index, int fame) +{ unsigned char buf[8]; WBUFW(buf,0) = 0x2b22; WBUFB(buf,2) = type; @@ -2825,7 +3008,7 @@ void char_update_fame_list(int type, int index, int fame) { //Loads a character's name and stores it in the buffer given (must be NAME_LENGTH in size) and not NULL //Returns 1 on found, 0 on not found (buffer is filled with Unknown char name) -int char_loadName(int char_id, char* name) +static int char_loadName(int char_id, char *name) { char* data; size_t len; @@ -2845,77 +3028,13 @@ int char_loadName(int char_id, char* name) return 0; } -/// Initializes a server structure. -void mapif_server_init(int id) -{ - //memset(&chr->server[id], 0, sizeof(server[id])); - chr->server[id].fd = -1; -} - -/// Destroys a server structure. -void mapif_server_destroy(int id) -{ - if( chr->server[id].fd == -1 ) - { - sockt->close(chr->server[id].fd); - chr->server[id].fd = -1; - } -} - - -/// Resets all the data related to a server. -void mapif_server_reset(int id) -{ - int i,j; - unsigned char buf[16384]; - int fd = chr->server[id].fd; - //Notify other map servers that this one is gone. [Skotlex] - WBUFW(buf,0) = 0x2b20; - WBUFL(buf,4) = htonl(chr->server[id].ip); - WBUFW(buf,8) = htons(chr->server[id].port); - j = 0; - for (i = 0; i < VECTOR_LENGTH(chr->server[id].maps); i++) { - uint16 m = VECTOR_INDEX(chr->server[id].maps, i); - if (m != 0) - WBUFW(buf,10+(j++)*4) = m; - } - if (j > 0) { - WBUFW(buf,2) = j * 4 + 10; - mapif->sendallwos(fd, buf, WBUFW(buf,2)); - } - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `index`='%d'", ragsrvinfo_db, chr->server[id].fd) ) - Sql_ShowDebug(inter->sql_handle); - chr->online_char_db->foreach(chr->online_char_db,chr->db_setoffline,id); //Tag relevant chars as 'in disconnected' server. - mapif->server_destroy(id); - mapif->server_init(id); -} - -/// Called when the connection to a Map Server is disconnected. -void mapif_on_disconnect(int id) -{ - ShowStatus("Map-server #%d has disconnected.\n", id); - mapif->server_reset(id); -} - -void mapif_on_parse_accinfo(int account_id, int u_fd, int u_aid, int u_group, int map_fd) { - Assert_retv(chr->login_fd > 0); - WFIFOHEAD(chr->login_fd,22); - WFIFOW(chr->login_fd,0) = 0x2740; - WFIFOL(chr->login_fd,2) = account_id; - WFIFOL(chr->login_fd,6) = u_fd; - WFIFOL(chr->login_fd,10) = u_aid; - WFIFOL(chr->login_fd,14) = u_group; - WFIFOL(chr->login_fd,18) = map_fd; - WFIFOSET(chr->login_fd,22); -} - -void char_parse_frommap_datasync(int fd) +static void char_parse_frommap_datasync(int fd) { sockt->datasync(fd, false); RFIFOSKIP(fd,RFIFOW(fd,2)); } -void char_parse_frommap_skillid2idx(int fd) +static void char_parse_frommap_skillid2idx(int fd) { int i; int j = RFIFOW(fd, 2) - 4; @@ -2924,8 +3043,8 @@ void char_parse_frommap_skillid2idx(int fd) if( j ) j /= 4; for(i = 0; i < j; i++) { - if( RFIFOW(fd, 4 + (i*4)) > MAX_SKILL_ID ) { - 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); + if (RFIFOW(fd, 4 + (i*4)) >= MAX_SKILL_ID) { + 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); continue; } skillid2idx[RFIFOW(fd, 4 + (i*4))] = RFIFOW(fd, 6 + (i*4)); @@ -2933,7 +3052,7 @@ void char_parse_frommap_skillid2idx(int fd) RFIFOSKIP(fd, RFIFOW(fd, 2)); } -void char_map_received_ok(int fd) +static void char_map_received_ok(int fd) { WFIFOHEAD(fd, 3 + NAME_LENGTH); WFIFOW(fd,0) = 0x2afb; @@ -2942,7 +3061,7 @@ void char_map_received_ok(int fd) WFIFOSET(fd,3+NAME_LENGTH); } -void char_send_maps(int fd, int id, int j) +static void char_send_maps(int fd, int id, int j) { int k,i; @@ -2979,7 +3098,7 @@ void char_send_maps(int fd, int id, int j) } } -void char_parse_frommap_map_names(int fd, int id) +static void char_parse_frommap_map_names(int fd, int id) { int i; @@ -3000,7 +3119,7 @@ void char_parse_frommap_map_names(int fd, int id) RFIFOSKIP(fd,RFIFOW(fd,2)); } -void char_send_scdata(int fd, int aid, int cid) +static void char_send_scdata(int fd, int aid, int cid) { #ifdef ENABLE_SC_SAVING if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` " @@ -3054,7 +3173,7 @@ void char_send_scdata(int fd, int aid, int cid) #endif } -void char_parse_frommap_request_scdata(int fd) +static void char_parse_frommap_request_scdata(int fd) { #ifdef ENABLE_SC_SAVING int aid = RFIFOL(fd,2); @@ -3064,7 +3183,7 @@ void char_parse_frommap_request_scdata(int fd) RFIFOSKIP(fd, 10); } -void char_parse_frommap_set_users_count(int fd, int id) +static void char_parse_frommap_set_users_count(int fd, int id) { if (RFIFOW(fd,2) != chr->server[id].users) { chr->server[id].users = RFIFOW(fd,2); @@ -3073,7 +3192,7 @@ void char_parse_frommap_set_users_count(int fd, int id) RFIFOSKIP(fd, 4); } -void char_parse_frommap_set_users(int fd, int id) +static void char_parse_frommap_set_users(int fd, int id) { //TODO: When data mismatches memory, update guild/party online/offline states. int i; @@ -3096,7 +3215,7 @@ void char_parse_frommap_set_users(int fd, int id) RFIFOSKIP(fd,RFIFOW(fd,2)); } -void char_save_character_ack(int fd, int aid, int cid) +static void char_save_character_ack(int fd, int aid, int cid) { WFIFOHEAD(fd,10); WFIFOW(fd,0) = 0x2b21; //Save ack only needed on final save. @@ -3105,7 +3224,7 @@ void char_save_character_ack(int fd, int aid, int cid) WFIFOSET(fd,10); } -void char_parse_frommap_save_character(int fd, int id) +static void char_parse_frommap_save_character(int fd, int id) { int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2); struct online_char_data* character; @@ -3139,7 +3258,7 @@ void char_parse_frommap_save_character(int fd, int id) // 0 - not ok // 1 - ok -void char_select_ack(int fd, int account_id, uint8 flag) +static void char_select_ack(int fd, int account_id, uint8 flag) { WFIFOHEAD(fd,7); WFIFOW(fd,0) = 0x2b03; @@ -3148,7 +3267,7 @@ void char_select_ack(int fd, int account_id, uint8 flag) WFIFOSET(fd,7); } -void char_parse_frommap_char_select_req(int fd) +static void char_parse_frommap_char_select_req(int fd) { int account_id = RFIFOL(fd,2); uint32 login_id1 = RFIFOL(fd,6); @@ -3185,7 +3304,7 @@ void char_parse_frommap_char_select_req(int fd) } } -void char_change_map_server_ack(int fd, const uint8 *data, bool ok) +static void char_change_map_server_ack(int fd, const uint8 *data, bool ok) { WFIFOHEAD(fd,30); WFIFOW(fd,0) = 0x2b06; @@ -3195,7 +3314,7 @@ void char_change_map_server_ack(int fd, const uint8 *data, bool ok) WFIFOSET(fd,30); } -void char_parse_frommap_change_map_server(int fd) +static void char_parse_frommap_change_map_server(int fd) { int map_id, map_fd = -1; struct mmo_charstatus* char_data; @@ -3248,7 +3367,7 @@ void char_parse_frommap_change_map_server(int fd) RFIFOSKIP(fd,39); } -void char_parse_frommap_remove_friend(int fd) +static void char_parse_frommap_remove_friend(int fd) { int char_id = RFIFOL(fd,2); int friend_id = RFIFOL(fd,6); @@ -3259,22 +3378,27 @@ void char_parse_frommap_remove_friend(int fd) RFIFOSKIP(fd,10); } -void char_char_name_ack(int fd, int char_id) +static void char_char_name_ack(int fd, int char_id) { WFIFOHEAD(fd,30); WFIFOW(fd,0) = 0x2b09; WFIFOL(fd,2) = char_id; +#if PACKETVER_MAIN_NUM >= 20180307 || PACKETVER_RE_NUM >= 20180221 || PACKETVER_ZERO_NUM >= 20180328 + if (chr->loadName(char_id, WFIFOP(fd,6)) == 0) + WFIFOL(fd, 6) = 0; +#else chr->loadName(char_id, WFIFOP(fd,6)); +#endif WFIFOSET(fd,30); } -void char_parse_frommap_char_name_request(int fd) +static void char_parse_frommap_char_name_request(int fd) { chr->char_name_ack(fd, RFIFOL(fd,2)); RFIFOSKIP(fd,6); } -void char_parse_frommap_change_email(int fd) +static void char_parse_frommap_change_email(int fd) { if (chr->login_fd > 0) { // don't send request if no login-server WFIFOHEAD(chr->login_fd,86); @@ -3285,17 +3409,7 @@ void char_parse_frommap_change_email(int fd) RFIFOSKIP(fd, 86); } -void mapif_char_ban(int char_id, time_t timestamp) -{ - unsigned char buf[11]; - WBUFW(buf,0) = 0x2b14; - WBUFL(buf,2) = char_id; - WBUFB(buf,6) = 2; - WBUFL(buf,7) = (unsigned int)timestamp; - mapif->sendall(buf, 11); -} - -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) +static 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) { time_t timestamp; struct tm *tmtime; @@ -3320,8 +3434,8 @@ void char_ban(int account_id, int char_id, time_t *unban_time, short year, short if( SQL_SUCCESS != SQL->StmtPrepare(stmt, "UPDATE `%s` SET `unban_time` = ? WHERE `char_id` = ? LIMIT 1", char_db) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_LONG, ×tamp, sizeof(timestamp)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_INT, &char_id, sizeof(char_id)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_TIME, ×tamp, sizeof timestamp) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_INT, &char_id, sizeof char_id) || SQL_SUCCESS != SQL->StmtExecute(stmt) ) { SqlStmt_ShowDebug(stmt); @@ -3330,14 +3444,14 @@ void char_ban(int account_id, int char_id, time_t *unban_time, short year, short SQL->StmtFree(stmt); // condition applies; send to all map-servers to disconnect the player - if( timestamp > time(NULL) ) { + if (timestamp > time(NULL)) { mapif->char_ban(char_id, timestamp); // disconnect player if online on char-server chr->disconnect_player(account_id); } } -void char_unban(int char_id, int *result) +static void char_unban(int char_id, int *result) { /* handled by char server, so no redirection */ if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `unban_time` = '0' WHERE `char_id` = '%d' LIMIT 1", char_db, char_id) ) { @@ -3347,7 +3461,7 @@ void char_unban(int char_id, int *result) } } -void char_ask_name_ack(int fd, int acc, const char* name, int type, int result) +static void char_ask_name_ack(int fd, int acc, const char *name, int type, int result) { nullpo_retv(name); WFIFOHEAD(fd,34); @@ -3369,7 +3483,7 @@ void char_ask_name_ack(int fd, int acc, const char* name, int type, int result) * @retval 0 in case of success. * @retval 1 in case of failure. */ -int char_changecharsex(int char_id, int sex) +static int char_changecharsex(int char_id, int sex) { int class = 0, guild_id = 0, account_id = 0; char *data; @@ -3402,7 +3516,7 @@ int char_changecharsex(int char_id, int sex) return 0; } -void char_parse_frommap_change_account(int fd) +static void char_parse_frommap_change_account(int fd) { int result = 0; // 0-login-server request done, 1-player not found, 2-gm level too low, 3-login-server offline char esc_name[NAME_LENGTH*2+1]; @@ -3488,7 +3602,7 @@ void char_parse_frommap_change_account(int fd) } } -void char_parse_frommap_fame_list(int fd) +static void char_parse_frommap_fame_list(int fd) { int cid = RFIFOL(fd, 2); int fame = RFIFOL(fd, 6); @@ -3539,13 +3653,13 @@ void char_parse_frommap_fame_list(int fd) RFIFOSKIP(fd,11); } -void char_parse_frommap_divorce_char(int fd) +static void char_parse_frommap_divorce_char(int fd) { chr->divorce_char_sql(RFIFOL(fd,2), RFIFOL(fd,6)); RFIFOSKIP(fd,10); } -void char_parse_frommap_ragsrvinfo(int fd) +static void char_parse_frommap_ragsrvinfo(int fd) { char esc_server_name[sizeof(chr->server_name)*2+1]; @@ -3559,32 +3673,32 @@ void char_parse_frommap_ragsrvinfo(int fd) RFIFOSKIP(fd,14); } -void char_parse_frommap_set_char_offline(int fd) +static void char_parse_frommap_set_char_offline(int fd) { chr->set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6)); RFIFOSKIP(fd,10); } -void char_parse_frommap_set_all_offline(int fd, int id) +static void char_parse_frommap_set_all_offline(int fd, int id) { chr->set_all_offline(id); RFIFOSKIP(fd,2); } -void char_parse_frommap_set_char_online(int fd, int id) +static void char_parse_frommap_set_char_online(int fd, int id) { chr->set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6)); RFIFOSKIP(fd,10); } -void char_parse_frommap_build_fame_list(int fd) +static void char_parse_frommap_build_fame_list(int fd) { chr->read_fame_list(); chr->send_fame_list(-1); RFIFOSKIP(fd,2); } -void char_parse_frommap_save_status_change_data(int fd) +static void char_parse_frommap_save_status_change_data(int fd) { #ifdef ENABLE_SC_SAVING int aid = RFIFOL(fd, 4); @@ -3619,20 +3733,20 @@ void char_parse_frommap_save_status_change_data(int fd) RFIFOSKIP(fd, RFIFOW(fd, 2)); } -void char_send_pong(int fd) +static void char_send_pong(int fd) { WFIFOHEAD(fd,2); WFIFOW(fd,0) = 0x2b24; WFIFOSET(fd,2); } -void char_parse_frommap_ping(int fd) +static void char_parse_frommap_ping(int fd) { chr->send_pong(fd); RFIFOSKIP(fd,2); } -void char_map_auth_ok(int fd, int account_id, struct char_auth_node* node, struct mmo_charstatus* cd) +static void char_map_auth_ok(int fd, int account_id, struct char_auth_node *node, struct mmo_charstatus *cd) { nullpo_retv(cd); WFIFOHEAD(fd,25 + sizeof(struct mmo_charstatus)); @@ -3659,7 +3773,7 @@ void char_map_auth_ok(int fd, int account_id, struct char_auth_node* node, struc WFIFOSET(fd, WFIFOW(fd,2)); } -void char_map_auth_failed(int fd, int account_id, int char_id, int login_id1, char sex, uint32 ip) +static void char_map_auth_failed(int fd, int account_id, int char_id, int login_id1, char sex, uint32 ip) { WFIFOHEAD(fd,19); WFIFOW(fd,0) = 0x2b27; @@ -3671,7 +3785,7 @@ void char_map_auth_failed(int fd, int account_id, int char_id, int login_id1, ch WFIFOSET(fd,19); } -void char_parse_frommap_auth_request(int fd, int id) +static void char_parse_frommap_auth_request(int fd, int id) { struct mmo_charstatus char_dat; struct char_auth_node* node; @@ -3724,49 +3838,14 @@ void char_parse_frommap_auth_request(int fd, int id) } } -void char_parse_frommap_update_ip(int fd, int id) +static void char_parse_frommap_update_ip(int fd, int id) { chr->server[id].ip = ntohl(RFIFOL(fd, 2)); ShowInfo("Updated IP address of map-server #%d to %u.%u.%u.%u.\n", id, CONVIP(chr->server[id].ip)); RFIFOSKIP(fd,6); } -void char_parse_frommap_request_stats_report(int fd) -{ - int sfd;/* stat server fd */ - struct hSockOpt opt; - 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 */ - - opt.silent = 1; - opt.setTimeo = 1; - - if ((sfd = sockt->make_connection(sockt->host2ip("stats.herc.ws"),(uint16)25427,&opt) ) == -1) { - RFIFOSKIP(fd, RFIFOW(fd,2) );/* skip this packet */ - RFIFOFLUSH(fd); - return;/* connection not possible, we drop the report */ - } - - sockt->session[sfd]->flag.server = 1;/* to ensure we won't drop our own packet */ - sockt->realloc_fifo(sfd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - - WFIFOHEAD(sfd, RFIFOW(fd,2) ); - - memcpy(WFIFOP(sfd,0), RFIFOP(fd, 0), RFIFOW(fd,2)); - - WFIFOSET(sfd, RFIFOW(fd,2) ); - - do { - sockt->flush(sfd); - HSleep(1); - } while( !sockt->session[sfd]->flag.eof && sockt->session[sfd]->wdata_size ); - - sockt->close(sfd); - - RFIFOSKIP(fd, RFIFOW(fd,2) );/* skip this packet */ - RFIFOFLUSH(fd); -} - -void char_parse_frommap_scdata_update(int fd) +static void char_parse_frommap_scdata_update(int fd) { int account_id = RFIFOL(fd, 2); int char_id = RFIFOL(fd, 6); @@ -3786,7 +3865,7 @@ void char_parse_frommap_scdata_update(int fd) RFIFOSKIP(fd, 28); } -void char_parse_frommap_scdata_delete(int fd) +static void char_parse_frommap_scdata_delete(int fd) { int account_id = RFIFOL(fd, 2); int char_id = RFIFOL(fd, 6); @@ -3800,7 +3879,7 @@ void char_parse_frommap_scdata_delete(int fd) RFIFOSKIP(fd, 12); } -int char_parse_frommap(int fd) +static int char_parse_frommap(int fd) { int id; @@ -3996,14 +4075,6 @@ int char_parse_frommap(int fd) chr->parse_frommap_update_ip(fd, id); break; - case 0x3008: - if( RFIFOREST(fd) < RFIFOW(fd,4) ) - return 0;/* packet wasn't fully received yet (still fragmented) */ - else { - chr->parse_frommap_request_stats_report(fd); - } - break; - /* individual sc data insertion/update */ case 0x2740: if( RFIFOREST(fd) < 28 ) @@ -4040,14 +4111,14 @@ int char_parse_frommap(int fd) return 0; } -void do_init_mapif(void) +static void do_init_mapif(void) { int i; for( i = 0; i < ARRAYLENGTH(chr->server); ++i ) mapif->server_init(i); } -void do_final_mapif(void) +static void do_final_mapif(void) { int i; for( i = 0; i < ARRAYLENGTH(chr->server); ++i ) @@ -4056,7 +4127,7 @@ void do_final_mapif(void) // Searches for the mapserver that has a given map (and optionally ip/port, if not -1). // If found, returns the server's index in the 'server' array (otherwise returns -1). -int char_search_mapserver(unsigned short map, uint32 ip, uint16 port) +static int char_search_mapserver(unsigned short map, uint32 ip, uint16 port) { int i, j; @@ -4088,7 +4159,7 @@ static int char_mapif_init(int fd) * @retval 0 if it is a WAN IP. * @return the appropriate LAN server address to send, if it is a LAN IP. */ -uint32 char_lan_subnet_check(uint32 ip) +static uint32 char_lan_subnet_check(uint32 ip) { struct s_subnet lan = {0}; if (sockt->lan_subnet_check(ip, &lan)) { @@ -4108,7 +4179,7 @@ uint32 char_lan_subnet_check(uint32 ip) /// 4 (0x71a): To delete a character you must withdraw from the guild. /// 5 (0x71b): To delete a character you must withdraw from the party. /// Any (0x718): An unknown error has occurred. -void char_delete2_ack(int fd, int char_id, uint32 result, time_t delete_date) +static void char_delete2_ack(int fd, int char_id, uint32 result, time_t delete_date) {// HC: <0828>.W <char id>.L <Msg:0-5>.L <deleteDate>.L WFIFOHEAD(fd,14); WFIFOW(fd,0) = 0x828; @@ -4123,7 +4194,7 @@ void char_delete2_ack(int fd, int char_id, uint32 result, time_t delete_date) WFIFOSET(fd,14); } -void char_delete2_accept_actual_ack(int fd, int char_id, uint32 result) +static void char_delete2_accept_actual_ack(int fd, int char_id, uint32 result) { WFIFOHEAD(fd,10); WFIFOW(fd,0) = 0x82a; @@ -4140,12 +4211,12 @@ void char_delete2_accept_actual_ack(int fd, int char_id, uint32 result) /// 4 (0x71d): Deleting not yet possible time. /// 5 (0x71e): Date of birth do not match. /// Any (0x718): An unknown error has occurred. -void char_delete2_accept_ack(int fd, int char_id, uint32 result) +static void char_delete2_accept_ack(int fd, int char_id, uint32 result) {// HC: <082a>.W <char id>.L <Msg:0-5>.L -#if PACKETVER >= 20130000 /* not sure the exact date -- must refresh or client gets stuck */ +#if PACKETVER_MAIN_NUM >= 20130522 || PACKETVER_RE_NUM >= 20130327 || defined(PACKETVER_ZERO) if( result == 1 ) { struct char_session_data* sd = (struct char_session_data*)sockt->session[fd]->session_data; - chr->mmo_char_send099d(fd, sd); + chr->send_HC_ACK_CHARINFO_PER_PAGE(fd, sd); } #endif chr->delete2_accept_actual_ack(fd, char_id, result); @@ -4155,7 +4226,7 @@ void char_delete2_accept_ack(int fd, int char_id, uint32 result) /// 1 (0x718): none/success, (if char id not in deletion process): An unknown error has occurred. /// 2 (0x719): A database error occurred. /// Any (0x718): An unknown error has occurred. -void char_delete2_cancel_ack(int fd, int char_id, uint32 result) +static void char_delete2_cancel_ack(int fd, int char_id, uint32 result) {// HC: <082c>.W <char id>.L <Msg:1-2>.L WFIFOHEAD(fd,10); WFIFOW(fd,0) = 0x82c; @@ -4164,7 +4235,7 @@ void char_delete2_cancel_ack(int fd, int char_id, uint32 result) WFIFOSET(fd,10); } -static void char_delete2_req(int fd, struct char_session_data* sd) +static void char_delete2_req(int fd, struct char_session_data *sd) {// CH: <0827>.W <char id>.L int char_id, i; char* data; @@ -4235,7 +4306,7 @@ static void char_delete2_req(int fd, struct char_session_data* sd) chr->delete2_ack(fd, char_id, 1, delete_date); // 1: success } -static void char_delete2_accept(int fd, struct char_session_data* sd) +static void char_delete2_accept(int fd, struct char_session_data *sd) {// CH: <0829>.W <char id>.L <birth date:YYMMDD>.6B char birthdate[8+1]; int char_id, i; @@ -4307,7 +4378,7 @@ static void char_delete2_accept(int fd, struct char_session_data* sd) chr->delete2_accept_ack(fd, char_id, 1); // 1: success } -static void char_delete2_cancel(int fd, struct char_session_data* sd) +static void char_delete2_cancel(int fd, struct char_session_data *sd) {// CH: <082b>.W <char id>.L int char_id, i; @@ -4334,14 +4405,14 @@ static void char_delete2_cancel(int fd, struct char_session_data* sd) chr->delete2_cancel_ack(fd, char_id, 1); // 1: success } -void char_send_account_id(int fd, int account_id) +static void char_send_account_id(int fd, int account_id) { - WFIFOHEAD(fd,4); - WFIFOL(fd,0) = account_id; - WFIFOSET(fd,4); + WFIFOHEAD(fd, 4); + WFIFOL(fd, 0) = account_id; + WFIFOSET2(fd, 4); } -void char_parse_char_connect(int fd, struct char_session_data* sd, uint32 ipl) +static void char_parse_char_connect(int fd, struct char_session_data *sd, uint32 ipl) { int account_id = RFIFOL(fd,2); uint32 login_id1 = RFIFOL(fd,6); @@ -4373,6 +4444,7 @@ void char_parse_char_connect(int fd, struct char_session_data* sd, uint32 ipl) if( core->runflag != CHARSERVER_ST_RUNNING ) { chr->auth_error(fd, 0); + sockt->eof(fd); return; } @@ -4387,11 +4459,13 @@ void char_parse_char_connect(int fd, struct char_session_data* sd, uint32 ipl) /* restrictions apply */ if( chr->server_type == CST_MAINTENANCE && node->group_id < char_maintenance_min_group_id ) { chr->auth_error(fd, 0); + sockt->eof(fd); return; } /* the client will already deny this request, this check is to avoid someone bypassing. */ if( chr->server_type == CST_PAYING && (time_t)node->expiration_time < time(NULL) ) { chr->auth_error(fd, 0); + sockt->eof(fd); return; } idb_remove(auth_db, account_id); @@ -4403,23 +4477,38 @@ void char_parse_char_connect(int fd, struct char_session_data* sd, uint32 ipl) loginif->auth(fd, sd, ipl); } else { // if no login-server, we must refuse connection chr->auth_error(fd, 0); + sockt->eof(fd); } } } -void char_send_map_info(int fd, int i, uint32 subnet_map_ip, struct mmo_charstatus *cd) +static void char_send_map_info(int fd, int i, uint32 subnet_map_ip, struct mmo_charstatus *cd, char *dnsHost) { +#if PACKETVER < 20170329 + const int cmd = 0x71; + const int len = 28; +#else + const int cmd = 0xac5; + const int len = 156; +#endif nullpo_retv(cd); - WFIFOHEAD(fd,28); - WFIFOW(fd,0) = 0x71; - WFIFOL(fd,2) = cd->char_id; - mapindex->getmapname_ext(mapindex_id2name(cd->last_point.map), WFIFOP(fd,6)); - WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : chr->server[i].ip); - WFIFOW(fd,26) = sockt->ntows(htons(chr->server[i].port)); // [!] LE byte order here [!] - WFIFOSET(fd,28); + WFIFOHEAD(fd, len); + WFIFOW(fd, 0) = cmd; + WFIFOL(fd, 2) = cd->char_id; + mapindex->getmapname_ext(mapindex_id2name(cd->last_point.map), WFIFOP(fd, 6)); + WFIFOL(fd, 22) = htonl((subnet_map_ip) ? subnet_map_ip : chr->server[i].ip); + WFIFOW(fd, 26) = sockt->ntows(htons(chr->server[i].port)); // [!] LE byte order here [!] +#if PACKETVER >= 20170329 + if (dnsHost != NULL) { + safestrncpy(WFIFOP(fd, 28), dnsHost, 128); + } else { + memset(WFIFOP(fd, 28), 0, 128); + } +#endif + WFIFOSET(fd, len); } -void char_send_wait_char_server(int fd) +static void char_send_wait_char_server(int fd) { WFIFOHEAD(fd, 24); WFIFOW(fd, 0) = 0x840; @@ -4428,7 +4517,7 @@ void char_send_wait_char_server(int fd) WFIFOSET(fd, 24); } -int char_search_default_maps_mapserver(struct mmo_charstatus *cd) +static int char_search_default_maps_mapserver(struct mmo_charstatus *cd) { int i; int j; @@ -4460,8 +4549,8 @@ int char_search_default_maps_mapserver(struct mmo_charstatus *cd) return i; } -void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) __attribute__((nonnull (2))); -void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) +static void char_parse_char_select(int fd, struct char_session_data *sd, uint32 ipl) __attribute__((nonnull (2))); +static void char_parse_char_select(int fd, struct char_session_data *sd, uint32 ipl) { struct mmo_charstatus char_dat; struct mmo_charstatus *cd; @@ -4535,8 +4624,19 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) // FIXME: Why are we re-escaping the name if it was already escaped in rename/make_new_char? [Panikon] SQL->EscapeStringLen(inter->sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH)); if (SQL_ERROR == SQL->Query(inter->sql_handle, - "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `name`) VALUES (NOW(), '%d', '%d', '%d', '%s')", - charlog_db, sd->account_id, cd->char_id, slot, esc_name)) + "INSERT INTO `%s`(" + " `time`, `char_msg`, `account_id`, `char_id`, `char_num`, `class`, `name`," + " `str`, `agi`, `vit`, `int`, `dex`, `luk`," + " `hair`, `hair_color`" + ") VALUES (" + " NOW(), 'char select', '%d', '%d', '%d', '%d', '%s'," + " '%d', '%d', '%d', '%d', '%d', '%d'," + " '%d', '%d')", + charlog_db, + sd->account_id, cd->char_id, slot, char_dat.class, esc_name, + char_dat.str, char_dat.agi, char_dat.vit, char_dat.int_, char_dat.dex, char_dat.luk, + char_dat.hair, char_dat.hair_color + )) Sql_ShowDebug(inter->sql_handle); } ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name); @@ -4576,7 +4676,7 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) subnet_map_ip = chr->lan_subnet_check(ipl); //Send player to map - chr->send_map_info(fd, i, subnet_map_ip, cd); + chr->send_map_info(fd, i, subnet_map_ip, cd, NULL); // create temporary auth entry CREATE(node, struct char_auth_node, 1); @@ -4591,14 +4691,15 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) idb_put(auth_db, sd->account_id, node); } -void char_creation_failed(int fd, int result) +static void char_creation_failed(int fd, int result) { WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x6e; /* Others I found [Ind] */ /* 0x02 = Symbols in Character Names are forbidden */ /* 0x03 = You are not eligible to open the Character Slot. */ - /* 0x0B = This service is only available for premium users. */ + /* 0x0B = This service is only available for premium users. */ + /* 0x0C = Character name is invalid. */ switch (result) { case -1: WFIFOB(fd,2) = 0x00; break; // 'Charname already exists' case -2: WFIFOB(fd,2) = 0xFF; break; // 'Char creation denied' @@ -4614,7 +4715,7 @@ void char_creation_failed(int fd, int result) WFIFOSET(fd,3); } -void char_creation_ok(int fd, struct mmo_charstatus *char_dat) +static void char_creation_ok(int fd, struct mmo_charstatus *char_dat) { int len; @@ -4625,21 +4726,35 @@ void char_creation_ok(int fd, struct mmo_charstatus *char_dat) WFIFOSET(fd,len); } -void char_parse_char_create_new_char(int fd, struct char_session_data* sd) __attribute__((nonnull (2))); -void char_parse_char_create_new_char(int fd, struct char_session_data* sd) +static void char_parse_char_create_new_char(int fd, struct char_session_data *sd) __attribute__((nonnull (2))); +static void char_parse_char_create_new_char(int fd, struct char_session_data *sd) { int result; if (!enable_char_creation) { //turn character creation on/off [Kevin] result = -2; } else { - #if PACKETVER >= 20151001 - result = chr->make_new_char_sql(sd, RFIFOP(fd,2), 1, 1, 1, 1, 1, 1, RFIFOB(fd,26), RFIFOW(fd,27), RFIFOW(fd,29), RFIFOW(fd, 31)); - #elif PACKETVER >= 20120307 - result = chr->make_new_char_sql(sd, RFIFOP(fd,2), 1, 1, 1, 1, 1, 1, RFIFOB(fd,26), RFIFOW(fd,27), RFIFOW(fd,29), JOB_NOVICE); - #else - result = chr->make_new_char_sql(sd, 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), JOB_NOVICE); - #endif +#if PACKETVER >= 20151001 + uint8 sex = RFIFOB(fd, 35); + + switch (sex) { + case SEX_FEMALE: + sex = 'F'; + break; + case SEX_MALE: + sex = 'M'; + break; + default: + chr->creation_failed(fd, -2); // Char Creation Denied + RFIFOSKIP(fd, 36); + return; + } + result = chr->make_new_char_sql(sd, RFIFOP(fd, 2), 1, 1, 1, 1, 1, 1, RFIFOB(fd, 26), RFIFOW(fd, 27), RFIFOW(fd, 29), RFIFOL(fd, 31), sex); +#elif PACKETVER >= 20120307 + result = chr->make_new_char_sql(sd, RFIFOP(fd, 2), 1, 1, 1, 1, 1, 1, RFIFOB(fd, 26), RFIFOW(fd, 27), RFIFOW(fd, 29), JOB_NOVICE, 'U'); +#else + result = chr->make_new_char_sql(sd, 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), JOB_NOVICE, 'U'); +#endif } //'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3) @@ -4665,7 +4780,7 @@ void char_parse_char_create_new_char(int fd, struct char_session_data* sd) // flag: // 0 = Incorrect Email address -void char_delete_char_failed(int fd, int flag) +static void char_delete_char_failed(int fd, int flag) { WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x70; @@ -4673,15 +4788,15 @@ void char_delete_char_failed(int fd, int flag) WFIFOSET(fd,3); } -void char_delete_char_ok(int fd) +static void char_delete_char_ok(int fd) { WFIFOHEAD(fd,2); WFIFOW(fd,0) = 0x6f; WFIFOSET(fd,2); } -void char_parse_char_delete_char(int fd, struct char_session_data* sd, unsigned short cmd) __attribute__((nonnull (2))); -void char_parse_char_delete_char(int fd, struct char_session_data* sd, unsigned short cmd) +static void char_parse_char_delete_char(int fd, struct char_session_data *sd, unsigned short cmd) __attribute__((nonnull (2))); +static void char_parse_char_delete_char(int fd, struct char_session_data *sd, unsigned short cmd) { char email[40]; int cid = RFIFOL(fd,2); @@ -4735,12 +4850,12 @@ void char_parse_char_delete_char(int fd, struct char_session_data* sd, unsigned chr->delete_char_ok(fd); } -void char_parse_char_ping(int fd) +static void char_parse_char_ping(int fd) { RFIFOSKIP(fd,6); } -void char_allow_rename(int fd, int flag) +static void char_allow_rename(int fd, int flag) { WFIFOHEAD(fd, 4); WFIFOW(fd,0) = 0x28e; @@ -4748,12 +4863,11 @@ void char_allow_rename(int fd, int flag) WFIFOSET(fd,4); } -void char_parse_char_rename_char(int fd, struct char_session_data* sd) __attribute__((nonnull (2))); -void char_parse_char_rename_char(int fd, struct char_session_data* sd) +static void char_parse_char_rename_char(int fd, struct char_session_data *sd) __attribute__((nonnull (2))); +static void char_parse_char_rename_char(int fd, struct char_session_data *sd) { int i, cid =RFIFOL(fd,2); char name[NAME_LENGTH]; - char esc_name[NAME_LENGTH*2+1]; safestrncpy(name, RFIFOP(fd,6), NAME_LENGTH); RFIFOSKIP(fd,30); @@ -4762,8 +4876,7 @@ void char_parse_char_rename_char(int fd, struct char_session_data* sd) return; normalize_name(name,TRIM_CHARS); - SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); - if( !chr->check_char_name(name,esc_name) ) { + if (chr->check_char_name(name, NULL) == 0) { i = 1; safestrncpy(sd->new_name, name, NAME_LENGTH); } else { @@ -4773,12 +4886,11 @@ void char_parse_char_rename_char(int fd, struct char_session_data* sd) chr->allow_rename(fd, i); } -void char_parse_char_rename_char2(int fd, struct char_session_data* sd) __attribute__((nonnull (2))); -void char_parse_char_rename_char2(int fd, struct char_session_data* sd) +static void char_parse_char_rename_char2(int fd, struct char_session_data *sd) __attribute__((nonnull (2))); +static void char_parse_char_rename_char2(int fd, struct char_session_data *sd) { int i, aid = RFIFOL(fd,2), cid =RFIFOL(fd,6); char name[NAME_LENGTH]; - char esc_name[NAME_LENGTH*2+1]; safestrncpy(name, RFIFOP(fd,10), NAME_LENGTH); RFIFOSKIP(fd,34); @@ -4789,19 +4901,17 @@ void char_parse_char_rename_char2(int fd, struct char_session_data* sd) return; normalize_name(name,TRIM_CHARS); - SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); - if( !chr->check_char_name(name,esc_name) ) - { + if (chr->check_char_name(name, NULL) == 0) { i = 1; safestrncpy(sd->new_name, name, NAME_LENGTH); - } - else + } else { i = 0; + } chr->allow_rename(fd, i); } -void char_rename_char_ack(int fd, int flag) +static void char_rename_char_ack(int fd, int flag) { WFIFOHEAD(fd, 4); WFIFOW(fd,0) = 0x290; @@ -4809,8 +4919,8 @@ void char_rename_char_ack(int fd, int flag) WFIFOSET(fd,4); } -void char_parse_char_rename_char_confirm(int fd, struct char_session_data* sd) __attribute__((nonnull (2))); -void char_parse_char_rename_char_confirm(int fd, struct char_session_data* sd) +static void char_parse_char_rename_char_confirm(int fd, struct char_session_data *sd) __attribute__((nonnull (2))); +static void char_parse_char_rename_char_confirm(int fd, struct char_session_data *sd) { int i; int cid = RFIFOL(fd,2); @@ -4824,7 +4934,7 @@ void char_parse_char_rename_char_confirm(int fd, struct char_session_data* sd) chr->rename_char_ack(fd, i); } -void char_captcha_notsupported(int fd) +static void char_captcha_notsupported(int fd) { WFIFOHEAD(fd,5); WFIFOW(fd,0) = 0x7e9; @@ -4833,31 +4943,31 @@ void char_captcha_notsupported(int fd) WFIFOSET(fd,5); } -void char_parse_char_request_captcha(int fd) +static void char_parse_char_request_captcha(int fd) { chr->captcha_notsupported(fd); RFIFOSKIP(fd,8); } -void char_parse_char_check_captcha(int fd) +static void char_parse_char_check_captcha(int fd) { chr->captcha_notsupported(fd); RFIFOSKIP(fd,32); } -void char_parse_char_delete2_req(int fd, struct char_session_data* sd) +static void char_parse_char_delete2_req(int fd, struct char_session_data *sd) { chr->delete2_req(fd, sd); RFIFOSKIP(fd,6); } -void char_parse_char_delete2_accept(int fd, struct char_session_data* sd) +static void char_parse_char_delete2_accept(int fd, struct char_session_data *sd) { chr->delete2_accept(fd, sd); RFIFOSKIP(fd,12); } -void char_parse_char_delete2_cancel(int fd, struct char_session_data* sd) +static void char_parse_char_delete2_cancel(int fd, struct char_session_data *sd) { chr->delete2_cancel(fd, sd); RFIFOSKIP(fd,6); @@ -4866,15 +4976,15 @@ void char_parse_char_delete2_cancel(int fd, struct char_session_data* sd) // flag: // 0 - ok // 3 - error -void char_login_map_server_ack(int fd, uint8 flag) +static void char_login_map_server_ack(int fd, uint8 flag) { - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x2af9; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,3); + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x2af9; + WFIFOB(fd, 2) = flag; + WFIFOSET2(fd, 3); } -void char_parse_char_login_map_server(int fd, uint32 ipl) +static void char_parse_char_login_map_server(int fd, uint32 ipl) { char l_user[24], l_pass[24]; int i; @@ -4889,6 +4999,7 @@ void char_parse_char_login_map_server(int fd, uint32 ipl) !sockt->allowed_ip_check(ipl)) { chr->login_map_server_ack(fd, 3); // Failure + sockt->eof(fd); } else { chr->login_map_server_ack(fd, 0); // Success @@ -4898,6 +5009,7 @@ void char_parse_char_login_map_server(int fd, uint32 ipl) chr->server[i].users = 0; sockt->session[fd]->func_parse = chr->parse_frommap; sockt->session[fd]->flag.server = 1; + sockt->session[fd]->flag.validate = 0; sockt->realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); chr->mapif_init(fd); } @@ -4906,8 +5018,8 @@ void char_parse_char_login_map_server(int fd, uint32 ipl) RFIFOSKIP(fd,60); } -void char_parse_char_pincode_check(int fd, struct char_session_data* sd) __attribute__((nonnull (2))); -void char_parse_char_pincode_check(int fd, struct char_session_data* sd) +static void char_parse_char_pincode_check(int fd, struct char_session_data *sd) __attribute__((nonnull (2))); +static void char_parse_char_pincode_check(int fd, struct char_session_data *sd) { if (RFIFOL(fd,2) == sd->account_id) pincode->check(fd, sd); @@ -4915,17 +5027,17 @@ void char_parse_char_pincode_check(int fd, struct char_session_data* sd) RFIFOSKIP(fd, 10); } -void char_parse_char_pincode_window(int fd, struct char_session_data* sd) __attribute__((nonnull (2))); -void char_parse_char_pincode_window(int fd, struct char_session_data* sd) +static void char_parse_char_pincode_window(int fd, struct char_session_data *sd) __attribute__((nonnull (2))); +static void char_parse_char_pincode_window(int fd, struct char_session_data *sd) { if (RFIFOL(fd,2) == sd->account_id) - pincode->sendstate(fd, sd, PINCODE_NOTSET); + pincode->loginstate(fd, sd, PINCODE_LOGIN_NOTSET); RFIFOSKIP(fd, 6); } -void char_parse_char_pincode_change(int fd, struct char_session_data* sd) __attribute__((nonnull (2))); -void char_parse_char_pincode_change(int fd, struct char_session_data* sd) +static void char_parse_char_pincode_change(int fd, struct char_session_data *sd) __attribute__((nonnull (2))); +static void char_parse_char_pincode_change(int fd, struct char_session_data *sd) { if (RFIFOL(fd,2) == sd->account_id) pincode->change(fd, sd); @@ -4933,21 +5045,21 @@ void char_parse_char_pincode_change(int fd, struct char_session_data* sd) RFIFOSKIP(fd, 14); } -void char_parse_char_pincode_first_pin(int fd, struct char_session_data* sd) __attribute__((nonnull (2))); -void char_parse_char_pincode_first_pin(int fd, struct char_session_data* sd) +static void char_parse_char_pincode_first_pin(int fd, struct char_session_data *sd) __attribute__((nonnull (2))); +static void char_parse_char_pincode_first_pin(int fd, struct char_session_data *sd) { if (RFIFOL(fd,2) == sd->account_id) pincode->setnew (fd, sd); RFIFOSKIP(fd, 10); } -void char_parse_char_request_chars(int fd, struct char_session_data* sd) +static void char_parse_char_request_chars(int fd, struct char_session_data *sd) { - chr->mmo_char_send099d(fd, sd); + chr->send_HC_ACK_CHARINFO_PER_PAGE(fd, sd); RFIFOSKIP(fd,2); } -void char_change_character_slot_ack(int fd, bool ret) +static void char_change_character_slot_ack(int fd, bool ret) { WFIFOHEAD(fd, 8); WFIFOW(fd, 0) = 0x8d5; @@ -4957,28 +5069,28 @@ void char_change_character_slot_ack(int fd, bool ret) WFIFOSET(fd, 8); } -void char_parse_char_move_character(int fd, struct char_session_data* sd) +static void char_parse_char_move_character(int fd, struct char_session_data *sd) { bool ret = chr->char_slotchange(sd, fd, RFIFOW(fd, 2), RFIFOW(fd, 4)); chr->change_character_slot_ack(fd, ret); /* for some stupid reason it requires the char data again (gravity -_-) */ if( ret ) -#if PACKETVER >= 20130000 - chr->mmo_char_send099d(fd, sd); +#if PACKETVER_MAIN_NUM >= 20130522 || PACKETVER_RE_NUM >= 20130327 || defined(PACKETVER_ZERO) + chr->send_HC_ACK_CHARINFO_PER_PAGE(fd, sd); #else chr->mmo_char_send_characters(fd, sd); #endif RFIFOSKIP(fd, 8); } -int char_parse_char_unknown_packet(int fd, uint32 ipl) +static int char_parse_char_unknown_packet(int fd, uint32 ipl) { 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)); sockt->eof(fd); return 1; } -int char_parse_char(int fd) +static int char_parse_char(int fd) { unsigned short cmd; struct char_session_data* sd; @@ -5042,7 +5154,7 @@ int char_parse_char(int fd) // S 0a39 <name>.24B <slot>.B <hair color>.W <hair style>.W <starting job class ID>.W <Unknown>.(W or 2 B's)??? <sex>.B case 0xa39: { - FIFOSD_CHECK(36); + FIFOSD_CHECK(36); #elif PACKETVER >= 20120307 // S 0970 <name>.24B <slot>.B <hair color>.W <hair style>.W case 0x970: @@ -5195,71 +5307,8 @@ int char_parse_char(int fd) return 0; } -int mapif_sendall(const unsigned char *buf, unsigned int len) -{ - int i, c; - - nullpo_ret(buf); - c = 0; - for(i = 0; i < ARRAYLENGTH(chr->server); i++) { - int fd; - if ((fd = chr->server[i].fd) > 0) { - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - c++; - } - } - - return c; -} - -int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) -{ - int i, c; - - nullpo_ret(buf); - c = 0; - for(i = 0; i < ARRAYLENGTH(chr->server); i++) { - int fd; - if ((fd = chr->server[i].fd) > 0 && fd != sfd) { - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - c++; - } - } - - return c; -} - -int mapif_send(int fd, unsigned char *buf, unsigned int len) -{ - nullpo_ret(buf); - if (fd >= 0) { - int i; - ARR_FIND( 0, ARRAYLENGTH(chr->server), i, fd == chr->server[i].fd ); - if( i < ARRAYLENGTH(chr->server) ) - { - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - return 1; - } - } - return 0; -} - -void mapif_send_users_count(int users) +static int char_broadcast_user_count(int tid, int64 tick, int id, intptr_t data) { - uint8 buf[6]; - // send number of players to all map-servers - WBUFW(buf,0) = 0x2b00; - WBUFL(buf,2) = users; - mapif->sendall(buf,6); -} - -int char_broadcast_user_count(int tid, int64 tick, int id, intptr_t data) { int users = chr->count_users(); // only send an update when needed @@ -5298,7 +5347,8 @@ static int char_send_accounts_tologin_sub(union DBKey key, struct DBData *data, return 0; } -int char_send_accounts_tologin(int tid, int64 tick, int id, intptr_t data) { +static int char_send_accounts_tologin(int tid, int64 tick, int id, intptr_t data) +{ if (chr->login_fd > 0 && sockt->session[chr->login_fd]) { // send account list to login server @@ -5315,7 +5365,8 @@ int char_send_accounts_tologin(int tid, int64 tick, int id, intptr_t data) { return 0; } -int char_check_connect_login_server(int tid, int64 tick, int id, intptr_t data) { +static int char_check_connect_login_server(int tid, int64 tick, int id, intptr_t data) +{ if (chr->login_fd > 0 && sockt->session[chr->login_fd] != NULL) return 0; @@ -5328,6 +5379,7 @@ int char_check_connect_login_server(int tid, int64 tick, int id, intptr_t data) sockt->session[chr->login_fd]->func_parse = chr->parse_fromlogin; sockt->session[chr->login_fd]->flag.server = 1; + sockt->session[chr->login_fd]->flag.validate = 0; sockt->realloc_fifo(chr->login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); loginif->connect_to_server(); @@ -5339,7 +5391,8 @@ int char_check_connect_login_server(int tid, int64 tick, int id, intptr_t data) //Invoked 15 seconds after mapif->disconnectplayer in case the map server doesn't //replies/disconnect the player we tried to kick. [Skotlex] //------------------------------------------------ -static int char_waiting_disconnect(int tid, int64 tick, int id, intptr_t data) { +static int char_waiting_disconnect(int tid, int64 tick, int id, intptr_t data) +{ struct online_char_data* character; if ((character = (struct online_char_data*)idb_get(chr->online_char_db, id)) != NULL && character->waiting_disconnect == tid) { //Mark it offline due to timeout. @@ -5366,7 +5419,8 @@ static int char_online_data_cleanup_sub(union DBKey key, struct DBData *data, va return 0; } -static int char_online_data_cleanup(int tid, int64 tick, int id, intptr_t data) { +static int char_online_data_cleanup(int tid, int64 tick, int id, intptr_t data) +{ chr->online_char_db->foreach(chr->online_char_db, chr->online_data_cleanup_sub); return 0; } @@ -5379,7 +5433,7 @@ static int char_online_data_cleanup(int tid, int64 tick, int id, intptr_t data) * * @retval false in case of error. */ -bool char_sql_config_read(const char *filename, bool imported) +static bool char_sql_config_read(const char *filename, bool imported) { struct config_t config; const struct config_setting_t *setting = NULL; @@ -5436,7 +5490,7 @@ bool char_sql_config_read(const char *filename, bool imported) * * @retval false in case of error. */ -bool char_sql_config_read_registry(const char *filename, const struct config_t *config, bool imported) +static bool char_sql_config_read_registry(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -5467,7 +5521,7 @@ bool char_sql_config_read_registry(const char *filename, const struct config_t * * * @retval false in case of error. */ -bool char_sql_config_read_pc(const char *filename, const struct config_t *config, bool imported) +static bool char_sql_config_read_pc(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -5483,6 +5537,7 @@ bool char_sql_config_read_pc(const char *filename, const struct config_t *config libconfig->setting_lookup_mutable_string(setting, "hotkey_db", hotkey_db, sizeof(hotkey_db)); libconfig->setting_lookup_mutable_string(setting, "scdata_db", scdata_db, sizeof(scdata_db)); libconfig->setting_lookup_mutable_string(setting, "inventory_db", inventory_db, sizeof(inventory_db)); + libconfig->setting_lookup_mutable_string(setting, "achievement_db", char_achievement_db, sizeof(char_achievement_db)); libconfig->setting_lookup_mutable_string(setting, "cart_db", cart_db, sizeof(cart_db)); libconfig->setting_lookup_mutable_string(setting, "charlog_db", charlog_db, sizeof(charlog_db)); libconfig->setting_lookup_mutable_string(setting, "storage_db", storage_db, sizeof(storage_db)); @@ -5513,7 +5568,7 @@ bool char_sql_config_read_pc(const char *filename, const struct config_t *config * * @retval false in case of error. */ -bool char_sql_config_read_guild(const char *filename, const struct config_t *config, bool imported) +static bool char_sql_config_read_guild(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -5547,7 +5602,7 @@ bool char_sql_config_read_guild(const char *filename, const struct config_t *con * * @retval false in case of error. */ -bool char_config_read(const char *filename, bool imported) +static bool char_config_read(const char *filename, bool imported) { struct config_t config; const char *import = NULL; @@ -5603,7 +5658,7 @@ bool char_config_read(const char *filename, bool imported) * * @retval false in case of error. */ -bool char_config_read_top(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_top(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -5647,7 +5702,7 @@ bool char_config_read_top(const char *filename, const struct config_t *config, b * * @retval false in case of error. */ -bool char_config_read_inter(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_inter(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; const char *str = NULL; @@ -5691,7 +5746,7 @@ bool char_config_read_inter(const char *filename, const struct config_t *config, * * @retval false in case of error. */ -bool char_config_read_database(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_database(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -5723,7 +5778,7 @@ bool char_config_read_database(const char *filename, const struct config_t *conf * * @retval false in case of error. */ -bool char_config_read_console(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_console(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting; @@ -5756,7 +5811,7 @@ bool char_config_read_console(const char *filename, const struct config_t *confi * * @retval false in case of error. */ -bool char_config_read_player(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_player(const char *filename, const struct config_t *config, bool imported) { bool retval = true; @@ -5784,7 +5839,7 @@ bool char_config_read_player(const char *filename, const struct config_t *config * * @retval false in case of error. */ -bool char_config_read_player_fame(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_player_fame(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -5828,7 +5883,7 @@ bool char_config_read_player_fame(const char *filename, const struct config_t *c * * @retval false in case of error. */ -bool char_config_read_player_deletion(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_player_deletion(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -5857,7 +5912,7 @@ bool char_config_read_player_deletion(const char *filename, const struct config_ * * @retval false in case of error. */ -bool char_config_read_player_name(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_player_name(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -5874,6 +5929,7 @@ bool char_config_read_player_name(const char *filename, const struct config_t *c libconfig->setting_lookup_mutable_string(setting, "name_letters", char_name_letters, sizeof(char_name_letters)); libconfig->setting_lookup_int(setting, "name_option", &char_name_option); libconfig->setting_lookup_bool_real(setting, "name_ignoring_case", &name_ignoring_case); + libconfig->setting_lookup_bool_real(setting, "use_aegis_rename", &char_aegis_rename); return true; } @@ -5883,7 +5939,7 @@ bool char_config_read_player_name(const char *filename, const struct config_t *c * * @param setting The already retrieved start_item setting. */ -void char_config_set_start_item(const struct config_setting_t *setting) +static void char_config_set_start_item(const struct config_setting_t *setting) { int i, count; @@ -5932,7 +5988,7 @@ void char_config_set_start_item(const struct config_setting_t *setting) * * @retval false in case of error. */ -bool char_config_read_player_new(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_player_new(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL, *setting2 = NULL; #ifdef RENEWAL @@ -5988,7 +6044,7 @@ bool char_config_read_player_new(const char *filename, const struct config_t *co * * @retval false in case of error. */ -bool char_config_read_permission(const char *filename, const struct config_t *config, bool imported) +static bool char_config_read_permission(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -6032,7 +6088,7 @@ bool char_config_read_permission(const char *filename, const struct config_t *co * * @retval false in case of error. */ -bool char_config_set_ip(const char *type, const char *value, uint32 *out_ip, char *out_ip_str) +static bool char_config_set_ip(const char *type, const char *value, uint32 *out_ip, char *out_ip_str) { uint32 ip = 0; @@ -6050,7 +6106,8 @@ bool char_config_set_ip(const char *type, const char *value, uint32 *out_ip, cha return true; } -int do_final(void) { +int do_final(void) +{ int i; ShowStatus("Terminating...\n"); @@ -6066,6 +6123,7 @@ int do_final(void) { do_final_mapif(); loginif->final(); + pincode->final(); if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s`", ragsrvinfo_db) ) Sql_ShowDebug(inter->sql_handle); @@ -6108,12 +6166,13 @@ void do_abort(void) { } -void set_server_type(void) { +void set_server_type(void) +{ SERVER_TYPE = SERVER_TYPE_CHAR; } /// Called when a terminate signal is received. -void do_shutdown(void) +static void do_shutdown(void) { if( core->runflag != CHARSERVER_ST_SHUTDOWN ) { @@ -6189,7 +6248,8 @@ void cmdline_args_init_local(void) CMDLINEARG_DEF2(net-config, netconfig, "Alternative network configuration.", CMDLINE_OPT_PARAM); } -int do_init(int argc, char **argv) { +int do_init(int argc, char **argv) +{ int i; memset(&skillid2idx, 0, sizeof(skillid2idx)); @@ -6212,6 +6272,7 @@ int do_init(int argc, char **argv) { //Read map indexes mapindex->init(); + pincode->init(); #ifdef RENEWAL start_point.map = mapindex->name2id("iz_int"); @@ -6311,6 +6372,7 @@ int do_init(int argc, char **argv) { Sql_ShowDebug(inter->sql_handle); sockt->set_defaultparse(chr->parse_char); + sockt->validate = true; if ((chr->char_fd = sockt->make_listen_bind(bind_ip,chr->port)) == -1) { ShowFatalError("Failed to bind to port '"CL_WHITE"%d"CL_RESET"'\n",chr->port); @@ -6343,6 +6405,7 @@ void char_load_defaults(void) loginif_defaults(); mapif_defaults(); inter_auction_defaults(); + inter_clan_defaults(); inter_elemental_defaults(); inter_guild_defaults(); inter_homunculus_defaults(); @@ -6352,6 +6415,8 @@ void char_load_defaults(void) inter_pet_defaults(); inter_quest_defaults(); inter_storage_defaults(); + inter_rodex_defaults(); + inter_achievement_defaults(); inter_defaults(); geoip_defaults(); } @@ -6395,18 +6460,20 @@ void char_defaults(void) chr->create_charstatus = char_create_charstatus; chr->mmo_char_tosql = char_mmo_char_tosql; chr->memitemdata_to_sql = char_memitemdata_to_sql; + chr->getitemdata_from_sql = char_getitemdata_from_sql; chr->mmo_gender = char_mmo_gender; chr->mmo_chars_fromsql = char_mmo_chars_fromsql; chr->mmo_char_fromsql = char_mmo_char_fromsql; chr->mmo_char_sql_init = char_mmo_char_sql_init; chr->char_slotchange = char_char_slotchange; chr->rename_char_sql = char_rename_char_sql; + chr->name_exists = char_name_exists; chr->check_char_name = char_check_char_name; chr->make_new_char_sql = char_make_new_char_sql; chr->divorce_char_sql = char_divorce_char_sql; chr->count_users = char_count_users; chr->mmo_char_tobuf = char_mmo_char_tobuf; - chr->mmo_char_send099d = char_mmo_char_send099d; + chr->send_HC_ACK_CHARINFO_PER_PAGE = char_send_HC_ACK_CHARINFO_PER_PAGE; chr->mmo_char_send_ban_list = char_mmo_char_send_ban_list; chr->mmo_char_send_slots_info = char_mmo_char_send_slots_info; chr->mmo_char_send_characters = char_mmo_char_send_characters; @@ -6479,7 +6546,6 @@ void char_defaults(void) chr->map_auth_failed = char_map_auth_failed; chr->parse_frommap_auth_request = char_parse_frommap_auth_request; chr->parse_frommap_update_ip = char_parse_frommap_update_ip; - chr->parse_frommap_request_stats_report = char_parse_frommap_request_stats_report; chr->parse_frommap_scdata_update = char_parse_frommap_scdata_update; chr->parse_frommap_scdata_delete = char_parse_frommap_scdata_delete; chr->parse_frommap = char_parse_frommap; diff --git a/src/char/char.h b/src/char/char.h index d7bc96e13..914530537 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -139,19 +139,21 @@ struct char_interface { void (*set_all_offline_sql) (void); struct DBData (*create_charstatus) (union DBKey key, va_list args); int (*mmo_char_tosql) (int char_id, struct mmo_charstatus* p); - int (*memitemdata_to_sql) (const struct item items[], int max, int id, int tableswitch); + int (*getitemdata_from_sql) (struct item *items, int max, int guid, enum inventory_table_type table); + int (*memitemdata_to_sql) (const struct item items[], int id, enum inventory_table_type table); int (*mmo_gender) (const struct char_session_data *sd, const struct mmo_charstatus *p, char sex); - int (*mmo_chars_fromsql) (struct char_session_data* sd, uint8* buf); + int (*mmo_chars_fromsql) (struct char_session_data* sd, uint8* buf, int *count); int (*mmo_char_fromsql) (int char_id, struct mmo_charstatus* p, bool load_everything); int (*mmo_char_sql_init) (void); bool (*char_slotchange) (struct char_session_data *sd, int fd, unsigned short from, unsigned short to); int (*rename_char_sql) (struct char_session_data *sd, int char_id); - int (*check_char_name) (char * name, char * esc_name); - int (*make_new_char_sql) (struct char_session_data *sd, const char *name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style, short starting_job); + bool (*name_exists) (const char *name, const char *esc_name); + int (*check_char_name) (const char *name, const char *esc_name); + int (*make_new_char_sql) (struct char_session_data *sd, const char *name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style, short starting_job, uint8 sex); int (*divorce_char_sql) (int partner_id1, int partner_id2); int (*count_users) (void); int (*mmo_char_tobuf) (uint8* buffer, struct mmo_charstatus* p); - void (*mmo_char_send099d) (int fd, struct char_session_data *sd); + void (*send_HC_ACK_CHARINFO_PER_PAGE) (int fd, struct char_session_data *sd); void (*mmo_char_send_ban_list) (int fd, struct char_session_data *sd); void (*mmo_char_send_slots_info) (int fd, struct char_session_data* sd); int (*mmo_char_send_characters) (int fd, struct char_session_data* sd); @@ -224,7 +226,6 @@ struct char_interface { void (*map_auth_failed) (int fd, int account_id, int char_id, int login_id1, char sex, uint32 ip); void (*parse_frommap_auth_request) (int fd, int id); void (*parse_frommap_update_ip) (int fd, int id); - void (*parse_frommap_request_stats_report) (int fd); void (*parse_frommap_scdata_update) (int fd); void (*parse_frommap_scdata_delete) (int fd); int (*parse_frommap) (int fd); @@ -240,7 +241,7 @@ struct char_interface { void (*delete2_cancel) (int fd, struct char_session_data* sd); void (*send_account_id) (int fd, int account_id); void (*parse_char_connect) (int fd, struct char_session_data* sd, uint32 ipl); - void (*send_map_info) (int fd, int i, uint32 subnet_map_ip, struct mmo_charstatus *cd); + void (*send_map_info) (int fd, int i, uint32 subnet_map_ip, struct mmo_charstatus *cd, char *dnsHost); void (*send_wait_char_server) (int fd); int (*search_default_maps_mapserver) (struct mmo_charstatus *cd); void (*parse_char_select) (int fd, struct char_session_data* sd, uint32 ipl); @@ -327,6 +328,8 @@ extern char pet_db[256]; extern char mail_db[256]; extern char auction_db[256]; extern char quest_db[256]; +extern char rodex_db[256]; +extern char rodex_item_db[256]; extern char homunculus_db[256]; extern char skill_homunculus_db[256]; extern char mercenary_db[256]; @@ -337,6 +340,7 @@ extern char acc_reg_num_db[32]; extern char acc_reg_str_db[32]; extern char char_reg_str_db[32]; extern char char_reg_num_db[32]; +extern char char_achievement_db[256]; extern int guild_exp_rate; diff --git a/src/char/geoip.c b/src/char/geoip.c index 0b84d2b09..2870e5f0c 100644 --- a/src/char/geoip.c +++ b/src/char/geoip.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -30,9 +30,9 @@ #include <stdio.h> #include <sys/stat.h> // for stat/lstat/fstat - [Dekamaster/Ultimate GM Tool] -struct s_geoip geoip_data; +static struct s_geoip geoip_data; -struct geoip_interface geoip_s; +static struct geoip_interface geoip_s; struct geoip_interface *geoip; /* [Dekamaster/Nightroad] */ @@ -40,36 +40,38 @@ struct geoip_interface *geoip; #define GEOIP_STRUCTURE_INFO_MAX_SIZE 20 #define GEOIP_COUNTRY_BEGIN 16776960 -const char * geoip_countryname[GEOIP_MAX_COUNTRIES] = {"Unknown","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles", - "Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados", - "Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia", - "Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the", - "Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica", - "Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic", - "Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji", - "Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana", - "Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala", - "Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia", - "Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan", - "Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait", - "Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania", - "Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali", - "Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives", - "Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua", - "Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia", - "Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau", - "Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan", - "Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname", - "Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand", - "Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan", - "Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela", - "Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa", - "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey", - "Saint Barthelemy", "Saint Martin", "Bonaire, Saint Eustatius and Saba", "South Sudan"}; +static const char *geoip_countryname[GEOIP_MAX_COUNTRIES] = { + "Unknown","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles", + "Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados", + "Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia", + "Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the", + "Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica", + "Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic", + "Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji", + "Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana", + "Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala", + "Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia", + "Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan", + "Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait", + "Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania", + "Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali", + "Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives", + "Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua", + "Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia", + "Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau", + "Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan", + "Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname", + "Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand", + "Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan", + "Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela", + "Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa", + "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey", + "Saint Barthelemy", "Saint Martin", "Bonaire, Saint Eustatius and Saba", "South Sudan" +}; /* [Dekamaster/Nightroad] */ /* WHY NOT A DBMAP: There are millions of entries in GeoIP and it has its own algorithm to go quickly through them, a DBMap wouldn't be efficient */ -const char* geoip_getcountry(uint32 ipnum) +static const char *geoip_getcountry(uint32 ipnum) { int depth; unsigned int x; @@ -111,7 +113,7 @@ const char* geoip_getcountry(uint32 ipnum) * Disables GeoIP * frees geoip.cache **/ -void geoip_final(bool shutdown) +static void geoip_final(bool shutdown) { if (geoip->data->cache) { aFree(geoip->data->cache); @@ -130,7 +132,7 @@ void geoip_final(bool shutdown) * geoip.cache should be freed after use! * http://dev.maxmind.com/geoip/legacy/geolite/ **/ -void geoip_init(void) +static void geoip_init(void) { int fno; char db_type = 1; diff --git a/src/char/geoip.h b/src/char/geoip.h index c5d71f5e1..be9fe4fff 100644 --- a/src/char/geoip.h +++ b/src/char/geoip.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify diff --git a/src/char/int_achievement.c b/src/char/int_achievement.c new file mode 100644 index 000000000..14311ecf0 --- /dev/null +++ b/src/char/int_achievement.c @@ -0,0 +1,252 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2017 Hercules Dev Team +* Copyright (C) Smokexyz +* +* Hercules is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#define HERCULES_CORE + +#include "int_achievement.h" + +#include "char/char.h" +#include "char/inter.h" +#include "char/mapif.h" + +#include "common/db.h" +#include "common/memmgr.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/sql.h" +#include "common/strlib.h" + +#include <stdio.h> +#include <stdlib.h> + +static struct inter_achievement_interface inter_achievement_s; +struct inter_achievement_interface *inter_achievement; + +/** + * Saves changed achievements for a character. + * @param[in] char_id character identifier. + * @param[out] cp pointer to loaded achievements. + * @param[in] p pointer to map-sent character achievements. + * @return number of achievements saved. + */ +static int inter_achievement_tosql(int char_id, struct char_achievements *cp, const struct char_achievements *p) +{ + StringBuf buf; + int i = 0, rows = 0; + + nullpo_ret(cp); + nullpo_ret(p); + Assert_ret(char_id > 0); + + StrBuf->Init(&buf); + StrBuf->Printf(&buf, "REPLACE INTO `%s` (`char_id`, `ach_id`, `completed_at`, `rewarded_at`", char_achievement_db); + for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) + StrBuf->Printf(&buf, ", `obj_%d`", i); + StrBuf->AppendStr(&buf, ") VALUES "); + + for (i = 0; i < VECTOR_LENGTH(*p); i++) { + int j = 0; + bool save = false; + struct achievement *pa = &VECTOR_INDEX(*p, i), *cpa = NULL; + + ARR_FIND(0, VECTOR_LENGTH(*cp), j, ((cpa = &VECTOR_INDEX(*cp, j)) && cpa->id == pa->id)); + + if (j == VECTOR_LENGTH(*cp)) + save = true; + else if (memcmp(cpa, pa, sizeof(struct achievement)) != 0) + save = true; + + if (save) { + StrBuf->Printf(&buf, "%s('%d', '%d', '%"PRId64"', '%"PRId64"'", rows ?", ":"", char_id, pa->id, (int64)pa->completed_at, (int64)pa->rewarded_at); + for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) + StrBuf->Printf(&buf, ", '%d'", pa->objective[j]); + StrBuf->AppendStr(&buf, ")"); + rows++; + } + } + + if (rows > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { + Sql_ShowDebug(inter->sql_handle); + StrBuf->Destroy(&buf); // Destroy the buffer. + return 0; + } + // Destroy the buffer. + StrBuf->Destroy(&buf); + + if (rows) { + ShowInfo("achievements saved for char %d (total: %d, saved: %d)\n", char_id, VECTOR_LENGTH(*p), rows); + + /* Sync with inter-db acheivements. */ + VECTOR_CLEAR(*cp); + VECTOR_ENSURE(*cp, VECTOR_LENGTH(*p), 1); + VECTOR_PUSHARRAY(*cp, VECTOR_DATA(*p), VECTOR_LENGTH(*p)); + } + + return rows; +} + +/** + * Retrieves all achievements of a character. + * @param[in] char_id character identifier. + * @param[out] cp pointer to character achievements structure. + * @return true on success, false on failure. + */ +static bool inter_achievement_fromsql(int char_id, struct char_achievements *cp) +{ + StringBuf buf; + char *data; + int i = 0, num_rows = 0; + + nullpo_ret(cp); + + Assert_ret(char_id > 0); + + // char_achievements (`char_id`, `ach_id`, `completed_at`, `rewarded_at`, `obj_0`, `obj_2`, ...`obj_9`) + StrBuf->Init(&buf); + StrBuf->AppendStr(&buf, "SELECT `ach_id`, `completed_at`, `rewarded_at`"); + for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) + StrBuf->Printf(&buf, ", `obj_%d`", i); + StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id` = '%d' ORDER BY `ach_id`", char_achievement_db, char_id); + + if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { + Sql_ShowDebug(inter->sql_handle); + StrBuf->Destroy(&buf); + return false; + } + + VECTOR_CLEAR(*cp); + + if ((num_rows = (int) SQL->NumRows(inter->sql_handle)) != 0) { + int j = 0; + + VECTOR_ENSURE(*cp, num_rows, 1); + + for (i = 0; i < num_rows && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); i++) { + struct achievement t_ach = { 0 }; + SQL->GetData(inter->sql_handle, 0, &data, NULL); t_ach.id = atoi(data); + SQL->GetData(inter->sql_handle, 1, &data, NULL); t_ach.completed_at = atoi(data); + SQL->GetData(inter->sql_handle, 2, &data, NULL); t_ach.rewarded_at = atoi(data); + /* Objectives */ + for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) { + SQL->GetData(inter->sql_handle, j + 3, &data, NULL); + t_ach.objective[j] = atoi(data); + } + /* Add Entry */ + VECTOR_PUSH(*cp, t_ach); + } + } + + SQL->FreeResult(inter->sql_handle); + + StrBuf->Destroy(&buf); + + if (num_rows > 0) + ShowInfo("achievements loaded for char %d (total: %d)\n", char_id, num_rows); + + return true; +} + +/** + * Handles checking of map server packets and calls appropriate functions. + * @param fd socket descriptor. + * @return 0 on failure, 1 on succes. + */ +static int inter_achievement_parse_frommap(int fd) +{ + RFIFOHEAD(fd); + + switch (RFIFOW(fd,0)) { + case 0x3012: + mapif->pLoadAchievements(fd); + break; + case 0x3013: + mapif->pSaveAchievements(fd); + break; + default: + return 0; + } + + return 1; +} + +/** + * Initialization function + */ +static int inter_achievement_sql_init(void) +{ + // Initialize the loaded db storage. + // used as a comparand against map-server achievement data before saving. + inter_achievement->char_achievements = idb_alloc(DB_OPT_RELEASE_DATA); + return 1; +} + +/** + * This function ensures idb's entry. + */ +static struct DBData inter_achievement_ensure_char_achievements(union DBKey key, va_list args) +{ + struct char_achievements *ca = NULL; + + CREATE(ca, struct char_achievements, 1); + VECTOR_INIT(*ca); + + return DB->ptr2data(ca); +} + +/** + * Cleaning function called through db_destroy() + */ +static int inter_achievement_char_achievements_clear(union DBKey key, struct DBData *data, va_list args) +{ + struct char_achievements *ca = DB->data2ptr(data); + + VECTOR_CLEAR(*ca); + + return 0; +} + +/** + * Finalization function. + */ +static void inter_achievement_sql_final(void) +{ + inter_achievement->char_achievements->destroy(inter_achievement->char_achievements, inter_achievement->char_achievements_clear); +} + +/** + * Inter-achievement interface. + */ +void inter_achievement_defaults(void) +{ + inter_achievement = &inter_achievement_s; + /* */ + inter_achievement->ensure_char_achievements = inter_achievement_ensure_char_achievements; + /* */ + inter_achievement->sql_init = inter_achievement_sql_init; + inter_achievement->sql_final = inter_achievement_sql_final; + /* */ + inter_achievement->tosql = inter_achievement_tosql; + inter_achievement->fromsql = inter_achievement_fromsql; + /* */ + inter_achievement->parse_frommap = inter_achievement_parse_frommap; + inter_achievement->char_achievements_clear = inter_achievement_char_achievements_clear; +} diff --git a/src/char/int_achievement.h b/src/char/int_achievement.h new file mode 100644 index 000000000..4a44a798d --- /dev/null +++ b/src/char/int_achievement.h @@ -0,0 +1,53 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2017 Hercules Dev Team +* Copyright (C) Smokexyz +* +* Hercules is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef CHAR_INT_ACHIEVEMENT_H +#define CHAR_INT_ACHIEVEMENT_H + +#include "common/hercules.h" +#include "common/db.h" + +struct achievement; +struct char_achievements; + +/** + * inter_achievement Interface + */ +struct inter_achievement_interface { + struct DBMap *char_achievements; + /* */ + int (*sql_init) (void); + void (*sql_final) (void); + /* */ + int (*tosql) (int char_id, struct char_achievements *cp, const struct char_achievements *p); + bool (*fromsql) (int char_id, struct char_achievements *a); + /* */ + struct DBData(*ensure_char_achievements) (union DBKey key, va_list args); + int (*char_achievements_clear) (union DBKey key, struct DBData *data, va_list args); + /* */ + int (*parse_frommap) (int fd); +}; + +#ifdef HERCULES_CORE +void inter_achievement_defaults(void); +#endif // HERCULES_CORE + +HPShared struct inter_achievement_interface *inter_achievement; +#endif /* CHAR_INT_ACHIEVEMENT_H */ diff --git a/src/char/int_auction.c b/src/char/int_auction.c index bf690327c..1e5b0a06d 100644 --- a/src/char/int_auction.c +++ b/src/char/int_auction.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -40,7 +40,7 @@ #include <stdio.h> #include <stdlib.h> -struct inter_auction_interface inter_auction_s; +static struct inter_auction_interface inter_auction_s; struct inter_auction_interface *inter_auction; static int inter_auction_count(int char_id, bool buy) @@ -59,7 +59,7 @@ static int inter_auction_count(int char_id, bool buy) return i; } -void inter_auction_save(struct auction_data *auction) +static void inter_auction_save(struct auction_data *auction) { int j; StringBuf buf; @@ -71,8 +71,10 @@ void inter_auction_save(struct auction_data *auction) StrBuf->Init(&buf); StrBuf->Printf(&buf, "UPDATE `%s` SET `seller_id` = '%d', `seller_name` = ?, `buyer_id` = '%d', `buyer_name` = ?, `price` = '%d', `buynow` = '%d', `hours` = '%d', `timestamp` = '%lu', `nameid` = '%d', `item_name` = ?, `type` = '%d', `refine` = '%d', `attribute` = '%d'", auction_db, auction->seller_id, auction->buyer_id, auction->price, auction->buynow, auction->hours, (unsigned long)auction->timestamp, auction->item.nameid, auction->type, auction->item.refine, auction->item.attribute); - for( j = 0; j < MAX_SLOTS; j++ ) + for (j = 0; j < MAX_SLOTS; j++) StrBuf->Printf(&buf, ", `card%d` = '%d'", j, auction->item.card[j]); + for (j = 0; j < MAX_ITEM_OPTIONS; j++) + StrBuf->Printf(&buf, ", `opt_idx%d` = '%d', `opt_val%d` = '%d'", j, auction->item.option[j].index, j, auction->item.option[j].value); StrBuf->Printf(&buf, " WHERE `auction_id` = '%u'", auction->auction_id); stmt = SQL->StmtMalloc(inter->sql_handle); @@ -89,39 +91,41 @@ void inter_auction_save(struct auction_data *auction) StrBuf->Destroy(&buf); } -unsigned int inter_auction_create(struct auction_data *auction) +static unsigned int inter_auction_create(struct auction_data *auction) { int j; StringBuf buf; struct SqlStmt *stmt; - if( !auction ) - return false; + nullpo_ret(auction); auction->timestamp = time(NULL) + (auction->hours * 3600); StrBuf->Init(&buf); StrBuf->Printf(&buf, "INSERT INTO `%s` (`seller_id`,`seller_name`,`buyer_id`,`buyer_name`,`price`,`buynow`,`hours`,`timestamp`,`nameid`,`item_name`,`type`,`refine`,`attribute`,`unique_id`", auction_db); - for( j = 0; j < MAX_SLOTS; j++ ) + for (j = 0; j < MAX_SLOTS; j++) StrBuf->Printf(&buf, ",`card%d`", j); + for (j = 0; j < MAX_ITEM_OPTIONS; j++) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j); StrBuf->Printf(&buf, ") VALUES ('%d',?,'%d',?,'%d','%d','%d','%lu','%d',?,'%d','%d','%d','%"PRIu64"'", auction->seller_id, auction->buyer_id, auction->price, auction->buynow, auction->hours, (unsigned long)auction->timestamp, auction->item.nameid, auction->type, auction->item.refine, auction->item.attribute, auction->item.unique_id); - for( j = 0; j < MAX_SLOTS; j++ ) + for (j = 0; j < MAX_SLOTS; j++) StrBuf->Printf(&buf, ",'%d'", auction->item.card[j]); + for (j = 0; j < MAX_ITEM_OPTIONS; j++) + StrBuf->Printf(&buf, ",'%d','%d'", auction->item.option[j].index, auction->item.option[j].value); + StrBuf->AppendStr(&buf, ")"); stmt = SQL->StmtMalloc(inter->sql_handle); - if( SQL_SUCCESS != SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) + if (SQL_SUCCESS != SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_STRING, auction->seller_name, strnlen(auction->seller_name, NAME_LENGTH)) || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_STRING, auction->buyer_name, strnlen(auction->buyer_name, NAME_LENGTH)) || SQL_SUCCESS != SQL->StmtBindParam(stmt, 2, SQLDT_STRING, auction->item_name, strnlen(auction->item_name, ITEM_NAME_LENGTH)) - || SQL_SUCCESS != SQL->StmtExecute(stmt) ) + || SQL_SUCCESS != SQL->StmtExecute(stmt)) { SqlStmt_ShowDebug(stmt); auction->auction_id = 0; - } - else - { + } else { struct auction_data *auction_; int64 tick = (int64)auction->hours * 3600000; @@ -144,17 +148,8 @@ unsigned int inter_auction_create(struct auction_data *auction) return auction->auction_id; } -void mapif_auction_message(int char_id, unsigned char result) +static int inter_auction_end_timer(int tid, int64 tick, int id, intptr_t data) { - unsigned char buf[74]; - - WBUFW(buf,0) = 0x3854; - WBUFL(buf,2) = char_id; - WBUFL(buf,6) = result; - mapif->sendall(buf,7); -} - -static int inter_auction_end_timer(int tid, int64 tick, int id, intptr_t data) { struct auction_data *auction; if( (auction = (struct auction_data *)idb_get(inter_auction->db, id)) != NULL ) { @@ -176,7 +171,7 @@ static int inter_auction_end_timer(int tid, int64 tick, int id, intptr_t data) { return 0; } -void inter_auction_delete(struct auction_data *auction) +static void inter_auction_delete(struct auction_data *auction) { unsigned int auction_id; nullpo_retv(auction); @@ -192,7 +187,7 @@ void inter_auction_delete(struct auction_data *auction) idb_remove(inter_auction->db, auction_id); } -void inter_auctions_fromsql(void) +static void inter_auctions_fromsql(void) { int i; struct auction_data *auction; @@ -204,8 +199,10 @@ void inter_auctions_fromsql(void) StrBuf->Init(&buf); StrBuf->AppendStr(&buf, "SELECT `auction_id`,`seller_id`,`seller_name`,`buyer_id`,`buyer_name`," "`price`,`buynow`,`hours`,`timestamp`,`nameid`,`item_name`,`type`,`refine`,`attribute`,`unique_id`"); - for( i = 0; i < MAX_SLOTS; i++ ) + for (i = 0; i < MAX_SLOTS; i++) StrBuf->Printf(&buf, ",`card%d`", i); + for (i = 0; i < MAX_ITEM_OPTIONS; i++) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", i, i); StrBuf->Printf(&buf, " FROM `%s` ORDER BY `auction_id` DESC", auction_db); if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) @@ -238,14 +235,20 @@ void inter_auctions_fromsql(void) item->identify = 1; item->amount = 1; item->expire_time = 0; - - for( i = 0; i < MAX_SLOTS; i++ ) - { + /* Card Slots */ + for (i = 0; i < MAX_SLOTS; i++) { SQL->GetData(inter->sql_handle, 15 + i, &data, NULL); item->card[i] = atoi(data); } + /* Item Options */ + for (i = 0; i < MAX_ITEM_OPTIONS; i++) { + SQL->GetData(inter->sql_handle, 15 + MAX_SLOTS + i * 2, &data, NULL); + item->option[i].index = atoi(data); + SQL->GetData(inter->sql_handle, 16 + MAX_SLOTS + i * 2, &data, NULL); + item->option[i].value = atoi(data); + } - if( auction->timestamp > now ) + if (auction->timestamp > now) endtick = ((int64)(auction->timestamp - now) * 1000) + tick; else endtick = tick + 10000; // 10 seconds to process ended auctions @@ -257,235 +260,10 @@ void inter_auctions_fromsql(void) SQL->FreeResult(inter->sql_handle); } -void mapif_auction_sendlist(int fd, int char_id, short count, short pages, unsigned char *buf) -{ - int len = (sizeof(struct auction_data) * count) + 12; - - nullpo_retv(buf); - - WFIFOHEAD(fd, len); - WFIFOW(fd,0) = 0x3850; - WFIFOW(fd,2) = len; - WFIFOL(fd,4) = char_id; - WFIFOW(fd,8) = count; - WFIFOW(fd,10) = pages; - memcpy(WFIFOP(fd,12), buf, len - 12); - WFIFOSET(fd,len); -} - -void mapif_parse_auction_requestlist(int fd) -{ - char searchtext[NAME_LENGTH]; - int char_id = RFIFOL(fd,4), len = sizeof(struct auction_data); - int price = RFIFOL(fd,10); - short type = RFIFOW(fd,8), page = max(1,RFIFOW(fd,14)); - unsigned char buf[5 * sizeof(struct auction_data)]; - struct DBIterator *iter = db_iterator(inter_auction->db); - struct auction_data *auction; - short i = 0, j = 0, pages = 1; - - memcpy(searchtext, RFIFOP(fd,16), NAME_LENGTH); - - for( auction = dbi_first(iter); dbi_exists(iter); auction = dbi_next(iter) ) - { - if( (type == 0 && auction->type != IT_ARMOR && auction->type != IT_PETARMOR) || - (type == 1 && auction->type != IT_WEAPON) || - (type == 2 && auction->type != IT_CARD) || - (type == 3 && auction->type != IT_ETC) || - (type == 4 && !strstr(auction->item_name, searchtext)) || - (type == 5 && auction->price > price) || - (type == 6 && auction->seller_id != char_id) || - (type == 7 && auction->buyer_id != char_id) ) - continue; - - i++; - if( i > 5 ) - { // Counting Pages of Total Results (5 Results per Page) - pages++; - i = 1; // First Result of This Page - } - - if( page != pages ) - continue; // This is not the requested Page - - memcpy(WBUFP(buf, j * len), auction, len); - j++; // Found Results - } - dbi_destroy(iter); - - mapif->auction_sendlist(fd, char_id, j, pages, buf); -} - -void mapif_auction_register(int fd, struct auction_data *auction) -{ - int len = sizeof(struct auction_data) + 4; - - nullpo_retv(auction); - - WFIFOHEAD(fd,len); - WFIFOW(fd,0) = 0x3851; - WFIFOW(fd,2) = len; - memcpy(WFIFOP(fd,4), auction, sizeof(struct auction_data)); - WFIFOSET(fd,len); -} - -void mapif_parse_auction_register(int fd) -{ - struct auction_data auction; - if( RFIFOW(fd,2) != sizeof(struct auction_data) + 4 ) - return; - - memcpy(&auction, RFIFOP(fd,4), sizeof(struct auction_data)); - if( inter_auction->count(auction.seller_id, false) < 5 ) - auction.auction_id = inter_auction->create(&auction); - - mapif->auction_register(fd, &auction); -} - -void mapif_auction_cancel(int fd, int char_id, unsigned char result) -{ - WFIFOHEAD(fd,7); - WFIFOW(fd,0) = 0x3852; - WFIFOL(fd,2) = char_id; - WFIFOB(fd,6) = result; - WFIFOSET(fd,7); -} - -void mapif_parse_auction_cancel(int fd) -{ - int char_id = RFIFOL(fd,2), auction_id = RFIFOL(fd,6); - struct auction_data *auction; - - if( (auction = (struct auction_data *)idb_get(inter_auction->db, auction_id)) == NULL ) - { - mapif->auction_cancel(fd, char_id, 1); // Bid Number is Incorrect - return; - } - - if( auction->seller_id != char_id ) - { - mapif->auction_cancel(fd, char_id, 2); // You cannot end the auction - return; - } - - if( auction->buyer_id > 0 ) - { - mapif->auction_cancel(fd, char_id, 3); // An auction with at least one bidder cannot be canceled - return; - } - - inter_mail->sendmail(0, "Auction Manager", auction->seller_id, auction->seller_name, "Auction", "Auction canceled.", 0, &auction->item); - inter_auction->delete_(auction); - - mapif->auction_cancel(fd, char_id, 0); // The auction has been canceled -} - -void mapif_auction_close(int fd, int char_id, unsigned char result) -{ - WFIFOHEAD(fd,7); - WFIFOW(fd,0) = 0x3853; - WFIFOL(fd,2) = char_id; - WFIFOB(fd,6) = result; - WFIFOSET(fd,7); -} - -void mapif_parse_auction_close(int fd) -{ - int char_id = RFIFOL(fd,2), auction_id = RFIFOL(fd,6); - struct auction_data *auction; - - if( (auction = (struct auction_data *)idb_get(inter_auction->db, auction_id)) == NULL ) - { - mapif->auction_close(fd, char_id, 2); // Bid Number is Incorrect - return; - } - - if( auction->seller_id != char_id ) - { - mapif->auction_close(fd, char_id, 1); // You cannot end the auction - return; - } - - if( auction->buyer_id == 0 ) - { - mapif->auction_close(fd, char_id, 1); // You cannot end the auction - return; - } - - // Send Money to Seller - inter_mail->sendmail(0, "Auction Manager", auction->seller_id, auction->seller_name, "Auction", "Auction closed.", auction->price, NULL); - // Send Item to Buyer - inter_mail->sendmail(0, "Auction Manager", auction->buyer_id, auction->buyer_name, "Auction", "Auction winner.", 0, &auction->item); - mapif->auction_message(auction->buyer_id, 6); // You have won the auction - inter_auction->delete_(auction); - - mapif->auction_close(fd, char_id, 0); // You have ended the auction -} - -void mapif_auction_bid(int fd, int char_id, int bid, unsigned char result) -{ - WFIFOHEAD(fd,11); - WFIFOW(fd,0) = 0x3855; - WFIFOL(fd,2) = char_id; - WFIFOL(fd,6) = bid; // To Return Zeny - WFIFOB(fd,10) = result; - WFIFOSET(fd,11); -} - -void mapif_parse_auction_bid(int fd) -{ - int char_id = RFIFOL(fd,4), bid = RFIFOL(fd,12); - unsigned int auction_id = RFIFOL(fd,8); - struct auction_data *auction; - - if( (auction = (struct auction_data *)idb_get(inter_auction->db, auction_id)) == NULL || auction->price >= bid || auction->seller_id == char_id ) - { - mapif->auction_bid(fd, char_id, bid, 0); // You have failed to bid in the auction - return; - } - - if( inter_auction->count(char_id, true) > 4 && bid < auction->buynow && auction->buyer_id != char_id ) - { - mapif->auction_bid(fd, char_id, bid, 9); // You cannot place more than 5 bids at a time - return; - } - - if( auction->buyer_id > 0 ) - { // Send Money back to the previous Buyer - if( auction->buyer_id != char_id ) - { - inter_mail->sendmail(0, "Auction Manager", auction->buyer_id, auction->buyer_name, "Auction", "Someone has placed a higher bid.", auction->price, NULL); - mapif->auction_message(auction->buyer_id, 7); // You have failed to win the auction - } - else - inter_mail->sendmail(0, "Auction Manager", auction->buyer_id, auction->buyer_name, "Auction", "You have placed a higher bid.", auction->price, NULL); - } - - auction->buyer_id = char_id; - safestrncpy(auction->buyer_name, RFIFOP(fd,16), NAME_LENGTH); - auction->price = bid; - - if( bid >= auction->buynow ) - { // Automatic won the auction - mapif->auction_bid(fd, char_id, bid - auction->buynow, 1); // You have successfully bid in the auction - - inter_mail->sendmail(0, "Auction Manager", auction->buyer_id, auction->buyer_name, "Auction", "You have won the auction.", 0, &auction->item); - mapif->auction_message(char_id, 6); // You have won the auction - inter_mail->sendmail(0, "Auction Manager", auction->seller_id, auction->seller_name, "Auction", "Payment for your auction!.", auction->buynow, NULL); - - inter_auction->delete_(auction); - return; - } - - inter_auction->save(auction); - - mapif->auction_bid(fd, char_id, 0, 1); // You have successfully bid in the auction -} - /*========================================== * Packets From Map Server *------------------------------------------*/ -int inter_auction_parse_frommap(int fd) +static int inter_auction_parse_frommap(int fd) { switch(RFIFOW(fd,0)) { @@ -500,7 +278,7 @@ int inter_auction_parse_frommap(int fd) return 1; } -int inter_auction_sql_init(void) +static int inter_auction_sql_init(void) { inter_auction->db = idb_alloc(DB_OPT_RELEASE_DATA); inter_auction->fromsql(); @@ -508,7 +286,7 @@ int inter_auction_sql_init(void) return 0; } -void inter_auction_sql_final(void) +static void inter_auction_sql_final(void) { inter_auction->db->destroy(inter_auction->db,NULL); diff --git a/src/char/int_auction.h b/src/char/int_auction.h index 9f0ccbd09..720efe22a 100644 --- a/src/char/int_auction.h +++ b/src/char/int_auction.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify diff --git a/src/char/int_clan.c b/src/char/int_clan.c new file mode 100644 index 000000000..9fb8ad95e --- /dev/null +++ b/src/char/int_clan.c @@ -0,0 +1,155 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2017 Hercules Dev Team + * + * Hercules is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#define HERCULES_CORE + +#include "config/core.h" // DBPATH +#include "int_clan.h" + +#include "char/char.h" +#include "char/inter.h" +#include "char/mapif.h" +#include "common/cbasetypes.h" +#include "common/memmgr.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/sql.h" +#include "common/strlib.h" +#include "common/timer.h" + +#include <stdio.h> +#include <stdlib.h> + +static struct inter_clan_interface inter_clan_s; +struct inter_clan_interface *inter_clan; + +/** + * Kick offline members of a clan + * + * Perform the update on the DB to reset clan id to 0 + * of the members that are inactive for too long + * + * @param clan_id Id of the clan + * @param kick_interval Time needed to consider a player inactive and kick it + * @return 0 on failure, 1 on success + */ +static int inter_clan_kick_inactive_members(int clan_id, int kick_interval) +{ + if (clan_id <= 0) { + ShowError("inter_clan_kick_inactive_members: Invalid clan id received '%d'\n", clan_id); + Assert_report(clan_id > 0); + return 0; + } else if (kick_interval <= 0) { + ShowError("inter_clan_kick_inactive_members: Invalid kick_interval received '%d'", kick_interval); + Assert_report(kick_interval > 0); + return 0; + } + + // Kick Inactive members + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET " + "`clan_id` = 0 WHERE `clan_id` = '%d' AND `online` = 0 AND `last_login` < %"PRId64, + char_db, clan_id, (int64)(time(NULL) - kick_interval))) + { + Sql_ShowDebug(inter->sql_handle); + return 0; + } + + return 1; +} + +/** + * Count members of a clan + * + * @param clan_id Id of the clan + * @param kick_interval Time needed to consider a player inactive and ignore it on the count + */ +static int inter_clan_count_members(int clan_id, int kick_interval) +{ + struct SqlStmt *stmt; + int count = 0; + + if (clan_id <= 0) { + ShowError("inter_clan_count_members: Invalid clan id received '%d'\n", clan_id); + Assert_report(clan_id > 0); + return 0; + } else if (kick_interval <= 0) { + ShowError("inter_clan_count_member: Invalid kick_interval received '%d'", kick_interval); + Assert_report(kick_interval > 0); + return 0; + } + + stmt = SQL->StmtMalloc(inter->sql_handle); + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return 0; + } + + // Count members + if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT COUNT(*) FROM `%s` WHERE `clan_id` = ? AND `last_login` >= %"PRId64, char_db, (int64)(time(NULL) - kick_interval)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &clan_id, sizeof(clan_id)) + || SQL_ERROR == SQL->StmtExecute(stmt) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &count, sizeof(count), NULL, NULL) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return 0; + } + + if (SQL->StmtNumRows(stmt) > 0 && SQL_SUCCESS != SQL->StmtNextRow(stmt)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return 0; + } + + SQL->StmtFree(stmt); + return count; +} + +// Communication from the map server +// - Can analyzed only one by one packet +// Data packet length that you set to inter.c +//- Shouldn't do checking and packet length, RFIFOSKIP is done by the caller +// Must Return +// 1 : ok +// 0 : error +static int inter_clan_parse_frommap(int fd) +{ + RFIFOHEAD(fd); + + switch(RFIFOW(fd, 0)) { + case 0x3044: mapif->parse_ClanMemberCount(fd, RFIFOL(fd, 2), RFIFOL(fd, 6)); break; + case 0x3045: mapif->parse_ClanMemberKick(fd, RFIFOL(fd, 2), RFIFOL(fd, 6)); break; + + default: + return 0; + } + + return 1; +} + +void inter_clan_defaults(void) +{ + inter_clan = &inter_clan_s; + + inter_clan->kick_inactive_members = inter_clan_kick_inactive_members; + inter_clan->count_members = inter_clan_count_members; + inter_clan->parse_frommap = inter_clan_parse_frommap; +} diff --git a/src/char/int_clan.h b/src/char/int_clan.h new file mode 100644 index 000000000..e7b44ecd3 --- /dev/null +++ b/src/char/int_clan.h @@ -0,0 +1,39 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2017 Hercules Dev Team + * + * Hercules is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef CHAR_INT_CLAN_H +#define CHAR_INT_CLAN_H + +#include "common/mmo.h" + +/** + * inter clan Interface + **/ +struct inter_clan_interface { + int (*kick_inactive_members) (int clan_id, int kick_interval); + int (*count_members) (int clan_id, int kick_interval); + int (*parse_frommap) (int fd); +}; + +#ifdef HERCULES_CORE +void inter_clan_defaults(void); +#endif // HERCULES_CORE + +HPShared struct inter_clan_interface *inter_clan; +#endif /* CHAR_INT_CLAN_H */ diff --git a/src/char/int_elemental.c b/src/char/int_elemental.c index aed25febd..175007b66 100644 --- a/src/char/int_elemental.c +++ b/src/char/int_elemental.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -37,7 +37,7 @@ #include <stdio.h> #include <stdlib.h> -struct inter_elemental_interface inter_elemental_s; +static struct inter_elemental_interface inter_elemental_s; struct inter_elemental_interface *inter_elemental; /** @@ -50,7 +50,7 @@ struct inter_elemental_interface *inter_elemental; * @param[in,out] ele The new elemental's data. * @retval false in case of errors. */ -bool mapif_elemental_create(struct s_elemental *ele) +static bool inter_elemental_create(struct s_elemental *ele) { nullpo_retr(false, ele); Assert_retr(false, ele->elemental_id == 0); @@ -73,7 +73,7 @@ bool mapif_elemental_create(struct s_elemental *ele) * @param ele The elemental's data. * @retval false in case of errors. */ -bool mapif_elemental_save(const struct s_elemental *ele) +static bool inter_elemental_save(const struct s_elemental *ele) { nullpo_retr(false, ele); Assert_retr(false, ele->elemental_id > 0); @@ -90,7 +90,8 @@ bool mapif_elemental_save(const struct s_elemental *ele) return true; } -bool mapif_elemental_load(int ele_id, int char_id, struct s_elemental *ele) { +static bool inter_elemental_load(int ele_id, int char_id, struct s_elemental *ele) +{ char* data; nullpo_retr(false, ele); @@ -134,7 +135,8 @@ bool mapif_elemental_load(int ele_id, int char_id, struct s_elemental *ele) { return true; } -bool mapif_elemental_delete(int ele_id) { +static bool inter_elemental_delete(int ele_id) +{ if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `ele_id` = '%d'", elemental_db, ele_id) ) { Sql_ShowDebug(inter->sql_handle); return false; @@ -143,72 +145,21 @@ bool mapif_elemental_delete(int ele_id) { return true; } -void mapif_elemental_send(int fd, struct s_elemental *ele, unsigned char flag) { - int size = sizeof(struct s_elemental) + 5; - - nullpo_retv(ele); - WFIFOHEAD(fd,size); - WFIFOW(fd,0) = 0x387c; - WFIFOW(fd,2) = size; - WFIFOB(fd,4) = flag; - memcpy(WFIFOP(fd,5),ele,sizeof(struct s_elemental)); - WFIFOSET(fd,size); -} - -void mapif_parse_elemental_create(int fd, const struct s_elemental *ele) +static void inter_elemental_sql_init(void) { - struct s_elemental ele_; - bool result; - - memcpy(&ele_, ele, sizeof(ele_)); - - result = mapif->elemental_create(&ele_); - mapif->elemental_send(fd, &ele_, result); -} - -void mapif_parse_elemental_load(int fd, int ele_id, int char_id) { - struct s_elemental ele; - bool result = mapif->elemental_load(ele_id, char_id, &ele); - mapif->elemental_send(fd, &ele, result); -} - -void mapif_elemental_deleted(int fd, unsigned char flag) { - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x387d; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,3); -} - -void mapif_parse_elemental_delete(int fd, int ele_id) { - bool result = mapif->elemental_delete(ele_id); - mapif->elemental_deleted(fd, result); -} - -void mapif_elemental_saved(int fd, unsigned char flag) { - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x387e; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,3); -} - -void mapif_parse_elemental_save(int fd, const struct s_elemental *ele) -{ - bool result = mapif->elemental_save(ele); - mapif->elemental_saved(fd, result); -} - -void inter_elemental_sql_init(void) { return; } -void inter_elemental_sql_final(void) { +static void inter_elemental_sql_final(void) +{ return; } /*========================================== * Inter Packets *------------------------------------------*/ -int inter_elemental_parse_frommap(int fd) { +static int inter_elemental_parse_frommap(int fd) +{ unsigned short cmd = RFIFOW(fd,0); switch (cmd) { @@ -229,4 +180,9 @@ void inter_elemental_defaults(void) inter_elemental->sql_init = inter_elemental_sql_init; inter_elemental->sql_final = inter_elemental_sql_final; inter_elemental->parse_frommap = inter_elemental_parse_frommap; + + inter_elemental->create = inter_elemental_create; + inter_elemental->save = inter_elemental_save; + inter_elemental->load = inter_elemental_load; + inter_elemental->delete = inter_elemental_delete; } diff --git a/src/char/int_elemental.h b/src/char/int_elemental.h index 8a046b0f8..3172dcacf 100644 --- a/src/char/int_elemental.h +++ b/src/char/int_elemental.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ #define CHAR_INT_ELEMENTAL_H #include "common/hercules.h" +#include "common/mmo.h" /** * inter_elemental_interface interface @@ -30,6 +31,11 @@ struct inter_elemental_interface { void (*sql_init) (void); void (*sql_final) (void); int (*parse_frommap) (int fd); + + bool (*create) (struct s_elemental *ele); + bool (*save) (const struct s_elemental *ele); + bool (*load) (int ele_id, int char_id, struct s_elemental *ele); + bool (*delete) (int ele_id); }; #ifdef HERCULES_CORE diff --git a/src/char/int_guild.c b/src/char/int_guild.c index b4b4bdde4..56e1c1ba3 100644 --- a/src/char/int_guild.c +++ b/src/char/int_guild.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -51,12 +51,13 @@ #define GUILD_ALLIANCE_TYPE_MASK 0x01 #define GUILD_ALLIANCE_REMOVE 0x08 -struct inter_guild_interface inter_guild_s; +static struct inter_guild_interface inter_guild_s; struct inter_guild_interface *inter_guild; static const char dataToHex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; -int inter_guild_save_timer(int tid, int64 tick, int id, intptr_t data) { +static int inter_guild_save_timer(int tid, int64 tick, int id, intptr_t data) +{ static int last_id = 0; //To know in which guild we were. int state = 0; //0: Have not reached last guild. 1: Reached last guild, ready for save. 2: Some guild saved, don't do further saving. struct DBIterator *iter = db_iterator(inter_guild->guild_db); @@ -101,7 +102,7 @@ int inter_guild_save_timer(int tid, int64 tick, int id, intptr_t data) { return 0; } -int inter_guild_removemember_tosql(int account_id, int char_id) +static int inter_guild_removemember_tosql(int account_id, int char_id) { if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE from `%s` where `account_id` = '%d' and `char_id` = '%d'", guild_member_db, account_id, char_id) ) Sql_ShowDebug(inter->sql_handle); @@ -111,7 +112,7 @@ int inter_guild_removemember_tosql(int account_id, int char_id) } // Save guild into sql -int inter_guild_tosql(struct guild *g,int flag) +static int inter_guild_tosql(struct guild *g, int flag) { // Table guild (GS_BASIC_MASK) // GS_EMBLEM `emblem_len`,`emblem_id`,`emblem_data` @@ -138,7 +139,7 @@ int inter_guild_tosql(struct guild *g,int flag) if (g->guild_id<=0 && g->guild_id != -1) return 0; #ifdef NOISY - ShowInfo("Save guild request ("CL_BOLD"%d"CL_RESET" - flag 0x%x).",g->guild_id, flag); + ShowInfo("Save guild request ("CL_BOLD"%d"CL_RESET" - flag 0x%x).\n",g->guild_id, flag); #endif SQL->EscapeStringLen(inter->sql_handle, esc_name, g->name, strnlen(g->name, NAME_LENGTH)); @@ -343,7 +344,7 @@ int inter_guild_tosql(struct guild *g,int flag) } // Read guild from sql -struct guild * inter_guild_fromsql(int guild_id) +static struct guild *inter_guild_fromsql(int guild_id) { struct guild *g; char* data; @@ -417,8 +418,8 @@ struct guild * inter_guild_fromsql(int guild_id) } // load guild member info - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`name` " - "FROM `%s` WHERE `guild_id`='%d' ORDER BY `position`", guild_member_db, guild_id) ) + if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT g.`account_id`,g.`char_id`,g.`hair`,g.`hair_color`,g.`gender`,g.`class`,g.`lv`,g.`exp`,g.`exp_payper`,g.`online`,g.`position`,g.`name`,c.`last_login`" + "FROM `%s` g LEFT JOIN `%s` c ON c.`char_id` = g.`char_id` WHERE g.`guild_id`='%d' ORDER BY `position`", guild_member_db, char_db, guild_id) ) { Sql_ShowDebug(inter->sql_handle); aFree(g); @@ -442,6 +443,9 @@ struct guild * inter_guild_fromsql(int guild_id) if( m->position >= MAX_GUILDPOSITION ) // Fix reduction of MAX_GUILDPOSITION [PoW] m->position = MAX_GUILDPOSITION - 1; SQL->GetData(inter->sql_handle, 11, &data, &len); memcpy(m->name, data, min(len, NAME_LENGTH)); + SQL->GetData(inter->sql_handle, 12, &data, NULL); + if (data != NULL) + m->last_login = atoi(data); m->modified = GS_MEMBER_UNMODIFIED; } @@ -532,7 +536,7 @@ struct guild * inter_guild_fromsql(int guild_id) } // `guild_castle` (`castle_id`, `guild_id`, `economy`, `defense`, `triggerE`, `triggerD`, `nextTime`, `payTime`, `createTime`, `visibleC`, `visibleG0`, `visibleG1`, `visibleG2`, `visibleG3`, `visibleG4`, `visibleG5`, `visibleG6`, `visibleG7`) -int inter_guild_castle_tosql(struct guild_castle *gc) +static int inter_guild_castle_tosql(struct guild_castle *gc) { StringBuf buf; int i; @@ -556,7 +560,7 @@ int inter_guild_castle_tosql(struct guild_castle *gc) } // Read guild_castle from SQL -struct guild_castle* inter_guild_castle_fromsql(int castle_id) +static struct guild_castle *inter_guild_castle_fromsql(int castle_id) { char *data; int i; @@ -608,7 +612,8 @@ struct guild_castle* inter_guild_castle_fromsql(int castle_id) // Read exp_guild.txt -bool inter_guild_exp_parse_row(char* split[], int column, int current) { +static bool inter_guild_exp_parse_row(char *split[], int column, int current) +{ int64 exp = strtoll(split[0], NULL, 10); nullpo_retr(true, split); @@ -622,7 +627,8 @@ bool inter_guild_exp_parse_row(char* split[], int column, int current) { } -int inter_guild_CharOnline(int char_id, int guild_id) { +static int inter_guild_CharOnline(int char_id, int guild_id) +{ struct guild *g; int i; @@ -671,7 +677,7 @@ int inter_guild_CharOnline(int char_id, int guild_id) { return 1; } -int inter_guild_CharOffline(int char_id, int guild_id) +static int inter_guild_CharOffline(int char_id, int guild_id) { struct guild *g=NULL; int online_count, i; @@ -727,7 +733,7 @@ int inter_guild_CharOffline(int char_id, int guild_id) } // Initialize guild sql -int inter_guild_sql_init(void) +static int inter_guild_sql_init(void) { //Initialize the guild cache inter_guild->guild_db= idb_alloc(DB_OPT_RELEASE_DATA); @@ -744,7 +750,7 @@ int inter_guild_sql_init(void) /** * @see DBApply */ -int inter_guild_db_final(union DBKey key, struct DBData *data, va_list ap) +static int inter_guild_db_final(union DBKey key, struct DBData *data, va_list ap) { struct guild *g = DB->data2ptr(data); nullpo_ret(g); @@ -755,7 +761,7 @@ int inter_guild_db_final(union DBKey key, struct DBData *data, va_list ap) return 0; } -void inter_guild_sql_final(void) +static void inter_guild_sql_final(void) { inter_guild->guild_db->destroy(inter_guild->guild_db, inter_guild->db_final); db_destroy(inter_guild->castle_db); @@ -763,7 +769,7 @@ void inter_guild_sql_final(void) } // Get guild_id by its name. Returns 0 if not found, -1 on error. -int inter_guild_search_guildname(const char *str) +static int inter_guild_search_guildname(const char *str) { int guild_id; char esc_name[NAME_LENGTH*2+1]; @@ -806,7 +812,8 @@ static bool inter_guild_check_empty(struct guild *g) return true; } -unsigned int inter_guild_nextexp(int level) { +static unsigned int inter_guild_nextexp(int level) +{ if (level == 0) return 1; if (level <= 0 || level > MAX_GUILDLEVEL) @@ -815,7 +822,7 @@ unsigned int inter_guild_nextexp(int level) { return inter_guild->exp[level-1]; } -int inter_guild_checkskill(struct guild *g, int id) +static int inter_guild_checkskill(struct guild *g, int id) { int idx = id - GD_SKILLBASE; @@ -826,7 +833,7 @@ int inter_guild_checkskill(struct guild *g, int id) return g->skill[idx].lv; } -int inter_guild_calcinfo(struct guild *g) +static int inter_guild_calcinfo(struct guild *g) { int i,c; unsigned int nextexp; @@ -890,276 +897,7 @@ int inter_guild_calcinfo(struct guild *g) return 0; } -//------------------------------------------------------------------- -// Packet sent to map server - -int mapif_guild_created(int fd, int account_id, struct guild *g) -{ - WFIFOHEAD(fd, 10); - WFIFOW(fd,0)=0x3830; - WFIFOL(fd,2)=account_id; - if(g != NULL) - { - WFIFOL(fd,6)=g->guild_id; - ShowInfo("int_guild: Guild created (%d - %s)\n",g->guild_id,g->name); - } else - WFIFOL(fd,6)=0; - - WFIFOSET(fd,10); - return 0; -} - -// Guild not found -int mapif_guild_noinfo(int fd, int guild_id) -{ - unsigned char buf[12]; - WBUFW(buf,0)=0x3831; - WBUFW(buf,2)=8; - WBUFL(buf,4)=guild_id; - ShowWarning("int_guild: info not found %d\n",guild_id); - if(fd<0) - mapif->sendall(buf,8); - else - mapif->send(fd,buf,8); - return 0; -} - -// Send guild info -int mapif_guild_info(int fd, struct guild *g) -{ - unsigned char buf[8+sizeof(struct guild)]; - nullpo_ret(g); - WBUFW(buf,0)=0x3831; - WBUFW(buf,2)=4+sizeof(struct guild); - memcpy(buf+4,g,sizeof(struct guild)); - if(fd<0) - mapif->sendall(buf,WBUFW(buf,2)); - else - mapif->send(fd,buf,WBUFW(buf,2)); - return 0; -} - -// ACK member add -int mapif_guild_memberadded(int fd, int guild_id, int account_id, int char_id, int flag) -{ - WFIFOHEAD(fd, 15); - WFIFOW(fd,0)=0x3832; - WFIFOL(fd,2)=guild_id; - WFIFOL(fd,6)=account_id; - WFIFOL(fd,10)=char_id; - WFIFOB(fd,14)=flag; - WFIFOSET(fd,15); - return 0; -} - -// ACK member leave -int mapif_guild_withdraw(int guild_id,int account_id,int char_id,int flag, const char *name, const char *mes) -{ - unsigned char buf[55+NAME_LENGTH]; - - nullpo_ret(name); - nullpo_ret(mes); - - WBUFW(buf, 0)=0x3834; - WBUFL(buf, 2)=guild_id; - WBUFL(buf, 6)=account_id; - WBUFL(buf,10)=char_id; - WBUFB(buf,14)=flag; - memcpy(WBUFP(buf,15),mes,40); - memcpy(WBUFP(buf,55),name,NAME_LENGTH); - mapif->sendall(buf,55+NAME_LENGTH); - ShowInfo("int_guild: guild withdraw (%d - %d: %s - %s)\n",guild_id,account_id,name,mes); - return 0; -} - -// Send short member's info -int mapif_guild_memberinfoshort(struct guild *g, int idx) -{ - unsigned char buf[19]; - nullpo_ret(g); - Assert_ret(idx >= 0 && idx < MAX_GUILD); - WBUFW(buf, 0)=0x3835; - WBUFL(buf, 2)=g->guild_id; - WBUFL(buf, 6)=g->member[idx].account_id; - WBUFL(buf,10)=g->member[idx].char_id; - WBUFB(buf,14)=(unsigned char)g->member[idx].online; - WBUFW(buf,15)=g->member[idx].lv; - WBUFW(buf,17)=g->member[idx].class; - mapif->sendall(buf,19); - return 0; -} - -// Send guild broken -int mapif_guild_broken(int guild_id, int flag) -{ - unsigned char buf[7]; - WBUFW(buf,0)=0x3836; - WBUFL(buf,2)=guild_id; - WBUFB(buf,6)=flag; - mapif->sendall(buf,7); - ShowInfo("int_guild: Guild broken (%d)\n",guild_id); - return 0; -} - -// Send guild message -int mapif_guild_message(int guild_id, int account_id, const char *mes, int len, int sfd) -{ - unsigned char buf[512]; - nullpo_ret(mes); - if (len > 500) - len = 500; - WBUFW(buf,0)=0x3837; - WBUFW(buf,2)=len+12; - WBUFL(buf,4)=guild_id; - WBUFL(buf,8)=account_id; - memcpy(WBUFP(buf,12),mes,len); - mapif->sendallwos(sfd, buf,len+12); - return 0; -} - -// Send basic info -int mapif_guild_basicinfochanged(int guild_id, int type, const void *data, int len) -{ - unsigned char buf[2048]; - nullpo_ret(data); - if (len > 2038) - len = 2038; - WBUFW(buf, 0)=0x3839; - WBUFW(buf, 2)=len+10; - WBUFL(buf, 4)=guild_id; - WBUFW(buf, 8)=type; - memcpy(WBUFP(buf,10),data,len); - mapif->sendall(buf,len+10); - return 0; -} - -// Send member info -int mapif_guild_memberinfochanged(int guild_id, int account_id, int char_id, int type, const void *data, int len) -{ - unsigned char buf[2048]; - nullpo_ret(data); - if (len > 2030) - len = 2030; - WBUFW(buf, 0)=0x383a; - WBUFW(buf, 2)=len+18; - WBUFL(buf, 4)=guild_id; - WBUFL(buf, 8)=account_id; - WBUFL(buf,12)=char_id; - WBUFW(buf,16)=type; - memcpy(WBUFP(buf,18),data,len); - mapif->sendall(buf,len+18); - return 0; -} - -// ACK guild skill up -int mapif_guild_skillupack(int guild_id, uint16 skill_id, int account_id) -{ - unsigned char buf[14]; - WBUFW(buf, 0)=0x383c; - WBUFL(buf, 2)=guild_id; - WBUFL(buf, 6)=skill_id; - WBUFL(buf,10)=account_id; - mapif->sendall(buf,14); - return 0; -} - -// ACK guild alliance -int mapif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag, const char *name1, const char *name2) -{ - unsigned char buf[19+2*NAME_LENGTH]; - nullpo_ret(name1); - nullpo_ret(name2); - WBUFW(buf, 0)=0x383d; - WBUFL(buf, 2)=guild_id1; - WBUFL(buf, 6)=guild_id2; - WBUFL(buf,10)=account_id1; - WBUFL(buf,14)=account_id2; - WBUFB(buf,18)=flag; - memcpy(WBUFP(buf,19),name1,NAME_LENGTH); - memcpy(WBUFP(buf,19+NAME_LENGTH),name2,NAME_LENGTH); - mapif->sendall(buf,19+2*NAME_LENGTH); - return 0; -} - -// Send a guild position desc -int mapif_guild_position(struct guild *g, int idx) -{ - unsigned char buf[12 + sizeof(struct guild_position)]; - nullpo_ret(g); - Assert_ret(idx >= 0 && idx < MAX_GUILDPOSITION); - WBUFW(buf,0)=0x383b; - WBUFW(buf,2)=sizeof(struct guild_position)+12; - WBUFL(buf,4)=g->guild_id; - WBUFL(buf,8)=idx; - memcpy(WBUFP(buf,12),&g->position[idx],sizeof(struct guild_position)); - mapif->sendall(buf,WBUFW(buf,2)); - return 0; -} - -// Send the guild notice -int mapif_guild_notice(struct guild *g) -{ - unsigned char buf[256]; - nullpo_ret(g); - WBUFW(buf,0)=0x383e; - WBUFL(buf,2)=g->guild_id; - memcpy(WBUFP(buf,6),g->mes1,MAX_GUILDMES1); - memcpy(WBUFP(buf,66),g->mes2,MAX_GUILDMES2); - mapif->sendall(buf,186); - return 0; -} - -// Send emblem data -int mapif_guild_emblem(struct guild *g) -{ - unsigned char buf[12 + sizeof(g->emblem_data)]; - nullpo_ret(g); - WBUFW(buf,0)=0x383f; - WBUFW(buf,2)=g->emblem_len+12; - WBUFL(buf,4)=g->guild_id; - WBUFL(buf,8)=g->emblem_id; - memcpy(WBUFP(buf,12),g->emblem_data,g->emblem_len); - mapif->sendall(buf,WBUFW(buf,2)); - return 0; -} - -int mapif_guild_master_changed(struct guild *g, int aid, int cid) -{ - unsigned char buf[14]; - nullpo_ret(g); - WBUFW(buf,0)=0x3843; - WBUFL(buf,2)=g->guild_id; - WBUFL(buf,6)=aid; - WBUFL(buf,10)=cid; - mapif->sendall(buf,14); - return 0; -} - -int mapif_guild_castle_dataload(int fd, int sz, const int *castle_ids) -{ - struct guild_castle *gc = NULL; - int num = (sz - 4) / sizeof(int); - int len = 4 + num * sizeof(*gc); - int i; - - nullpo_ret(castle_ids); - WFIFOHEAD(fd, len); - WFIFOW(fd, 0) = 0x3840; - WFIFOW(fd, 2) = len; - for (i = 0; i < num; i++) { - gc = inter_guild->castle_fromsql(*(castle_ids++)); - memcpy(WFIFOP(fd, 4 + i * sizeof(*gc)), gc, sizeof(*gc)); - } - WFIFOSET(fd, len); - return 0; -} - -//------------------------------------------------------------------- -// Packet received from map server - - -// Guild creation request -int mapif_parse_CreateGuild(int fd, int account_id, const char *name, const struct guild_member *master) +static struct guild *inter_guild_create(const char *name, const struct guild_member *master) { struct guild *g; int i=0; @@ -1170,21 +908,18 @@ int mapif_parse_CreateGuild(int fd, int account_id, const char *name, const stru nullpo_ret(master); if(inter_guild->search_guildname(name) != 0){ ShowInfo("int_guild: guild with same name exists [%s]\n",name); - mapif->guild_created(fd,account_id,NULL); - return 0; + return NULL; } // Check Authorized letters/symbols in the name of the character if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorized for (i = 0; i < NAME_LENGTH && name[i]; i++) if (strchr(char_name_letters, name[i]) == NULL) { - mapif->guild_created(fd,account_id,NULL); - return 0; + return NULL; } } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden for (i = 0; i < NAME_LENGTH && name[i]; i++) if (strchr(char_name_letters, name[i]) != NULL) { - mapif->guild_created(fd,account_id,NULL); - return 0; + return NULL; } } @@ -1220,108 +955,84 @@ int mapif_parse_CreateGuild(int fd, int account_id, const char *name, const stru if (!inter_guild->tosql(g,GS_BASIC|GS_POSITION|GS_SKILL|GS_MEMBER)) { //Failed to Create guild.... ShowError("Failed to create Guild %s (Guild Master: %s)\n", g->name, g->master); - mapif->guild_created(fd,account_id,NULL); aFree(g); - return 0; + return NULL; } ShowInfo("Created Guild %d - %s (Guild Master: %s)\n", g->guild_id, g->name, g->master); //Add to cache idb_put(inter_guild->guild_db, g->guild_id, g); - // Report to client - mapif->guild_created(fd,account_id,g); - mapif->guild_info(fd,g); - if (inter->enable_logs) inter->log("guild %s (id=%d) created by master %s (id=%d)\n", name, g->guild_id, master->name, master->account_id); - return 0; -} - -// Return guild info to client -int mapif_parse_GuildInfo(int fd, int guild_id) -{ - struct guild * g = inter_guild->fromsql(guild_id); //We use this because on start-up the info of castle-owned guilds is required. [Skotlex] - if(g) - { - if (!inter_guild->calcinfo(g)) - mapif->guild_info(fd,g); - } - else - mapif->guild_noinfo(fd,guild_id); // Failed to load info - return 0; + return g; } // Add member to guild -int mapif_parse_GuildAddMember(int fd, int guild_id, const struct guild_member *m) +static bool inter_guild_add_member(int guild_id, const struct guild_member *member, int map_fd) { struct guild * g; int i; + nullpo_ret(member); - nullpo_ret(m); g = inter_guild->fromsql(guild_id); - if(g==NULL){ - mapif->guild_memberadded(fd,guild_id,m->account_id,m->char_id,1); // 1: Failed to add - return 0; + if (g == NULL) { + mapif->guild_memberadded(map_fd, guild_id, member->account_id, member->char_id, 1); // 1: Failed to add + return false; } // Find an empty slot - for(i=0;i<g->max_member;i++) - { - if(g->member[i].account_id==0) - { - memcpy(&g->member[i],m,sizeof(struct guild_member)); + for (i = 0; i < g->max_member; i++) { + if (g->member[i].account_id == 0) { + g->member[i] = *member; g->member[i].modified = (GS_MEMBER_NEW | GS_MEMBER_MODIFIED); - mapif->guild_memberadded(fd,guild_id,m->account_id,m->char_id,0); // 0: success + mapif->guild_memberadded(map_fd, guild_id, member->account_id, member->char_id, 0); // 0: success if (!inter_guild->calcinfo(g)) //Send members if it was not invoked. - mapif->guild_info(-1,g); + mapif->guild_info(-1, g); g->save_flag |= GS_MEMBER; if (g->save_flag&GS_REMOVE) g->save_flag&=~GS_REMOVE; - return 0; + return true; } } - mapif->guild_memberadded(fd,guild_id,m->account_id,m->char_id,1); // 1: Failed to add - return 0; + mapif->guild_memberadded(map_fd, guild_id, member->account_id, member->char_id, 1); // 1: Failed to add + return false; } // Delete member from guild -int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, int flag, const char *mes) +static bool inter_guild_leave(int guild_id, int account_id, int char_id, int flag, const char *mes, int map_fd) { int i; - struct guild* g = inter_guild->fromsql(guild_id); - if( g == NULL ) - { + struct guild *g = inter_guild->fromsql(guild_id); + if (g == NULL) { // Unknown guild, just update the player - if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `guild_id`='0' WHERE `account_id`='%d' AND `char_id`='%d'", char_db, account_id, char_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `guild_id`='0' WHERE `account_id`='%d' AND `char_id`='%d'", char_db, account_id, char_id)) Sql_ShowDebug(inter->sql_handle); // mapif->guild_withdraw(guild_id,account_id,char_id,flag,g->member[i].name,mes); - return 0; + return false; } nullpo_ret(mes); // Find the member - ARR_FIND( 0, g->max_member, i, g->member[i].account_id == account_id && g->member[i].char_id == char_id ); - if( i == g->max_member ) - { + ARR_FIND(0, g->max_member, i, g->member[i].account_id == account_id && g->member[i].char_id == char_id); + if (i == g->max_member) { //TODO - return 0; + return false; } if (flag) { // Write expulsion reason // Find an empty slot int j; - ARR_FIND( 0, MAX_GUILDEXPULSION, j, g->expulsion[j].account_id == 0 ); - if( j == MAX_GUILDEXPULSION ) - { + ARR_FIND(0, MAX_GUILDEXPULSION, j, g->expulsion[j].account_id == 0); + if (j == MAX_GUILDEXPULSION) { // Expulsion list is full, flush the oldest one - for( j = 0; j < MAX_GUILDEXPULSION - 1; j++ ) + for (j = 0; j < MAX_GUILDEXPULSION - 1; j++) g->expulsion[j] = g->expulsion[j+1]; j = MAX_GUILDEXPULSION-1; } @@ -1336,38 +1047,38 @@ int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, in memset(&g->member[i],0,sizeof(struct guild_member)); - if( inter_guild->check_empty(g) ) - mapif->parse_BreakGuild(-1,guild_id); //Break the guild. - else { + if (inter_guild->check_empty(g)) { + inter_guild->disband(guild_id); + } else { //Update member info. if (!inter_guild->calcinfo(g)) - mapif->guild_info(fd,g); + mapif->guild_info(map_fd,g); g->save_flag |= GS_EXPULSION; } - return 0; + return true; } // Change member info -int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, int char_id, int online, int lv, int16 class) +static bool inter_guild_update_member_info_short(int guild_id, int account_id, int char_id, int online, int lv, int16 class) { // Could speed up by manipulating only guild_member - struct guild * g; + struct guild *g; int i,sum,c; int prev_count, prev_alv; g = inter_guild->fromsql(guild_id); - if(g==NULL) - return 0; + if (g == NULL) + return false; - ARR_FIND( 0, g->max_member, i, g->member[i].account_id == account_id && g->member[i].char_id == char_id ); - if( i < g->max_member ) - { + ARR_FIND(0, g->max_member, i, g->member[i].account_id == account_id && g->member[i].char_id == char_id); + if (i < g->max_member) { g->member[i].online = online; g->member[i].lv = lv; g->member[i].class = class; + g->member[i].last_login = (uint32)time(NULL); g->member[i].modified = GS_MEMBER_MODIFIED; - mapif->guild_memberinfoshort(g,i); + mapif->guild_memberinfoshort(g, i); } prev_count = g->connect_member; @@ -1378,99 +1089,90 @@ int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, c = 0; sum = 0; - for( i = 0; i < g->max_member; i++ ) - { - if( g->member[i].account_id > 0 ) - { + for (i = 0; i < g->max_member; i++) { + if (g->member[i].account_id > 0) { sum += g->member[i].lv; c++; } - if( g->member[i].online ) + if (g->member[i].online) g->connect_member++; } - if( c ) // this check should always succeed... - { + if (c != 0) { // this check should always succeed... g->average_lv = sum / c; - if( g->connect_member != prev_count || g->average_lv != prev_alv ) + if (g->connect_member != prev_count || g->average_lv != prev_alv) g->save_flag |= GS_CONNECT; - if( g->save_flag & GS_REMOVE ) + if (g->save_flag & GS_REMOVE) g->save_flag &= ~GS_REMOVE; } g->save_flag |= GS_MEMBER; //Update guild member data - return 0; + return true; } // BreakGuild -int mapif_parse_BreakGuild(int fd, int guild_id) +static bool inter_guild_disband(int guild_id) { - struct guild * g; - - g = inter_guild->fromsql(guild_id); - if(g==NULL) - return 0; + struct guild *g = inter_guild->fromsql(guild_id); + if (g == NULL) + return false; // Delete guild from sql //printf("- Delete guild %d from guild\n",guild_id); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_db, guild_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_db, guild_id)) Sql_ShowDebug(inter->sql_handle); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_member_db, guild_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_member_db, guild_id)) Sql_ShowDebug(inter->sql_handle); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_castle_db, guild_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_castle_db, guild_id)) Sql_ShowDebug(inter->sql_handle); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_storage_db, guild_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_storage_db, guild_id)) Sql_ShowDebug(inter->sql_handle); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d' OR `alliance_id` = '%d'", guild_alliance_db, guild_id, guild_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d' OR `alliance_id` = '%d'", guild_alliance_db, guild_id, guild_id)) Sql_ShowDebug(inter->sql_handle); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_position_db, guild_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_position_db, guild_id)) Sql_ShowDebug(inter->sql_handle); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_skill_db, guild_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_skill_db, guild_id)) Sql_ShowDebug(inter->sql_handle); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_expulsion_db, guild_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_expulsion_db, guild_id)) Sql_ShowDebug(inter->sql_handle); //printf("- Update guild %d of char\n",guild_id); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `guild_id`='0' WHERE `guild_id`='%d'", char_db, guild_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `guild_id`='0' WHERE `guild_id`='%d'", char_db, guild_id)) Sql_ShowDebug(inter->sql_handle); - mapif->guild_broken(guild_id,0); + mapif->guild_broken(guild_id, 0); if (inter->enable_logs) inter->log("guild %s (id=%d) broken\n", g->name, guild_id); //Remove the guild from memory. [Skotlex] idb_remove(inter_guild->guild_db, guild_id); - return 0; -} -// Forward Guild message to others map servers -int mapif_parse_GuildMessage(int fd, int guild_id, int account_id, const char *mes, int len) -{ - return mapif->guild_message(guild_id,account_id,mes,len, fd); + return true; } /** * Changes basic guild information * The types are available in mmo.h::guild_basic_info **/ -int mapif_parse_GuildBasicInfoChange(int fd, int guild_id, int type, const void *data, int len) { +static bool inter_guild_update_basic_info(int guild_id, int type, const void *data, int len) +{ struct guild *g; struct guild_skill gd_skill; short value; g = inter_guild->fromsql(guild_id); - if( g == NULL ) - return 0; + if (g == NULL) + return false; nullpo_ret(data); - switch(type) { + switch (type) { case GBI_EXP: value = *((const int16 *)data); if( value < 0 && abs(value) > g->exp ) @@ -1506,26 +1208,25 @@ int mapif_parse_GuildBasicInfoChange(int fd, int guild_id, int type, const void default: ShowError("int_guild: GuildBasicInfoChange: Unknown type %d, see mmo.h::guild_basic_info for more information\n",type); - return 0; + return false; } mapif->guild_info(-1,g); g->save_flag |= GS_LEVEL; - // Information is already sent in mapif->guild_info - //mapif->guild_basicinfochanged(guild_id,type,data,len); - return 0; + + return true; } // Modification of the guild -int mapif_parse_GuildMemberInfoChange(int fd, int guild_id, int account_id, int char_id, int type, const char *data, int len) +static bool inter_guild_update_member_info(int guild_id, int account_id, int char_id, int type, const char *data, int len) { // Could make some improvement in speed, because only change guild_member int i; - struct guild * g; + struct guild *g; nullpo_ret(data); g = inter_guild->fromsql(guild_id); if(g==NULL) - return 0; + return false; // Search the member for (i = 0; i < g->max_member; i++) { @@ -1537,7 +1238,7 @@ int mapif_parse_GuildMemberInfoChange(int fd, int guild_id, int account_id, int if(i==g->max_member){ ShowWarning("int_guild: GuildMemberChange: Not found %d,%d in guild (%d - %s)\n", account_id,char_id,guild_id,g->name); - return 0; + return false; } switch(type) @@ -1618,17 +1319,18 @@ int mapif_parse_GuildMemberInfoChange(int fd, int guild_id, int account_id, int } default: ShowError("int_guild: GuildMemberInfoChange: Unknown type %d\n",type); + return false; break; } - return 0; + return true; } -int inter_guild_sex_changed(int guild_id, int account_id, int char_id, short gender) +static int inter_guild_sex_changed(int guild_id, int account_id, int char_id, short gender) { - return mapif->parse_GuildMemberInfoChange(0, guild_id, account_id, char_id, GMI_GENDER, (const char*)&gender, sizeof(gender)); + return inter_guild->update_member_info(guild_id, account_id, char_id, GMI_GENDER, (const char*)&gender, sizeof(gender)); } -int inter_guild_charname_changed(int guild_id, int account_id, int char_id, char *name) +static int inter_guild_charname_changed(int guild_id, int account_id, int char_id, char *name) { struct guild *g; int i, flag = 0; @@ -1666,35 +1368,34 @@ int inter_guild_charname_changed(int guild_id, int account_id, int char_id, char } // Change a position desc -int mapif_parse_GuildPosition(int fd, int guild_id, int idx, const struct guild_position *p) +static bool inter_guild_update_position(int guild_id, int idx, const struct guild_position *p) { // Could make some improvement in speed, because only change guild_position - struct guild * g; + struct guild *g; nullpo_ret(p); g = inter_guild->fromsql(guild_id); if(g==NULL || idx<0 || idx>=MAX_GUILDPOSITION) - return 0; + return false; memcpy(&g->position[idx],p,sizeof(struct guild_position)); mapif->guild_position(g,idx); g->position[idx].modified = GS_POSITION_MODIFIED; g->save_flag |= GS_POSITION; // Change guild_position - return 0; + return true; } // Guild Skill UP -int mapif_parse_GuildSkillUp(int fd, int guild_id, uint16 skill_id, int account_id, int max) +static bool inter_guild_use_skill_point(int guild_id, uint16 skill_id, int account_id, int max) { struct guild * g; int idx = skill_id - GD_SKILLBASE; g = inter_guild->fromsql(guild_id); if(g == NULL || idx < 0 || idx >= MAX_GUILDSKILL) - return 0; + return false; - if(g->skill_point>0 && g->skill[idx].id>0 && g->skill[idx].lv<max ) - { + if (g->skill_point > 0 && g->skill[idx].id > 0 && g->skill[idx].lv < max) { g->skill[idx].lv++; g->skill_point--; if (!inter_guild->calcinfo(g)) @@ -1702,11 +1403,11 @@ int mapif_parse_GuildSkillUp(int fd, int guild_id, uint16 skill_id, int account_ mapif->guild_skillupack(guild_id,skill_id,account_id); g->save_flag |= (GS_LEVEL|GS_SKILL); // Change guild & guild_skill } - return 0; + return true; } //Manual deletion of an alliance when partnering guild does not exists. [Skotlex] -int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int account_id1, int account_id2, int flag) +static bool inter_guild_remove_alliance(struct guild *g, int guild_id, int account_id1, int account_id2, int flag) { int i; char name[NAME_LENGTH]; @@ -1714,18 +1415,18 @@ int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int account_i nullpo_retr(-1, g); ARR_FIND( 0, MAX_GUILDALLIANCE, i, g->alliance[i].guild_id == guild_id ); if( i == MAX_GUILDALLIANCE ) - return -1; + return false; strcpy(name, g->alliance[i].name); g->alliance[i].guild_id=0; mapif->guild_alliance(g->guild_id,guild_id,account_id1,account_id2,flag,g->name,name); g->save_flag |= GS_ALLIANCE; - return 0; + return true; } // Alliance modification -int mapif_parse_GuildAlliance(int fd, int guild_id1, int guild_id2, int account_id1, int account_id2, int flag) +static bool inter_guild_change_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag) { // Could speed up struct guild *g[2] = { NULL }; @@ -1734,10 +1435,10 @@ int mapif_parse_GuildAlliance(int fd, int guild_id1, int guild_id2, int account_ g[1] = inter_guild->fromsql(guild_id2); if(g[0] && g[1]==NULL && (flag & GUILD_ALLIANCE_REMOVE)) //Requested to remove an alliance with a not found guild. - return mapif->parse_GuildDeleteAlliance(g[0], guild_id2, account_id1, account_id2, flag); //Try to do a manual removal of said guild. + return inter_guild->remove_alliance(g[0], guild_id2, account_id1, account_id2, flag); //Try to do a manual removal of said guild. if(g[0]==NULL || g[1]==NULL) - return 0; + return false; if( flag&GUILD_ALLIANCE_REMOVE ) { // Remove alliance/opposition, in case of alliance, remove on both side @@ -1766,11 +1467,11 @@ int mapif_parse_GuildAlliance(int fd, int guild_id1, int guild_id2, int account_ // Mark the two guild to be saved g[0]->save_flag |= GS_ALLIANCE; g[1]->save_flag |= GS_ALLIANCE; - return 0; + return true; } // Change guild message -int mapif_parse_GuildNotice(int fd, int guild_id, const char *mes1, const char *mes2) +static bool inter_guild_update_notice(int guild_id, const char *mes1, const char *mes2) { struct guild *g; @@ -1778,22 +1479,23 @@ int mapif_parse_GuildNotice(int fd, int guild_id, const char *mes1, const char * nullpo_ret(mes2); g = inter_guild->fromsql(guild_id); if(g==NULL) - return 0; + return false; memcpy(g->mes1,mes1,MAX_GUILDMES1); memcpy(g->mes2,mes2,MAX_GUILDMES2); g->save_flag |= GS_MES; //Change mes of guild - return mapif->guild_notice(g); + mapif->guild_notice(g); + return true; } -int mapif_parse_GuildEmblem(int fd, int len, int guild_id, int dummy, const char *data) +static bool inter_guild_update_emblem(int len, int guild_id, const char *data) { struct guild * g; nullpo_ret(data); g = inter_guild->fromsql(guild_id); if(g==NULL) - return 0; + return false; if (len > sizeof(g->emblem_data)) len = sizeof(g->emblem_data); @@ -1802,21 +1504,17 @@ int mapif_parse_GuildEmblem(int fd, int len, int guild_id, int dummy, const char g->emblem_len=len; g->emblem_id++; g->save_flag |= GS_EMBLEM; //Change guild - return mapif->guild_emblem(g); -} - -int mapif_parse_GuildCastleDataLoad(int fd, int len, const int *castle_ids) -{ - return mapif->guild_castle_dataload(fd, len, castle_ids); + mapif->guild_emblem(g); + return true; } -int mapif_parse_GuildCastleDataSave(int fd, int castle_id, int index, int value) +static bool inter_guild_update_castle_data(int castle_id, int index, int value) { struct guild_castle *gc = inter_guild->castle_fromsql(castle_id); if (gc == NULL) { - ShowError("mapif->parse_GuildCastleDataSave: castle id=%d not found\n", castle_id); - return 0; + ShowError("inter_guild->update_castle_data: castle id=%d not found\n", castle_id); + return false; } switch (index) { @@ -1842,14 +1540,14 @@ int mapif_parse_GuildCastleDataSave(int fd, int castle_id, int index, int value) gc->guardian[index-10].visible = value; break; } - ShowError("mapif->parse_GuildCastleDataSave: not found index=%d\n", index); - return 0; + ShowError("inter_guild->update_castle_data: not found index=%d\n", index); + return false; } inter_guild->castle_tosql(gc); - return 0; + return true; } -int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int len) +static bool inter_guild_change_leader(int guild_id, const char *name, int len) { struct guild * g; struct guild_member gm; @@ -1884,7 +1582,8 @@ int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int le ShowInfo("int_guild: Guildmaster Changed to %s (Guild %d - %s)\n",g->master, guild_id, g->name); g->save_flag |= (GS_BASIC|GS_MEMBER); //Save main data and member data. - return mapif->guild_master_changed(g, g->member[0].account_id, g->member[0].char_id); + mapif->guild_master_changed(g, g->member[0].account_id, g->member[0].char_id); + return true; } // Communication from the map server @@ -1894,7 +1593,7 @@ int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int le // Must Return // 1 : ok // 0 : error -int inter_guild_parse_frommap(int fd) +static int inter_guild_parse_frommap(int fd) { RFIFOHEAD(fd); switch(RFIFOW(fd,0)) { @@ -1923,13 +1622,7 @@ int inter_guild_parse_frommap(int fd) return 1; } -//Leave request from the server (for deleting character from guild) -int inter_guild_leave(int guild_id, int account_id, int char_id) -{ - return mapif->parse_GuildLeave(-1, guild_id, account_id, char_id, 0, "** Character Deleted **"); -} - -int inter_guild_broken(int guild_id) +static int inter_guild_broken(int guild_id) { return mapif->guild_broken(guild_id, 0); } @@ -1962,6 +1655,20 @@ void inter_guild_defaults(void) inter_guild->sex_changed = inter_guild_sex_changed; inter_guild->charname_changed = inter_guild_charname_changed; inter_guild->parse_frommap = inter_guild_parse_frommap; - inter_guild->leave = inter_guild_leave; inter_guild->broken = inter_guild_broken; + inter_guild->create = inter_guild_create; + inter_guild->add_member = inter_guild_add_member; + inter_guild->leave = inter_guild_leave; + inter_guild->update_member_info_short = inter_guild_update_member_info_short; + inter_guild->update_member_info = inter_guild_update_member_info; + inter_guild->disband = inter_guild_disband; + inter_guild->update_basic_info = inter_guild_update_basic_info; + inter_guild->update_position = inter_guild_update_position; + inter_guild->use_skill_point = inter_guild_use_skill_point; + inter_guild->remove_alliance = inter_guild_remove_alliance; + inter_guild->change_alliance = inter_guild_change_alliance; + inter_guild->update_notice = inter_guild_update_notice; + inter_guild->update_emblem = inter_guild_update_emblem; + inter_guild->update_castle_data = inter_guild_update_castle_data; + inter_guild->change_leader = inter_guild_change_leader; } diff --git a/src/char/int_guild.h b/src/char/int_guild.h index 93bf6c699..33873edcd 100644 --- a/src/char/int_guild.h +++ b/src/char/int_guild.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -68,8 +68,22 @@ struct inter_guild_interface { int (*sex_changed) (int guild_id, int account_id, int char_id, short gender); int (*charname_changed) (int guild_id, int account_id, int char_id, char *name); int (*parse_frommap) (int fd); - int (*leave) (int guild_id, int account_id, int char_id); int (*broken) (int guild_id); + struct guild *(*create) (const char *name, const struct guild_member *master); + bool (*add_member) (int guild_id, const struct guild_member *member, int map_fd); + bool (*leave) (int guild_id, int account_id, int char_id, int flag, const char *mes, int map_fd); + bool (*update_member_info_short) (int guild_id, int account_id, int char_id, int online, int lv, int16 class); + bool (*update_member_info) (int guild_id, int account_id, int char_id, int type, const char *data, int len); + bool (*disband) (int guild_id); + bool (*update_basic_info) (int guild_id, int type, const void *data, int len); + bool (*update_position) (int guild_id, int idx, const struct guild_position *p); + bool (*use_skill_point) (int guild_id, uint16 skill_id, int account_id, int max); + bool (*remove_alliance) (struct guild *g, int guild_id, int account_id1, int account_id2, int flag); + bool (*change_alliance) (int guild_id1, int guild_id2, int account_id1, int account_id2, int flag); + bool (*update_notice) (int guild_id, const char *mes1, const char *mes2); + bool (*update_emblem) (int len, int guild_id, const char *data); + bool (*update_castle_data) (int castle_id, int index, int value); + bool (*change_leader) (int guild_id, const char *name, int len); }; #ifdef HERCULES_CORE diff --git a/src/char/int_homun.c b/src/char/int_homun.c index 04f4c8f21..5a1c9d23f 100644 --- a/src/char/int_homun.c +++ b/src/char/int_homun.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -37,78 +37,18 @@ #include <stdio.h> #include <stdlib.h> -struct inter_homunculus_interface inter_homunculus_s; +static struct inter_homunculus_interface inter_homunculus_s; struct inter_homunculus_interface *inter_homunculus; -int inter_homunculus_sql_init(void) +static int inter_homunculus_sql_init(void) { return 0; } -void inter_homunculus_sql_final(void) +static void inter_homunculus_sql_final(void) { return; } -void mapif_homunculus_created(int fd, int account_id, const struct s_homunculus *sh, unsigned char flag) -{ - nullpo_retv(sh); - WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); - WFIFOW(fd,0) = 0x3890; - WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; - WFIFOL(fd,4) = account_id; - WFIFOB(fd,8)= flag; - memcpy(WFIFOP(fd,9),sh,sizeof(struct s_homunculus)); - WFIFOSET(fd, WFIFOW(fd,2)); -} - -void mapif_homunculus_deleted(int fd, int flag) -{ - WFIFOHEAD(fd, 3); - WFIFOW(fd, 0) = 0x3893; - WFIFOB(fd,2) = flag; //Flag 1 = success - WFIFOSET(fd, 3); -} - -void mapif_homunculus_loaded(int fd, int account_id, struct s_homunculus *hd) -{ - WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); - WFIFOW(fd,0) = 0x3891; - WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; - WFIFOL(fd,4) = account_id; - if( hd != NULL ) - { - WFIFOB(fd,8) = 1; // success - memcpy(WFIFOP(fd,9), hd, sizeof(struct s_homunculus)); - } - else - { - WFIFOB(fd,8) = 0; // not found. - memset(WFIFOP(fd,9), 0, sizeof(struct s_homunculus)); - } - WFIFOSET(fd, sizeof(struct s_homunculus)+9); -} - -void mapif_homunculus_saved(int fd, int account_id, bool flag) -{ - WFIFOHEAD(fd, 7); - WFIFOW(fd,0) = 0x3892; - WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = flag; // 1:success, 0:failure - WFIFOSET(fd, 7); -} - -void mapif_homunculus_renamed(int fd, int account_id, int char_id, unsigned char flag, const char *name) -{ - nullpo_retv(name); - WFIFOHEAD(fd, NAME_LENGTH+12); - WFIFOW(fd, 0) = 0x3894; - WFIFOL(fd, 2) = account_id; - WFIFOL(fd, 6) = char_id; - WFIFOB(fd,10) = flag; - safestrncpy(WFIFOP(fd,11), name, NAME_LENGTH); - WFIFOSET(fd, NAME_LENGTH+12); -} - /** * Creates a new homunculus with the given data. * @@ -119,7 +59,7 @@ void mapif_homunculus_renamed(int fd, int account_id, int char_id, unsigned char * @param[in,out] hd The new homunculus' data. * @retval false in case of errors. */ -bool mapif_homunculus_create(struct s_homunculus *hd) +static bool inter_homunculus_create(struct s_homunculus *hd) { char esc_name[NAME_LENGTH*2+1]; @@ -129,10 +69,10 @@ bool mapif_homunculus_create(struct s_homunculus *hd) SQL->EscapeStringLen(inter->sql_handle, esc_name, hd->name, strnlen(hd->name, NAME_LENGTH)); if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` " - "(`char_id`, `class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`, `rename_flag`, `vaporize`) " - "VALUES ('%d', '%d', '%d', '%s', '%d', '%u', '%u', '%d', '%d', %d, '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + "(`char_id`, `class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`, `rename_flag`, `vaporize`, `autofeed`) " + "VALUES ('%d', '%d', '%d', '%s', '%d', '%u', '%u', '%d', '%d', %d, '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk, - hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize)) { + hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize, hd->autofeed)) { Sql_ShowDebug(inter->sql_handle); return false; } @@ -146,7 +86,7 @@ bool mapif_homunculus_create(struct s_homunculus *hd) * @param hd The homunculus' data. * @retval false in case of errors. */ -bool mapif_homunculus_save(const struct s_homunculus *hd) +static bool inter_homunculus_save(const struct s_homunculus *hd) { bool flag = true; char esc_name[NAME_LENGTH*2+1]; @@ -156,9 +96,9 @@ bool mapif_homunculus_save(const struct s_homunculus *hd) SQL->EscapeStringLen(inter->sql_handle, esc_name, hd->name, strnlen(hd->name, NAME_LENGTH)); - if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `char_id`='%d', `class`='%d',`prev_class`='%d',`name`='%s',`level`='%d',`exp`='%u',`intimacy`='%u',`hunger`='%d', `str`='%d', `agi`='%d', `vit`='%d', `int`='%d', `dex`='%d', `luk`='%d', `hp`='%d',`max_hp`='%d',`sp`='%d',`max_sp`='%d',`skill_point`='%d', `rename_flag`='%d', `vaporize`='%d' WHERE `homun_id`='%d'", + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `char_id`='%d', `class`='%d',`prev_class`='%d',`name`='%s',`level`='%d',`exp`='%u',`intimacy`='%u',`hunger`='%d', `str`='%d', `agi`='%d', `vit`='%d', `int`='%d', `dex`='%d', `luk`='%d', `hp`='%d',`max_hp`='%d',`sp`='%d',`max_sp`='%d',`skill_point`='%d', `rename_flag`='%d', `vaporize`='%d', `autofeed`='%d' WHERE `homun_id`='%d'", homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk, - hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize, hd->hom_id)) { + hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize, hd->autofeed, hd->hom_id)) { Sql_ShowDebug(inter->sql_handle); flag = false; } else { @@ -171,9 +111,9 @@ bool mapif_homunculus_save(const struct s_homunculus *hd) } else { for (i = 0; i < MAX_HOMUNSKILL; ++i) { if (hd->hskill[i].id > 0 && hd->hskill[i].lv != 0) { - SQL->StmtBindParam(stmt, 0, SQLDT_USHORT, &hd->hskill[i].id, 0); - SQL->StmtBindParam(stmt, 1, SQLDT_USHORT, &hd->hskill[i].lv, 0); - if (SQL_ERROR == SQL->StmtExecute(stmt)) { + if (SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_USHORT, &hd->hskill[i].id, sizeof hd->hskill[i].id) + || SQL_ERROR == SQL->StmtBindParam(stmt, 1, SQLDT_UCHAR, &hd->hskill[i].lv, sizeof hd->hskill[i].lv) + || SQL_ERROR == SQL->StmtExecute(stmt)) { SqlStmt_ShowDebug(stmt); flag = false; break; @@ -188,7 +128,7 @@ bool mapif_homunculus_save(const struct s_homunculus *hd) } // Load an homunculus -bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd) +static bool inter_homunculus_load(int homun_id, struct s_homunculus *hd) { char* data; size_t len; @@ -196,7 +136,7 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd) nullpo_ret(hd); memset(hd, 0, sizeof(*hd)); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `homun_id`,`char_id`,`class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`rename_flag`, `vaporize` FROM `%s` WHERE `homun_id`='%d'", homunculus_db, homun_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `homun_id`,`char_id`,`class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`rename_flag`, `vaporize`, `autofeed` FROM `%s` WHERE `homun_id`='%d'", homunculus_db, homun_id)) { Sql_ShowDebug(inter->sql_handle); return false; @@ -236,6 +176,7 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd) SQL->GetData(inter->sql_handle, 19, &data, NULL); hd->skillpts = atoi(data); SQL->GetData(inter->sql_handle, 20, &data, NULL); hd->rename_flag = atoi(data); SQL->GetData(inter->sql_handle, 21, &data, NULL); hd->vaporize = atoi(data); + SQL->GetData(inter->sql_handle, 22, &data, NULL); hd->autofeed = atoi(data); SQL->FreeResult(inter->sql_handle); hd->intimacy = cap_value(hd->intimacy, 0, 100000); @@ -268,7 +209,7 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd) return true; } -bool mapif_homunculus_delete(int homun_id) +static bool inter_homunculus_delete(int homun_id) { if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `homun_id` = '%d'", homunculus_db, homun_id) || SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `homun_id` = '%d'", skill_homunculus_db, homun_id) @@ -279,7 +220,7 @@ bool mapif_homunculus_delete(int homun_id) return true; } -bool mapif_homunculus_rename(const char *name) +static bool inter_homunculus_rename(const char *name) { int i; @@ -301,47 +242,10 @@ bool mapif_homunculus_rename(const char *name) return true; } - -void mapif_parse_homunculus_create(int fd, int len, int account_id, const struct s_homunculus *phd) -{ - struct s_homunculus shd; - bool result; - - memcpy(&shd, phd, sizeof(shd)); - - result = mapif->homunculus_create(&shd); - mapif->homunculus_created(fd, account_id, &shd, result); -} - -void mapif_parse_homunculus_delete(int fd, int homun_id) -{ - bool result = mapif->homunculus_delete(homun_id); - mapif->homunculus_deleted(fd, result); -} - -void mapif_parse_homunculus_load(int fd, int account_id, int homun_id) -{ - struct s_homunculus hd; - bool result = mapif->homunculus_load(homun_id, &hd); - mapif->homunculus_loaded(fd, account_id, ( result ? &hd : NULL )); -} - -void mapif_parse_homunculus_save(int fd, int len, int account_id, const struct s_homunculus *phd) -{ - bool result = mapif->homunculus_save(phd); - mapif->homunculus_saved(fd, account_id, result); -} - -void mapif_parse_homunculus_rename(int fd, int account_id, int char_id, const char *name) -{ - bool result = mapif->homunculus_rename(name); - mapif->homunculus_renamed(fd, account_id, char_id, result, name); -} - /*========================================== * Inter Packets *------------------------------------------*/ -int inter_homunculus_parse_frommap(int fd) +static int inter_homunculus_parse_frommap(int fd) { unsigned short cmd = RFIFOW(fd,0); @@ -364,4 +268,10 @@ void inter_homunculus_defaults(void) inter_homunculus->sql_init = inter_homunculus_sql_init; inter_homunculus->sql_final = inter_homunculus_sql_final; inter_homunculus->parse_frommap = inter_homunculus_parse_frommap; + + inter_homunculus->create = inter_homunculus_create; + inter_homunculus->save = inter_homunculus_save; + inter_homunculus->load = inter_homunculus_load; + inter_homunculus->delete = inter_homunculus_delete; + inter_homunculus->rename = inter_homunculus_rename; } diff --git a/src/char/int_homun.h b/src/char/int_homun.h index 7ea7331f2..8eba66963 100644 --- a/src/char/int_homun.h +++ b/src/char/int_homun.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ #define CHAR_INT_HOMUN_H #include "common/hercules.h" +#include "common/mmo.h" /** * inter_homunculus interface @@ -30,6 +31,12 @@ struct inter_homunculus_interface { int (*sql_init) (void); void (*sql_final) (void); int (*parse_frommap) (int fd); + + bool (*create) (struct s_homunculus *hd); + bool (*save) (const struct s_homunculus *hd); + bool (*load) (int homun_id, struct s_homunculus* hd); + bool (*delete) (int homun_id); + bool (*rename) (const char *name); }; #ifdef HERCULES_CORE diff --git a/src/char/int_mail.c b/src/char/int_mail.c index 10f905a0d..e0625fcab 100644 --- a/src/char/int_mail.c +++ b/src/char/int_mail.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -37,10 +37,10 @@ #include <stdio.h> #include <stdlib.h> -struct inter_mail_interface inter_mail_s; +static struct inter_mail_interface inter_mail_s; struct inter_mail_interface *inter_mail; -static int inter_mail_fromsql(int char_id, struct mail_data* md) +static int inter_mail_fromsql(int char_id, struct mail_data *md) { int i, j; struct mail_message *msg; @@ -57,6 +57,8 @@ static int inter_mail_fromsql(int char_id, struct mail_data* md) "`zeny`,`amount`,`nameid`,`refine`,`attribute`,`identify`,`unique_id`"); for (i = 0; i < MAX_SLOTS; i++) StrBuf->Printf(&buf, ",`card%d`", i); + for (i = 0; i < MAX_ITEM_OPTIONS; i++) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", i, i); // I keep the `status` < 3 just in case someone forget to apply the sqlfix StrBuf->Printf(&buf, " FROM `%s` WHERE `dest_id`='%d' AND `status` < 3 ORDER BY `id` LIMIT %d", @@ -90,12 +92,18 @@ static int inter_mail_fromsql(int char_id, struct mail_data* md) SQL->GetData(inter->sql_handle,15, &data, NULL); item->unique_id = strtoull(data, NULL, 10); item->expire_time = 0; item->bound = 0; - - for (j = 0; j < MAX_SLOTS; j++) - { + /* Card Slots */ + for (j = 0; j < MAX_SLOTS; j++) { SQL->GetData(inter->sql_handle, 16 + j, &data, NULL); item->card[j] = atoi(data); } + /* Item Options */ + for (j = 0; j < MAX_ITEM_OPTIONS; j++) { + SQL->GetData(inter->sql_handle, 16 + MAX_SLOTS + j * 2, &data, NULL); + item->option[j].index = atoi(data); + SQL->GetData(inter->sql_handle, 17 + MAX_SLOTS + j * 2, &data, NULL); + item->option[j].value = atoi(data); + } } md->full = ( SQL->NumRows(inter->sql_handle) > MAIL_MAX_INBOX ); @@ -126,7 +134,7 @@ static int inter_mail_fromsql(int char_id, struct mail_data* md) /// Stores a single message in the database. /// Returns the message's ID if successful (or 0 if it fails). -int inter_mail_savemessage(struct mail_message* msg) +static int inter_mail_savemessage(struct mail_message *msg) { StringBuf buf; struct SqlStmt *stmt; @@ -138,25 +146,30 @@ int inter_mail_savemessage(struct mail_message* msg) StrBuf->Printf(&buf, "INSERT INTO `%s` (`send_name`, `send_id`, `dest_name`, `dest_id`, `title`, `message`, `time`, `status`, `zeny`, `amount`, `nameid`, `refine`, `attribute`, `identify`, `unique_id`", mail_db); for (j = 0; j < MAX_SLOTS; j++) StrBuf->Printf(&buf, ", `card%d`", j); + for (j = 0; j < MAX_ITEM_OPTIONS; j++) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j); StrBuf->Printf(&buf, ") VALUES (?, '%d', ?, '%d', ?, ?, '%lu', '%u', '%d', '%d', '%d', '%d', '%d', '%d', '%"PRIu64"'", msg->send_id, msg->dest_id, (unsigned long)msg->timestamp, msg->status, msg->zeny, msg->item.amount, msg->item.nameid, msg->item.refine, msg->item.attribute, msg->item.identify, msg->item.unique_id); for (j = 0; j < MAX_SLOTS; j++) StrBuf->Printf(&buf, ", '%d'", msg->item.card[j]); + for (j = 0; j < MAX_ITEM_OPTIONS; j++) + StrBuf->Printf(&buf, ", '%d', '%d'", msg->item.option[j].index, msg->item.option[j].value); StrBuf->AppendStr(&buf, ")"); // prepare and execute query stmt = SQL->StmtMalloc(inter->sql_handle); - if( SQL_SUCCESS != SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) + if (SQL_SUCCESS != SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_STRING, msg->send_name, strnlen(msg->send_name, NAME_LENGTH)) || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_STRING, msg->dest_name, strnlen(msg->dest_name, NAME_LENGTH)) || SQL_SUCCESS != SQL->StmtBindParam(stmt, 2, SQLDT_STRING, msg->title, strnlen(msg->title, MAIL_TITLE_LENGTH)) || SQL_SUCCESS != SQL->StmtBindParam(stmt, 3, SQLDT_STRING, msg->body, strnlen(msg->body, MAIL_BODY_LENGTH)) - || SQL_SUCCESS != SQL->StmtExecute(stmt) ) + || SQL_SUCCESS != SQL->StmtExecute(stmt)) { SqlStmt_ShowDebug(stmt); msg->id = 0; - } else + } else { msg->id = (int)SQL->StmtLastInsertId(stmt); + } SQL->StmtFree(stmt); StrBuf->Destroy(&buf); @@ -166,7 +179,7 @@ int inter_mail_savemessage(struct mail_message* msg) /// Retrieves a single message from the database. /// Returns true if the operation succeeds (or false if it fails). -static bool inter_mail_loadmessage(int mail_id, struct mail_message* msg) +static bool inter_mail_loadmessage(int mail_id, struct mail_message *msg) { int j; StringBuf buf; @@ -176,8 +189,10 @@ static bool inter_mail_loadmessage(int mail_id, struct mail_message* msg) StrBuf->Init(&buf); StrBuf->AppendStr(&buf, "SELECT `id`,`send_name`,`send_id`,`dest_name`,`dest_id`,`title`,`message`,`time`,`status`," "`zeny`,`amount`,`nameid`,`refine`,`attribute`,`identify`,`unique_id`"); - for( j = 0; j < MAX_SLOTS; j++ ) + for (j = 0; j < MAX_SLOTS; j++) StrBuf->Printf(&buf, ",`card%d`", j); + for (j = 0; j < MAX_ITEM_OPTIONS; j++) + StrBuf->Printf(&buf, ",`opt_idx%d`,`opt_val%d`", j, j); StrBuf->Printf(&buf, " FROM `%s` WHERE `id` = '%d'", mail_db, mail_id); if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) @@ -207,12 +222,18 @@ static bool inter_mail_loadmessage(int mail_id, struct mail_message* msg) SQL->GetData(inter->sql_handle,15, &data, NULL); msg->item.unique_id = strtoull(data, NULL, 10); msg->item.expire_time = 0; msg->item.bound = 0; - - for( j = 0; j < MAX_SLOTS; j++ ) - { + /* Card Slots */ + for (j = 0; j < MAX_SLOTS; j++) { SQL->GetData(inter->sql_handle,16 + j, &data, NULL); msg->item.card[j] = atoi(data); } + /* Item Options */ + for (j = 0 ; j < MAX_ITEM_OPTIONS; j++) { + SQL->GetData(inter->sql_handle, 16 + MAX_SLOTS + j * 2, &data, NULL); + msg->item.option[j].index = atoi(data); + SQL->GetData(inter->sql_handle, 17 + MAX_SLOTS + j * 2, &data, NULL); + msg->item.option[j].value = atoi(data); + } } StrBuf->Destroy(&buf); @@ -221,40 +242,16 @@ static bool inter_mail_loadmessage(int mail_id, struct mail_message* msg) return true; } -void mapif_mail_sendinbox(int fd, int char_id, unsigned char flag, struct mail_data *md) -{ - nullpo_retv(md); - //FIXME: dumping the whole structure like this is unsafe [ultramage] - WFIFOHEAD(fd, sizeof(struct mail_data) + 9); - WFIFOW(fd,0) = 0x3848; - WFIFOW(fd,2) = sizeof(struct mail_data) + 9; - WFIFOL(fd,4) = char_id; - WFIFOB(fd,8) = flag; - memcpy(WFIFOP(fd,9),md,sizeof(struct mail_data)); - WFIFOSET(fd,WFIFOW(fd,2)); -} - -/*========================================== - * Client Inbox Request - *------------------------------------------*/ -void mapif_parse_mail_requestinbox(int fd) -{ - int char_id = RFIFOL(fd,2); - unsigned char flag = RFIFOB(fd,6); - struct mail_data md; - memset(&md, 0, sizeof(md)); - inter_mail->fromsql(char_id, &md); - mapif->mail_sendinbox(fd, char_id, flag, &md); -} - /*========================================== * Mark mail as 'Read' *------------------------------------------*/ -void mapif_parse_mail_read(int fd) +static bool inter_mail_mark_read(int mail_id) { - int mail_id = RFIFOL(fd,2); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", mail_db, MAIL_READ, mail_id) ) + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", mail_db, MAIL_READ, mail_id)) { Sql_ShowDebug(inter->sql_handle); + return false; + } + return true; } /*========================================== @@ -269,6 +266,8 @@ static bool inter_mail_DeleteAttach(int mail_id) StrBuf->Printf(&buf, "UPDATE `%s` SET `zeny` = '0', `nameid` = '0', `amount` = '0', `refine` = '0', `attribute` = '0', `identify` = '0'", mail_db); for (i = 0; i < MAX_SLOTS; i++) StrBuf->Printf(&buf, ", `card%d` = '0'", i); + for (i = 0; i < MAX_ITEM_OPTIONS; i++) + StrBuf->Printf(&buf, ", `opt_idx%d` = '0', `opt_val%d` = '0'", i, i); StrBuf->Printf(&buf, " WHERE `id` = '%d'", mail_id); if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { @@ -282,194 +281,103 @@ static bool inter_mail_DeleteAttach(int mail_id) return true; } -void mapif_mail_sendattach(int fd, int char_id, struct mail_message *msg) +static bool inter_mail_get_attachment(int char_id, int mail_id, struct mail_message *msg) { - nullpo_retv(msg); - WFIFOHEAD(fd, sizeof(struct item) + 12); - WFIFOW(fd,0) = 0x384a; - WFIFOW(fd,2) = sizeof(struct item) + 12; - WFIFOL(fd,4) = char_id; - WFIFOL(fd,8) = (msg->zeny > 0)?msg->zeny:0; - memcpy(WFIFOP(fd,12), &msg->item, sizeof(struct item)); - WFIFOSET(fd,WFIFOW(fd,2)); -} - -void mapif_mail_getattach(int fd, int char_id, int mail_id) -{ - struct mail_message msg; - memset(&msg, 0, sizeof(msg)); - - if( !inter_mail->loadmessage(mail_id, &msg) ) - return; - - if( msg.dest_id != char_id ) - return; + nullpo_retr(false, msg); - if( msg.status != MAIL_READ ) - return; + if (!inter_mail->loadmessage(mail_id, msg)) + return false; - if( (msg.item.nameid < 1 || msg.item.amount < 1) && msg.zeny < 1 ) - return; // No Attachment + if (msg->dest_id != char_id) + return false; - if( !inter_mail->DeleteAttach(mail_id) ) - return; + if (msg->status != MAIL_READ) + return false; - mapif->mail_sendattach(fd, char_id, &msg); -} + if ((msg->item.nameid < 1 || msg->item.amount < 1) && msg->zeny < 1) + return false; // No Attachment -void mapif_parse_mail_getattach(int fd) -{ - mapif->mail_getattach(fd, RFIFOL(fd,2), RFIFOL(fd,6)); -} + if (!inter_mail->DeleteAttach(mail_id)) + return false; -/*========================================== - * Delete Mail - *------------------------------------------*/ -void mapif_mail_delete(int fd, int char_id, int mail_id, bool failed) -{ - WFIFOHEAD(fd,11); - WFIFOW(fd,0) = 0x384b; - WFIFOL(fd,2) = char_id; - WFIFOL(fd,6) = mail_id; - WFIFOB(fd,10) = failed; - WFIFOSET(fd,11); + return true; } -void mapif_parse_mail_delete(int fd) +static bool inter_mail_delete(int char_id, int mail_id) { - int char_id = RFIFOL(fd,2); - int mail_id = RFIFOL(fd,6); - bool failed = false; - if ( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", mail_db, mail_id) ) - { + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", mail_db, mail_id)) { Sql_ShowDebug(inter->sql_handle); - failed = true; + return false; } - mapif->mail_delete(fd, char_id, mail_id, failed); + return true; } -/*========================================== - * Report New Mail to Map Server - *------------------------------------------*/ -void mapif_mail_new(struct mail_message *msg) +static bool inter_mail_return_message(int char_id, int mail_id, int *new_mail) { - unsigned char buf[74]; - - if( !msg || !msg->id ) - return; - - WBUFW(buf,0) = 0x3849; - WBUFL(buf,2) = msg->dest_id; - WBUFL(buf,6) = msg->id; - memcpy(WBUFP(buf,10), msg->send_name, NAME_LENGTH); - memcpy(WBUFP(buf,34), msg->title, MAIL_TITLE_LENGTH); - mapif->sendall(buf, 74); -} + struct mail_message msg; + nullpo_retr(false, new_mail); -/*========================================== - * Return Mail - *------------------------------------------*/ -void mapif_mail_return(int fd, int char_id, int mail_id, int new_mail) -{ - WFIFOHEAD(fd,11); - WFIFOW(fd,0) = 0x384c; - WFIFOL(fd,2) = char_id; - WFIFOL(fd,6) = mail_id; - WFIFOB(fd,10) = (new_mail == 0); - WFIFOSET(fd,11); -} + if (!inter_mail->loadmessage(mail_id, &msg)) + return false; -void mapif_parse_mail_return(int fd) -{ - int char_id = RFIFOL(fd,2); - int mail_id = RFIFOL(fd,6); - struct mail_message msg; - int new_mail = 0; + if (msg.dest_id != char_id) + return false; - if( inter_mail->loadmessage(mail_id, &msg) ) - { - if( msg.dest_id != char_id) - return; - else if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", mail_db, mail_id) ) - Sql_ShowDebug(inter->sql_handle); - else - { - char temp_[MAIL_TITLE_LENGTH]; + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", mail_db, mail_id)) { + Sql_ShowDebug(inter->sql_handle); + } else { + char temp_[MAIL_TITLE_LENGTH]; - // swap sender and receiver - swap(msg.send_id, msg.dest_id); - safestrncpy(temp_, msg.send_name, NAME_LENGTH); - safestrncpy(msg.send_name, msg.dest_name, NAME_LENGTH); - safestrncpy(msg.dest_name, temp_, NAME_LENGTH); + // swap sender and receiver + swap(msg.send_id, msg.dest_id); + safestrncpy(temp_, msg.send_name, NAME_LENGTH); + safestrncpy(msg.send_name, msg.dest_name, NAME_LENGTH); + safestrncpy(msg.dest_name, temp_, NAME_LENGTH); - // set reply message title - snprintf(temp_, MAIL_TITLE_LENGTH, "RE:%s", msg.title); - safestrncpy(msg.title, temp_, MAIL_TITLE_LENGTH); + // set reply message title + safesnprintf(temp_, MAIL_TITLE_LENGTH, "RE:%s", msg.title); + safestrncpy(msg.title, temp_, MAIL_TITLE_LENGTH); - msg.status = MAIL_NEW; - msg.timestamp = time(NULL); + msg.status = MAIL_NEW; + msg.timestamp = time(NULL); - new_mail = inter_mail->savemessage(&msg); - mapif->mail_new(&msg); - } + *new_mail = inter_mail->savemessage(&msg); + mapif->mail_new(&msg); } - mapif->mail_return(fd, char_id, mail_id, new_mail); -} + return true; -/*========================================== - * Send Mail - *------------------------------------------*/ -void mapif_mail_send(int fd, struct mail_message* msg) -{ - int len = sizeof(struct mail_message) + 4; - - nullpo_retv(msg); - WFIFOHEAD(fd,len); - WFIFOW(fd,0) = 0x384d; - WFIFOW(fd,2) = len; - memcpy(WFIFOP(fd,4), msg, sizeof(struct mail_message)); - WFIFOSET(fd,len); } -void mapif_parse_mail_send(int fd) +static bool inter_mail_send(int account_id, struct mail_message *msg) { - struct mail_message msg; char esc_name[NAME_LENGTH*2+1]; - int account_id = 0; - - if(RFIFOW(fd,2) != 8 + sizeof(struct mail_message)) - return; - account_id = RFIFOL(fd,4); - memcpy(&msg, RFIFOP(fd,8), sizeof(struct mail_message)); + nullpo_retr(false, msg); // Try to find the Dest Char by Name - SQL->EscapeStringLen(inter->sql_handle, esc_name, msg.dest_name, strnlen(msg.dest_name, NAME_LENGTH)); - if ( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`, `char_id` FROM `%s` WHERE `name` = '%s'", char_db, esc_name) ) + SQL->EscapeStringLen(inter->sql_handle, esc_name, msg->dest_name, strnlen(msg->dest_name, NAME_LENGTH)); + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`, `char_id` FROM `%s` WHERE `name` = '%s'", char_db, esc_name)) { Sql_ShowDebug(inter->sql_handle); - else - if ( SQL_SUCCESS == SQL->NextRow(inter->sql_handle) ) - { + } else if (SQL_SUCCESS == SQL->NextRow(inter->sql_handle)) { char *data; SQL->GetData(inter->sql_handle, 0, &data, NULL); - if (atoi(data) != account_id) - { // Cannot send mail to char in the same account + if (atoi(data) != account_id) { + // Cannot send mail to char in the same account SQL->GetData(inter->sql_handle, 1, &data, NULL); - msg.dest_id = atoi(data); + msg->dest_id = atoi(data); } } SQL->FreeResult(inter->sql_handle); - msg.status = MAIL_NEW; + msg->status = MAIL_NEW; - if( msg.dest_id > 0 ) - msg.id = inter_mail->savemessage(&msg); + if (msg->dest_id > 0) + msg->id = inter_mail->savemessage(msg); - mapif->mail_send(fd, &msg); // notify sender - mapif->mail_new(&msg); // notify recipient + return true; } -void inter_mail_sendmail(int send_id, const char* send_name, int dest_id, const char* dest_name, const char* title, const char* body, int zeny, struct item *item) +static void inter_mail_sendmail(int send_id, const char* send_name, int dest_id, const char *dest_name, const char *title, const char *body, int zeny, struct item *item) { struct mail_message msg; nullpo_retv(send_name); @@ -497,7 +405,7 @@ void inter_mail_sendmail(int send_id, const char* send_name, int dest_id, const /*========================================== * Packets From Map Server *------------------------------------------*/ -int inter_mail_parse_frommap(int fd) +static int inter_mail_parse_frommap(int fd) { switch(RFIFOW(fd,0)) { @@ -513,12 +421,12 @@ int inter_mail_parse_frommap(int fd) return 1; } -int inter_mail_sql_init(void) +static int inter_mail_sql_init(void) { return 1; } -void inter_mail_sql_final(void) +static void inter_mail_sql_final(void) { return; } @@ -535,4 +443,9 @@ void inter_mail_defaults(void) inter_mail->sql_final = inter_mail_sql_final; inter_mail->fromsql = inter_mail_fromsql; inter_mail->loadmessage = inter_mail_loadmessage; + inter_mail->mark_read = inter_mail_mark_read; + inter_mail->get_attachment = inter_mail_get_attachment; + inter_mail->delete = inter_mail_delete; + inter_mail->return_message = inter_mail_return_message; + inter_mail->send = inter_mail_send; } diff --git a/src/char/int_mail.h b/src/char/int_mail.h index cb72db504..95934d0a1 100644 --- a/src/char/int_mail.h +++ b/src/char/int_mail.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -39,6 +39,11 @@ struct inter_mail_interface { bool (*loadmessage) (int mail_id, struct mail_message* msg); bool (*DeleteAttach) (int mail_id); void (*sendmail) (int send_id, const char* send_name, int dest_id, const char* dest_name, const char* title, const char* body, int zeny, struct item *item); + bool (*mark_read) (int mail_id); + bool (*get_attachment) (int char_id, int mail_id, struct mail_message *msg); + bool (*delete) (int char_id, int mail_id); + bool (*return_message) (int char_id, int mail_id, int *new_mail); + bool (*send) (int account_id, struct mail_message *msg); }; #ifdef HERCULES_CORE diff --git a/src/char/int_mercenary.c b/src/char/int_mercenary.c index f96e45a50..21bfb5538 100644 --- a/src/char/int_mercenary.c +++ b/src/char/int_mercenary.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -38,10 +38,10 @@ #include <stdio.h> #include <stdlib.h> -struct inter_mercenary_interface inter_mercenary_s; +static struct inter_mercenary_interface inter_mercenary_s; struct inter_mercenary_interface *inter_mercenary; -bool inter_mercenary_owner_fromsql(int char_id, struct mmo_charstatus *status) +static bool inter_mercenary_owner_fromsql(int char_id, struct mmo_charstatus *status) { char* data; @@ -70,7 +70,7 @@ bool inter_mercenary_owner_fromsql(int char_id, struct mmo_charstatus *status) return true; } -bool inter_mercenary_owner_tosql(int char_id, struct mmo_charstatus *status) +static bool inter_mercenary_owner_tosql(int char_id, struct mmo_charstatus *status) { nullpo_ret(status); if( SQL_ERROR == SQL->Query(inter->sql_handle, "REPLACE INTO `%s` (`char_id`, `merc_id`, `arch_calls`, `arch_faith`, `spear_calls`, `spear_faith`, `sword_calls`, `sword_faith`) VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", @@ -83,7 +83,7 @@ bool inter_mercenary_owner_tosql(int char_id, struct mmo_charstatus *status) return true; } -bool inter_mercenary_owner_delete(int char_id) +static bool inter_mercenary_owner_delete(int char_id) { if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d'", mercenary_owner_db, char_id) ) Sql_ShowDebug(inter->sql_handle); @@ -104,7 +104,7 @@ bool inter_mercenary_owner_delete(int char_id) * @param[in,out] merc The new mercenary's data. * @retval false in case of errors. */ -bool mapif_mercenary_create(struct s_mercenary *merc) +static bool inter_mercenary_create(struct s_mercenary *merc) { nullpo_retr(false, merc); Assert_retr(false, merc->mercenary_id == 0); @@ -126,7 +126,7 @@ bool mapif_mercenary_create(struct s_mercenary *merc) * @param merc The mercenary's data. * @retval false in case of errors. */ -bool mapif_mercenary_save(const struct s_mercenary *merc) +static bool inter_mercenary_save(const struct s_mercenary *merc) { nullpo_retr(false, merc); Assert_retr(false, merc->mercenary_id > 0); @@ -141,7 +141,7 @@ bool mapif_mercenary_save(const struct s_mercenary *merc) return true; } -bool mapif_mercenary_load(int merc_id, int char_id, struct s_mercenary *merc) +static bool inter_mercenary_load(int merc_id, int char_id, struct s_mercenary *merc) { char* data; @@ -174,7 +174,7 @@ bool mapif_mercenary_load(int merc_id, int char_id, struct s_mercenary *merc) return true; } -bool mapif_mercenary_delete(int merc_id) +static bool inter_mercenary_delete(int merc_id) { if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mer_id` = '%d'", mercenary_db, merc_id) ) { @@ -185,70 +185,12 @@ bool mapif_mercenary_delete(int merc_id) return true; } -void mapif_mercenary_send(int fd, struct s_mercenary *merc, unsigned char flag) -{ - int size = sizeof(struct s_mercenary) + 5; - - nullpo_retv(merc); - WFIFOHEAD(fd,size); - WFIFOW(fd,0) = 0x3870; - WFIFOW(fd,2) = size; - WFIFOB(fd,4) = flag; - memcpy(WFIFOP(fd,5),merc,sizeof(struct s_mercenary)); - WFIFOSET(fd,size); -} - -void mapif_parse_mercenary_create(int fd, const struct s_mercenary *merc) -{ - struct s_mercenary merc_; - bool result; - - memcpy(&merc_, merc, sizeof(merc_)); - - result = mapif->mercenary_create(&merc_); - mapif->mercenary_send(fd, &merc_, result); -} - -void mapif_parse_mercenary_load(int fd, int merc_id, int char_id) -{ - struct s_mercenary merc; - bool result = mapif->mercenary_load(merc_id, char_id, &merc); - mapif->mercenary_send(fd, &merc, result); -} - -void mapif_mercenary_deleted(int fd, unsigned char flag) -{ - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x3871; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,3); -} - -void mapif_parse_mercenary_delete(int fd, int merc_id) -{ - bool result = mapif->mercenary_delete(merc_id); - mapif->mercenary_deleted(fd, result); -} - -void mapif_mercenary_saved(int fd, unsigned char flag) -{ - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x3872; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,3); -} - -void mapif_parse_mercenary_save(int fd, struct s_mercenary* merc) -{ - bool result = mapif->mercenary_save(merc); - mapif->mercenary_saved(fd, result); -} - -int inter_mercenary_sql_init(void) +static int inter_mercenary_sql_init(void) { return 0; } -void inter_mercenary_sql_final(void) + +static void inter_mercenary_sql_final(void) { return; } @@ -256,7 +198,7 @@ void inter_mercenary_sql_final(void) /*========================================== * Inter Packets *------------------------------------------*/ -int inter_mercenary_parse_frommap(int fd) +static int inter_mercenary_parse_frommap(int fd) { unsigned short cmd = RFIFOW(fd,0); @@ -282,4 +224,9 @@ void inter_mercenary_defaults(void) inter_mercenary->sql_init = inter_mercenary_sql_init; inter_mercenary->sql_final = inter_mercenary_sql_final; inter_mercenary->parse_frommap = inter_mercenary_parse_frommap; + + inter_mercenary->create = inter_mercenary_create; + inter_mercenary->load = inter_mercenary_load; + inter_mercenary->save = inter_mercenary_save; + inter_mercenary->delete = inter_mercenary_delete; } diff --git a/src/char/int_mercenary.h b/src/char/int_mercenary.h index 3b1623438..6291bfcf6 100644 --- a/src/char/int_mercenary.h +++ b/src/char/int_mercenary.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ #define CHAR_INT_MERCENARY_H #include "common/hercules.h" +#include "common/mmo.h" struct mmo_charstatus; @@ -35,6 +36,11 @@ struct inter_mercenary_interface { int (*sql_init) (void); void (*sql_final) (void); int (*parse_frommap) (int fd); + + bool (*create) (struct s_mercenary *merc); + bool (*save) (const struct s_mercenary *merc); + bool (*load) (int merc_id, int char_id, struct s_mercenary *merc); + bool (*delete) (int merc_id); }; #ifdef HERCULES_CORE diff --git a/src/char/int_party.c b/src/char/int_party.c index 921bf6d9d..8ee03ecc5 100644 --- a/src/char/int_party.c +++ b/src/char/int_party.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -39,11 +39,12 @@ #include <stdio.h> #include <stdlib.h> -struct inter_party_interface inter_party_s; +static struct inter_party_interface inter_party_s; struct inter_party_interface *inter_party; //Updates party's level range and unsets even share if broken. -static int inter_party_check_lv(struct party_data *p) { +static int inter_party_check_lv(struct party_data *p) +{ int i; unsigned int lv; nullpo_ret(p); @@ -121,7 +122,7 @@ static void inter_party_calc_state(struct party_data *p) } // Save party to mysql -int inter_party_tosql(struct party *p, int flag, int index) +static int inter_party_tosql(struct party *p, int flag, int index) { // 'party' ('party_id','name','exp','item','leader_id','leader_char') char esc_name[NAME_LENGTH*2+1];// escaped party name @@ -196,7 +197,7 @@ int inter_party_tosql(struct party *p, int flag, int index) } // Read party from mysql -struct party_data *inter_party_fromsql(int party_id) +static struct party_data *inter_party_fromsql(int party_id) { int leader_id = 0; int leader_char = 0; @@ -268,7 +269,7 @@ struct party_data *inter_party_fromsql(int party_id) return p; } -int inter_party_sql_init(void) +static int inter_party_sql_init(void) { //memory alloc inter_party->db = idb_alloc(DB_OPT_RELEASE_DATA); @@ -287,7 +288,7 @@ int inter_party_sql_init(void) return 0; } -void inter_party_sql_final(void) +static void inter_party_sql_final(void) { inter_party->db->destroy(inter_party->db, NULL); aFree(inter_party->pt); @@ -295,7 +296,7 @@ void inter_party_sql_final(void) } // Search for the party according to its name -struct party_data* inter_party_search_partyname(const char *const str) +static struct party_data *inter_party_search_partyname(const char *const str) { char esc_name[NAME_LENGTH*2+1]; char* data; @@ -314,14 +315,14 @@ struct party_data* inter_party_search_partyname(const char *const str) } // Returns whether this party can keep having exp share or not. -int inter_party_check_exp_share(struct party_data *const p) +static int inter_party_check_exp_share(struct party_data *const p) { nullpo_ret(p); return (p->party.count < 2 || p->max_lv - p->min_lv <= party_share_level); } // Is there any member in the party? -int inter_party_check_empty(struct party_data *p) +static int inter_party_check_empty(struct party_data *p) { int i; if (p==NULL||p->party.party_id==0) return 1; @@ -333,161 +334,16 @@ int inter_party_check_empty(struct party_data *p) return 1; } -//------------------------------------------------------------------- -// Communication to the map server - - -// Create a party whether or not -int mapif_party_created(int fd, int account_id, int char_id, struct party *p) -{ - WFIFOHEAD(fd, 39); - WFIFOW(fd,0)=0x3820; - WFIFOL(fd,2)=account_id; - WFIFOL(fd,6)=char_id; - if(p!=NULL){ - WFIFOB(fd,10)=0; - WFIFOL(fd,11)=p->party_id; - memcpy(WFIFOP(fd,15),p->name,NAME_LENGTH); - ShowInfo("int_party: Party created (%d - %s)\n",p->party_id,p->name); - }else{ - WFIFOB(fd,10)=1; - WFIFOL(fd,11)=0; - memset(WFIFOP(fd,15),0,NAME_LENGTH); - } - WFIFOSET(fd,39); - - return 0; -} - -//Party information not found -void mapif_party_noinfo(int fd, int party_id, int char_id) -{ - WFIFOHEAD(fd, 12); - WFIFOW(fd,0) = 0x3821; - WFIFOW(fd,2) = 12; - WFIFOL(fd,4) = char_id; - WFIFOL(fd,8) = party_id; - WFIFOSET(fd,12); - ShowWarning("int_party: info not found (party_id=%d char_id=%d)\n", party_id, char_id); -} - -//Digest party information -void mapif_party_info(int fd, struct party* p, int char_id) -{ - unsigned char buf[8 + sizeof(struct party)]; - nullpo_retv(p); - WBUFW(buf,0) = 0x3821; - WBUFW(buf,2) = 8 + sizeof(struct party); - WBUFL(buf,4) = char_id; - memcpy(WBUFP(buf,8), p, sizeof(struct party)); - - if(fd<0) - mapif->sendall(buf,WBUFW(buf,2)); - else - mapif->send(fd,buf,WBUFW(buf,2)); -} - -//Whether or not additional party members -int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) { - WFIFOHEAD(fd, 15); - WFIFOW(fd,0) = 0x3822; - WFIFOL(fd,2) = party_id; - WFIFOL(fd,6) = account_id; - WFIFOL(fd,10) = char_id; - WFIFOB(fd,14) = flag; - WFIFOSET(fd,15); - - return 0; -} - -// Party setting change notification -int mapif_party_optionchanged(int fd, struct party *p, int account_id, int flag) -{ - unsigned char buf[16]; - nullpo_ret(p); - WBUFW(buf,0)=0x3823; - WBUFL(buf,2)=p->party_id; - WBUFL(buf,6)=account_id; - WBUFW(buf,10)=p->exp; - WBUFW(buf,12)=p->item; - WBUFB(buf,14)=flag; - if(flag==0) - mapif->sendall(buf,15); - else - mapif->send(fd,buf,15); - return 0; -} - -//Withdrawal notification party -int mapif_party_withdraw(int party_id,int account_id, int char_id) { - unsigned char buf[16]; - - WBUFW(buf,0) = 0x3824; - WBUFL(buf,2) = party_id; - WBUFL(buf,6) = account_id; - WBUFL(buf,10) = char_id; - mapif->sendall(buf, 14); - return 0; -} - -//Party map update notification -int mapif_party_membermoved(struct party *p, int idx) -{ - unsigned char buf[20]; - - nullpo_ret(p); - Assert_ret(idx >= 0 && idx < MAX_PARTY); - WBUFW(buf,0) = 0x3825; - WBUFL(buf,2) = p->party_id; - WBUFL(buf,6) = p->member[idx].account_id; - WBUFL(buf,10) = p->member[idx].char_id; - WBUFW(buf,14) = p->member[idx].map; - WBUFB(buf,16) = p->member[idx].online; - WBUFW(buf,17) = p->member[idx].lv; - mapif->sendall(buf, 19); - return 0; -} - -//Dissolution party notification -int mapif_party_broken(int party_id, int flag) -{ - unsigned char buf[16]; - WBUFW(buf,0)=0x3826; - WBUFL(buf,2)=party_id; - WBUFB(buf,6)=flag; - mapif->sendall(buf,7); - //printf("int_party: broken %d\n",party_id); - return 0; -} - -//Remarks in the party -int mapif_party_message(int party_id, int account_id, const char *mes, int len, int sfd) -{ - unsigned char buf[512]; - nullpo_ret(mes); - WBUFW(buf,0)=0x3827; - WBUFW(buf,2)=len+12; - WBUFL(buf,4)=party_id; - WBUFL(buf,8)=account_id; - memcpy(WBUFP(buf,12),mes,len); - mapif->sendallwos(sfd, buf,len+12); - return 0; -} - -//------------------------------------------------------------------- -// Communication from the map server - - // Create Party -int mapif_parse_CreateParty(int fd, const char *name, int item, int item2, const struct party_member *leader) +static struct party_data *inter_party_create(const char *name, int item, int item2, const struct party_member *leader) { struct party_data *p; int i; nullpo_ret(name); nullpo_ret(leader); - if (!*name || (p = inter_party->search_partyname(name)) != NULL) { - mapif->party_created(fd,leader->account_id,leader->char_id,NULL); - return 0; + + if (!*name || inter_party->search_partyname(name) != NULL) { + return NULL; } // Check Authorized letters/symbols in the name of the character if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorized @@ -497,18 +353,16 @@ int mapif_parse_CreateParty(int fd, const char *name, int item, int item2, const char *newname = aStrndup(name, NAME_LENGTH-1); normalize_name(newname,"\""); trim(newname); - mapif->parse_CreateParty(fd, newname, item, item2, leader); + p = inter_party->create(newname, item, item2, leader); aFree(newname); - return 0; + return p; } - mapif->party_created(fd,leader->account_id,leader->char_id,NULL); - return 0; + return NULL; } } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden for (i = 0; i < NAME_LENGTH && name[i]; i++) if (strchr(char_name_letters, name[i]) != NULL) { - mapif->party_created(fd,leader->account_id,leader->char_id,NULL); - return 0; + return NULL; } } @@ -523,34 +377,20 @@ int mapif_parse_CreateParty(int fd, const char *name, int item, int item2, const p->party.member[0].online=1; p->party.party_id=-1;//New party. - if (inter_party->tosql(&p->party,PS_CREATE|PS_ADDMEMBER,0)) { - //Add party to db - inter_party->calc_state(p); - idb_put(inter_party->db, p->party.party_id, p); - mapif->party_info(fd, &p->party, 0); - mapif->party_created(fd,leader->account_id,leader->char_id,&p->party); - } else { //Failed to create party. + if (!inter_party->tosql(&p->party, PS_CREATE | PS_ADDMEMBER, 0)) { aFree(p); - mapif->party_created(fd,leader->account_id,leader->char_id,NULL); + return NULL; } - return 0; -} - -// Party information request -void mapif_parse_PartyInfo(int fd, int party_id, int char_id) -{ - struct party_data *p; - p = inter_party->fromsql(party_id); + //Add party to db + inter_party->calc_state(p); + idb_put(inter_party->db, p->party.party_id, p); - if (p) - mapif->party_info(fd, &p->party, char_id); - else - mapif->party_noinfo(fd, party_id, char_id); + return p; } // Add a player to party request -int mapif_parse_PartyAddMember(int fd, int party_id, const struct party_member *member) +static bool inter_party_add_member(int party_id, const struct party_member *member) { struct party_data *p; int i; @@ -558,15 +398,13 @@ int mapif_parse_PartyAddMember(int fd, int party_id, const struct party_member * nullpo_ret(member); p = inter_party->fromsql(party_id); if( p == NULL || p->size == MAX_PARTY ) { - mapif->party_memberadded(fd, party_id, member->account_id, member->char_id, 1); - return 0; + return false; } ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == 0 ); - if( i == MAX_PARTY ) - {// Party full - mapif->party_memberadded(fd, party_id, member->account_id, member->char_id, 1); - return 0; + if (i == MAX_PARTY) { + // Party full + return false; } memcpy(&p->party.member[i], member, sizeof(struct party_member)); @@ -582,21 +420,20 @@ int mapif_parse_PartyAddMember(int fd, int party_id, const struct party_member * } mapif->party_info(-1, &p->party, 0); - mapif->party_memberadded(fd, party_id, member->account_id, member->char_id, 0); inter_party->tosql(&p->party, PS_ADDMEMBER, i); - return 0; + return true; } //Party setting change request -int mapif_parse_PartyChangeOption(int fd,int party_id,int account_id,int exp,int item) +static bool inter_party_change_option(int party_id, int account_id, int exp, int item, int map_fd) { struct party_data *p; int flag = 0; p = inter_party->fromsql(party_id); if(!p) - return 0; + return false; p->party.exp=exp; if( exp && !inter_party->check_exp_share(p) ){ @@ -604,13 +441,13 @@ int mapif_parse_PartyChangeOption(int fd,int party_id,int account_id,int exp,int p->party.exp=0; } p->party.item = item&0x3; //Filter out invalid values. - mapif->party_optionchanged(fd,&p->party,account_id,flag); + mapif->party_optionchanged(map_fd, &p->party, account_id, flag); inter_party->tosql(&p->party, PS_BASIC, 0); - return 0; + return true; } //Request leave party -int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) +static bool inter_party_leave(int party_id, int account_id, int char_id) { struct party_data *p; int i,j; @@ -620,7 +457,7 @@ int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) {// Party does not exists? if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id) ) Sql_ShowDebug(inter->sql_handle); - return 0; + return false; } for (i = 0; i < MAX_PARTY; i++) { @@ -630,10 +467,10 @@ int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) } } if (i >= MAX_PARTY) - return 0; //Member not found? + return false; //Member not found? mapif->party_withdraw(party_id, account_id, char_id); - + j = p->party.member[i].lv; if (p->party.member[i].online > 0) p->party.count--; @@ -648,23 +485,25 @@ int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) inter_party->tosql(&p->party, PS_DELMEMBER, i); mapif->party_info(-1, &p->party, 0); } - return 0; + return true; } + // When member goes to other map or levels up. -int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv) +static bool inter_party_change_map(int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv) { struct party_data *p; int i; p = inter_party->fromsql(party_id); if (p == NULL) - return 0; + return false; for(i = 0; i < MAX_PARTY && (p->party.member[i].account_id != account_id || p->party.member[i].char_id != char_id); i++); - if (i == MAX_PARTY) return 0; + if (i == MAX_PARTY) + return false; if (p->party.member[i].online != online) { @@ -704,30 +543,24 @@ int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id p->party.member[i].map = map; mapif->party_membermoved(&p->party, i); } - return 0; + return true; } //Request party dissolution -int mapif_parse_BreakParty(int fd, int party_id) +static bool inter_party_disband(int party_id) { struct party_data *p; p = inter_party->fromsql(party_id); if(!p) - return 0; + return false; inter_party->tosql(&p->party,PS_BREAK,0); - mapif->party_broken(fd,party_id); + mapif->party_broken(party_id, 1); return 0; } -//Party sending the message -int mapif_parse_PartyMessage(int fd, int party_id, int account_id, const char *mes, int len) -{ - return mapif->party_message(party_id,account_id,mes,len, fd); -} - -int mapif_parse_PartyLeaderChange(int fd, int party_id, int account_id, int char_id) +static bool inter_party_change_leader(int party_id, int account_id, int char_id) { struct party_data *p; int i; @@ -735,7 +568,7 @@ int mapif_parse_PartyLeaderChange(int fd, int party_id, int account_id, int char p = inter_party->fromsql(party_id); if(!p) - return 0; + return false; for (i = 0; i < MAX_PARTY; i++) { if(p->party.member[i].leader) @@ -745,10 +578,9 @@ int mapif_parse_PartyLeaderChange(int fd, int party_id, int account_id, int char inter_party->tosql(&p->party,PS_LEADER, i); } } - return 1; + return true; } - // Communication from the map server //-Analysis that only one packet // Data packet length is set to inter.c that you @@ -756,7 +588,7 @@ int mapif_parse_PartyLeaderChange(int fd, int party_id, int account_id, int char // Return : // 0 : error // 1 : ok -int inter_party_parse_frommap(int fd) +static int inter_party_parse_frommap(int fd) { RFIFOHEAD(fd); switch(RFIFOW(fd,0)) { @@ -775,13 +607,7 @@ int inter_party_parse_frommap(int fd) return 1; } -//Leave request from the server (for delete character) -int inter_party_leave(int party_id,int account_id, int char_id) -{ - return mapif->parse_PartyLeave(-1,party_id,account_id, char_id); -} - -int inter_party_CharOnline(int char_id, int party_id) +static int inter_party_CharOnline(int char_id, int party_id) { struct party_data* p; int i; @@ -828,7 +654,8 @@ int inter_party_CharOnline(int char_id, int party_id) return 1; } -int inter_party_CharOffline(int char_id, int party_id) { +static int inter_party_CharOffline(int char_id, int party_id) +{ struct party_data *p=NULL; int i; @@ -895,4 +722,11 @@ void inter_party_defaults(void) inter_party->leave = inter_party_leave; inter_party->CharOnline = inter_party_CharOnline; inter_party->CharOffline = inter_party_CharOffline; + + inter_party->create = inter_party_create; + inter_party->add_member = inter_party_add_member; + inter_party->change_option = inter_party_change_option; + inter_party->change_map = inter_party_change_map; + inter_party->disband = inter_party_disband; + inter_party->change_leader = inter_party_change_leader; } diff --git a/src/char/int_party.h b/src/char/int_party.h index 77363dd45..b9a888cca 100644 --- a/src/char/int_party.h +++ b/src/char/int_party.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -60,9 +60,15 @@ struct inter_party_interface { int (*check_exp_share) (struct party_data *p); int (*check_empty) (struct party_data *p); int (*parse_frommap) (int fd); - int (*leave) (int party_id,int account_id, int char_id); + bool (*leave) (int party_id,int account_id, int char_id); int (*CharOnline) (int char_id, int party_id); int (*CharOffline) (int char_id, int party_id); + struct party_data *(*create) (const char *name, int item, int item2, const struct party_member *leader); + bool (*add_member) (int party_id, const struct party_member *member); + bool (*change_option) (int party_id, int account_id, int exp, int item, int map_fd); + bool (*change_map) (int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv); + bool (*disband) (int party_id); + bool (*change_leader) (int party_id, int account_id, int char_id); }; #ifdef HERCULES_CORE diff --git a/src/char/int_pet.c b/src/char/int_pet.c index 489b10e3e..8f87becff 100644 --- a/src/char/int_pet.c +++ b/src/char/int_pet.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -37,7 +37,7 @@ #include <stdio.h> #include <stdlib.h> -struct inter_pet_interface inter_pet_s; +static struct inter_pet_interface inter_pet_s; struct inter_pet_interface *inter_pet; /** @@ -51,7 +51,7 @@ struct inter_pet_interface *inter_pet; * @return The ID of the saved pet. * @retval 0 in case of errors. */ -int inter_pet_tosql(const struct s_pet *p) +static int inter_pet_tosql(const struct s_pet *p) { //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`) char esc_name[NAME_LENGTH*2+1];// escaped pet name @@ -66,19 +66,19 @@ int inter_pet_tosql(const struct s_pet *p) if (p->pet_id == 0) { // New pet. if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` " - "(`class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`) " - "VALUES ('%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + "(`class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`, `autofeed`) " + "VALUES ('%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", pet_db, p->class_, esc_name, p->account_id, p->char_id, p->level, p->egg_id, - p->equip, intimate, hungry, p->rename_flag, p->incubate)) { + p->equip, intimate, hungry, p->rename_flag, p->incubate, p->autofeed)) { Sql_ShowDebug(inter->sql_handle); return 0; } pet_id = (int)SQL->LastInsertId(inter->sql_handle); } else { // Update pet. - if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `class`='%d',`name`='%s',`account_id`='%d',`char_id`='%d',`level`='%d',`egg_id`='%d',`equip`='%d',`intimate`='%d',`hungry`='%d',`rename_flag`='%d',`incubate`='%d' WHERE `pet_id`='%d'", + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `class`='%d',`name`='%s',`account_id`='%d',`char_id`='%d',`level`='%d',`egg_id`='%d',`equip`='%d',`intimate`='%d',`hungry`='%d',`rename_flag`='%d',`incubate`='%d', `autofeed`='%d' WHERE `pet_id`='%d'", pet_db, p->class_, esc_name, p->account_id, p->char_id, p->level, p->egg_id, - p->equip, intimate, hungry, p->rename_flag, p->incubate, p->pet_id)) { + p->equip, intimate, hungry, p->rename_flag, p->incubate, p->autofeed, p->pet_id)) { Sql_ShowDebug(inter->sql_handle); return 0; } @@ -91,7 +91,7 @@ int inter_pet_tosql(const struct s_pet *p) return pet_id; } -int inter_pet_fromsql(int pet_id, struct s_pet* p) +static int inter_pet_fromsql(int pet_id, struct s_pet *p) { char* data; size_t len; @@ -102,9 +102,9 @@ int inter_pet_fromsql(int pet_id, struct s_pet* p) nullpo_ret(p); memset(p, 0, sizeof(struct s_pet)); - //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`) + //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`, `autofeed`) - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate` FROM `%s` WHERE `pet_id`='%d'", pet_db, pet_id) ) + if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`,`autofeed` FROM `%s` WHERE `pet_id`='%d'", pet_db, pet_id) ) { Sql_ShowDebug(inter->sql_handle); return 0; @@ -124,6 +124,7 @@ int inter_pet_fromsql(int pet_id, struct s_pet* p) SQL->GetData(inter->sql_handle, 9, &data, NULL); p->hungry = atoi(data); SQL->GetData(inter->sql_handle, 10, &data, NULL); p->rename_flag = atoi(data); SQL->GetData(inter->sql_handle, 11, &data, NULL); p->incubate = atoi(data); + SQL->GetData(inter->sql_handle, 12, &data, NULL); p->autofeed = atoi(data); SQL->FreeResult(inter->sql_handle); @@ -137,17 +138,21 @@ int inter_pet_fromsql(int pet_id, struct s_pet* p) } //---------------------------------------------- -int inter_pet_sql_init(void) { +static int inter_pet_sql_init(void) +{ //memory alloc inter_pet->pt = (struct s_pet*)aCalloc(sizeof(struct s_pet), 1); return 0; } -void inter_pet_sql_final(void) { + +static void inter_pet_sql_final(void) +{ if (inter_pet->pt) aFree(inter_pet->pt); return; } //---------------------------------- -int inter_pet_delete(int pet_id) { +static int inter_pet_delete(int pet_id) +{ ShowInfo("delete pet request: %d...\n",pet_id); if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `pet_id`='%d'", pet_db, pet_id) ) @@ -155,74 +160,8 @@ int inter_pet_delete(int pet_id) { return 0; } //------------------------------------------------------ -int mapif_pet_created(int fd, int account_id, struct s_pet *p) -{ - WFIFOHEAD(fd, 12); - WFIFOW(fd, 0) = 0x3880; - WFIFOL(fd, 2) = account_id; - if(p!=NULL){ - WFIFOW(fd, 6) = p->class_; - WFIFOL(fd, 8) = p->pet_id; - ShowInfo("int_pet: created pet %d - %s\n", p->pet_id, p->name); - }else{ - WFIFOB(fd, 6) = 0; - WFIFOL(fd, 8) = 0; - } - WFIFOSET(fd, 12); - - return 0; -} - -int mapif_pet_info(int fd, int account_id, struct s_pet *p) -{ - nullpo_ret(p); - WFIFOHEAD(fd, sizeof(struct s_pet) + 9); - WFIFOW(fd, 0) =0x3881; - WFIFOW(fd, 2) =sizeof(struct s_pet) + 9; - WFIFOL(fd, 4) =account_id; - WFIFOB(fd, 8)=0; - memcpy(WFIFOP(fd, 9), p, sizeof(struct s_pet)); - WFIFOSET(fd, WFIFOW(fd, 2)); - - return 0; -} - -int mapif_pet_noinfo(int fd, int account_id) -{ - WFIFOHEAD(fd, sizeof(struct s_pet) + 9); - WFIFOW(fd, 0) =0x3881; - WFIFOW(fd, 2) =sizeof(struct s_pet) + 9; - WFIFOL(fd, 4) =account_id; - WFIFOB(fd, 8)=1; - memset(WFIFOP(fd, 9), 0, sizeof(struct s_pet)); - WFIFOSET(fd, WFIFOW(fd, 2)); - - return 0; -} - -int mapif_save_pet_ack(int fd, int account_id, int flag) -{ - WFIFOHEAD(fd, 7); - WFIFOW(fd, 0) =0x3882; - WFIFOL(fd, 2) =account_id; - WFIFOB(fd, 6) =flag; - WFIFOSET(fd, 7); - - return 0; -} - -int mapif_delete_pet_ack(int fd, int flag) -{ - WFIFOHEAD(fd, 3); - WFIFOW(fd, 0) =0x3883; - WFIFOB(fd, 2) =flag; - WFIFOSET(fd, 3); - - return 0; -} - -int mapif_create_pet(int fd, int account_id, int char_id, short pet_class, short pet_lv, short pet_egg_id, - short pet_equip, short intimate, short hungry, char rename_flag, char incubate, const char *pet_name) +static struct s_pet *inter_pet_create(int account_id, int char_id, short pet_class, short pet_lv, int pet_egg_id, + int pet_equip, short intimate, short hungry, char rename_flag, char incubate, const char *pet_name) { nullpo_ret(pet_name); memset(inter_pet->pt, 0, sizeof(struct s_pet)); @@ -253,90 +192,32 @@ int mapif_create_pet(int fd, int account_id, int char_id, short pet_class, short inter_pet->pt->pet_id = 0; //Signal NEW pet. if ((inter_pet->pt->pet_id = inter_pet->tosql(inter_pet->pt)) != 0) - mapif->pet_created(fd, account_id, inter_pet->pt); + return inter_pet->pt; else //Failed... - mapif->pet_created(fd, account_id, NULL); - - return 0; + return NULL; } -int mapif_load_pet(int fd, int account_id, int char_id, int pet_id) +static struct s_pet *inter_pet_load(int account_id, int char_id, int pet_id) { memset(inter_pet->pt, 0, sizeof(struct s_pet)); inter_pet->fromsql(pet_id, inter_pet->pt); if(inter_pet->pt!=NULL) { - if(inter_pet->pt->incubate == 1) { + if (inter_pet->pt->incubate == 1) { inter_pet->pt->account_id = inter_pet->pt->char_id = 0; - mapif->pet_info(fd, account_id, inter_pet->pt); + return inter_pet->pt; + } else if (account_id == inter_pet->pt->account_id && char_id == inter_pet->pt->char_id) { + return inter_pet->pt; + } else { + return NULL; } - else if(account_id == inter_pet->pt->account_id && char_id == inter_pet->pt->char_id) - mapif->pet_info(fd, account_id, inter_pet->pt); - else - mapif->pet_noinfo(fd, account_id); - } - else - mapif->pet_noinfo(fd, account_id); - - return 0; -} - -int mapif_save_pet(int fd, int account_id, const struct s_pet *data) -{ - //here process pet save request. - int len; - nullpo_ret(data); - RFIFOHEAD(fd); - len=RFIFOW(fd, 2); - if (sizeof(struct s_pet) != len-8) { - ShowError("inter pet: data size mismatch: %d != %"PRIuS"\n", len-8, sizeof(struct s_pet)); - return 0; } - inter_pet->tosql(data); - mapif->save_pet_ack(fd, account_id, 0); - - return 0; -} - -int mapif_delete_pet(int fd, int pet_id) -{ - mapif->delete_pet_ack(fd, inter_pet->delete_(pet_id)); - - return 0; + return NULL; } -int mapif_parse_CreatePet(int fd) -{ - RFIFOHEAD(fd); - mapif->create_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOW(fd, 10), RFIFOW(fd, 12), RFIFOW(fd, 14), RFIFOW(fd, 16), RFIFOW(fd, 18), - RFIFOW(fd, 20), RFIFOB(fd, 22), RFIFOB(fd, 23), RFIFOP(fd, 24)); - return 0; -} - -int mapif_parse_LoadPet(int fd) -{ - RFIFOHEAD(fd); - mapif->load_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10)); - return 0; -} - -int mapif_parse_SavePet(int fd) -{ - RFIFOHEAD(fd); - mapif->save_pet(fd, RFIFOL(fd, 4), RFIFOP(fd, 8)); - return 0; -} - -int mapif_parse_DeletePet(int fd) -{ - RFIFOHEAD(fd); - mapif->delete_pet(fd, RFIFOL(fd, 2)); - return 0; -} - -int inter_pet_parse_frommap(int fd) +static int inter_pet_parse_frommap(int fd) { RFIFOHEAD(fd); switch(RFIFOW(fd, 0)){ @@ -362,4 +243,7 @@ void inter_pet_defaults(void) inter_pet->sql_final = inter_pet_sql_final; inter_pet->delete_ = inter_pet_delete; inter_pet->parse_frommap = inter_pet_parse_frommap; + + inter_pet->create = inter_pet_create; + inter_pet->load = inter_pet_load; } diff --git a/src/char/int_pet.h b/src/char/int_pet.h index 13291e226..104771735 100644 --- a/src/char/int_pet.h +++ b/src/char/int_pet.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -36,6 +36,10 @@ struct inter_pet_interface { void (*sql_final) (void); int (*delete_) (int pet_id); int (*parse_frommap) (int fd); + + struct s_pet *(*create) (int account_id, int char_id, short pet_class, short pet_lv, int pet_egg_id, + int pet_equip, short intimate, short hungry, char rename_flag, char incubate, const char *pet_name); + struct s_pet *(*load) (int account_id, int char_id, int pet_id); }; #ifdef HERCULES_CORE diff --git a/src/char/int_quest.c b/src/char/int_quest.c index bd49cb7d4..0fa7771cb 100644 --- a/src/char/int_quest.c +++ b/src/char/int_quest.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -37,7 +37,7 @@ #include <stdio.h> #include <stdlib.h> -struct inter_quest_interface inter_quest_s; +static struct inter_quest_interface inter_quest_s; struct inter_quest_interface *inter_quest; /** @@ -48,7 +48,7 @@ struct inter_quest_interface *inter_quest; * @return Array of found entries. It has *count entries, and it is care of the * caller to aFree() it afterwards. */ -struct quest *mapif_quests_fromsql(int char_id, int *count) +static struct quest *inter_quest_fromsql(int char_id, int *count) { struct quest *questlog = NULL; struct quest tmp_quest; @@ -56,6 +56,7 @@ struct quest *mapif_quests_fromsql(int char_id, int *count) StringBuf buf; int i; int sqlerror = SQL_SUCCESS; + int quest_state = 0; if (!count) return NULL; @@ -77,11 +78,11 @@ struct quest *mapif_quests_fromsql(int char_id, int *count) memset(&tmp_quest, 0, sizeof(struct quest)); if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) - || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_quest.quest_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &tmp_quest.state, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UINT, &tmp_quest.time, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_quest.quest_id, sizeof tmp_quest.quest_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &quest_state, sizeof quest_state, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UINT, &tmp_quest.time, sizeof tmp_quest.time, NULL, NULL) ) { sqlerror = SQL_ERROR; } @@ -89,7 +90,7 @@ struct quest *mapif_quests_fromsql(int char_id, int *count) StrBuf->Destroy(&buf); for (i = 0; sqlerror != SQL_ERROR && i < MAX_QUEST_OBJECTIVES; i++) { // Stop on the first error - sqlerror = SQL->StmtBindColumn(stmt, 3+i, SQLDT_INT, &tmp_quest.count[i], 0, NULL, NULL); + sqlerror = SQL->StmtBindColumn(stmt, 3+i, SQLDT_INT, &tmp_quest.count[i], sizeof tmp_quest.count[i], NULL, NULL); } if (sqlerror == SQL_ERROR) { @@ -105,9 +106,10 @@ struct quest *mapif_quests_fromsql(int char_id, int *count) questlog = (struct quest *)aCalloc(*count, sizeof(struct quest)); while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { + tmp_quest.state = quest_state; if (i >= *count) // Sanity check, should never happen break; - memcpy(&questlog[i++], &tmp_quest, sizeof(tmp_quest)); + questlog[i++] = tmp_quest; } if (i < *count) { // Should never happen. Compact array @@ -127,7 +129,7 @@ struct quest *mapif_quests_fromsql(int char_id, int *count) * @param quest_id Quest ID * @return false in case of errors, true otherwise */ -bool mapif_quest_delete(int char_id, int quest_id) +static bool inter_quest_delete(int char_id, int quest_id) { if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `quest_id` = '%d' AND `char_id` = '%d'", quest_db, quest_id, char_id)) { Sql_ShowDebug(inter->sql_handle); @@ -144,7 +146,7 @@ bool mapif_quest_delete(int char_id, int quest_id) * @param qd Quest data * @return false in case of errors, true otherwise */ -bool mapif_quest_add(int char_id, struct quest qd) +static bool inter_quest_add(int char_id, struct quest qd) { StringBuf buf; int i; @@ -176,7 +178,7 @@ bool mapif_quest_add(int char_id, struct quest qd) * @param qd Quest data * @return false in case of errors, true otherwise */ -bool mapif_quest_update(int char_id, struct quest qd) +static bool inter_quest_update(int char_id, struct quest qd) { StringBuf buf; int i; @@ -198,34 +200,13 @@ bool mapif_quest_update(int char_id, struct quest qd) return true; } -void mapif_quest_save_ack(int fd, int char_id, bool success) +static bool inter_quest_save(int char_id, const struct quest *new_qd, int new_n) { - WFIFOHEAD(fd,7); - WFIFOW(fd,0) = 0x3861; - WFIFOL(fd,2) = char_id; - WFIFOB(fd,6) = success?1:0; - WFIFOSET(fd,7); -} - -/** - * Handles the save request from mapserver for a character's questlog. - * - * Received quests are saved, and an ack is sent back to the map server. - * - * @see inter_parse_frommap - */ -int mapif_parse_quest_save(int fd) -{ - int i, j, k, old_n, new_n = (RFIFOW(fd,2)-8)/sizeof(struct quest); - int char_id = RFIFOL(fd,4); + int i, j, k, old_n; struct quest *old_qd = NULL; - const struct quest *new_qd = NULL; bool success = true; - if (new_n > 0) - new_qd = RFIFOP(fd,8); - - old_qd = mapif->quests_fromsql(char_id, &old_n); + old_qd = inter_quest->fromsql(char_id, &old_n); for (i = 0; i < new_n; i++) { ARR_FIND( 0, old_n, j, new_qd[i].quest_id == old_qd[j].quest_id ); @@ -235,7 +216,7 @@ int mapif_parse_quest_save(int fd) // Only states and counts are changeable. ARR_FIND( 0, MAX_QUEST_OBJECTIVES, k, new_qd[i].count[k] != old_qd[j].count[k] ); if (k != MAX_QUEST_OBJECTIVES || new_qd[i].state != old_qd[j].state) - success &= mapif->quest_update(char_id, new_qd[i]); + success &= inter_quest->update(char_id, new_qd[i]); if (j < (--old_n)) { // Compact array @@ -244,59 +225,17 @@ int mapif_parse_quest_save(int fd) } } else { // Add new quests - success &= mapif->quest_add(char_id, new_qd[i]); + success &= inter_quest->add(char_id, new_qd[i]); } } for (i = 0; i < old_n; i++) // Quests not in new_qd but in old_qd are to be erased. - success &= mapif->quest_delete(char_id, old_qd[i].quest_id); + success &= inter_quest->delete(char_id, old_qd[i].quest_id); if (old_qd) aFree(old_qd); - // Send ack - mapif->quest_save_ack(fd, char_id, success); - - return 0; -} - -void mapif_send_quests(int fd, int char_id, struct quest *tmp_questlog, int num_quests) -{ - WFIFOHEAD(fd,num_quests*sizeof(struct quest)+8); - WFIFOW(fd,0) = 0x3860; - WFIFOW(fd,2) = num_quests*sizeof(struct quest)+8; - WFIFOL(fd,4) = char_id; - - if (num_quests > 0) { - nullpo_retv(tmp_questlog); - memcpy(WFIFOP(fd,8), tmp_questlog, sizeof(struct quest)*num_quests); - } - - WFIFOSET(fd,num_quests*sizeof(struct quest)+8); -} - -/** - * Sends questlog to the map server - * - * Note: Completed quests (state == Q_COMPLETE) are guaranteed to be sent last - * and the map server relies on this behavior (once the first Q_COMPLETE quest, - * all of them are considered to be Q_COMPLETE) - * - * @see inter_parse_frommap - */ -int mapif_parse_quest_load(int fd) -{ - int char_id = RFIFOL(fd,2); - struct quest *tmp_questlog = NULL; - int num_quests; - - tmp_questlog = mapif->quests_fromsql(char_id, &num_quests); - mapif->send_quests(fd, char_id, tmp_questlog, num_quests); - - if (tmp_questlog) - aFree(tmp_questlog); - - return 0; + return success; } /** @@ -304,7 +243,7 @@ int mapif_parse_quest_load(int fd) * * @see inter_parse_frommap */ -int inter_quest_parse_frommap(int fd) +static int inter_quest_parse_frommap(int fd) { switch(RFIFOW(fd,0)) { case 0x3060: mapif->parse_quest_load(fd); break; @@ -320,4 +259,9 @@ void inter_quest_defaults(void) inter_quest = &inter_quest_s; inter_quest->parse_frommap = inter_quest_parse_frommap; + inter_quest->fromsql = inter_quest_fromsql; + inter_quest->delete = inter_quest_delete; + inter_quest->add = inter_quest_add; + inter_quest->update = inter_quest_update; + inter_quest->save = inter_quest_save; } diff --git a/src/char/int_quest.h b/src/char/int_quest.h index 737213fff..e71afc561 100644 --- a/src/char/int_quest.h +++ b/src/char/int_quest.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -28,6 +28,12 @@ **/ struct inter_quest_interface { int (*parse_frommap) (int fd); + + struct quest *(*fromsql) (int char_id, int *count); + bool (*delete) (int char_id, int quest_id); + bool (*add) (int char_id, struct quest qd); + bool (*update) (int char_id, struct quest qd); + bool (*save) (int char_id, const struct quest *new_qd, int new_n); }; #ifdef HERCULES_CORE diff --git a/src/char/int_rodex.c b/src/char/int_rodex.c new file mode 100644 index 000000000..18c277574 --- /dev/null +++ b/src/char/int_rodex.c @@ -0,0 +1,432 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2017 Hercules Dev Team + * + * Hercules is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#define HERCULES_CORE + +#include "int_rodex.h" + +#include "char/char.h" +#include "char/inter.h" +#include "char/mapif.h" +#include "common/db.h" +#include "common/memmgr.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/sql.h" +#include "common/strlib.h" +#include "common/timer.h" + +#include <stdio.h> +#include <stdlib.h> + +static struct inter_rodex_interface inter_rodex_s; +struct inter_rodex_interface *inter_rodex; + +// Loads new mails of this char_id/account_id +static int inter_rodex_fromsql(int char_id, int account_id, int8 opentype, int64 mail_id, struct rodex_maillist *mails) +{ + int count = 0; + struct rodex_message msg = { 0 }; + struct SqlStmt *stmt; + + nullpo_retr(-1, mails); + + stmt = SQL->StmtMalloc(inter->sql_handle); + + switch (opentype) { + case RODEX_OPENTYPE_MAIL: + if (SQL_ERROR == SQL->StmtPrepare(stmt, + "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," + "`title`, `body`, `zeny`, `type`, `is_read`, `sender_read`, `send_date`, `expire_date`, `weight`" + "FROM `%s` WHERE `expire_date` > '%d' AND `receiver_id` = '%d' AND `mail_id` > '%"PRId64"'" + "ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), char_id, mail_id) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return -1; + } + break; + + case RODEX_OPENTYPE_ACCOUNT: + if (SQL_ERROR == SQL->StmtPrepare(stmt, + "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," + "`title`, `body`, `zeny`, `type`, `is_read`, `sender_read`, `send_date`, `expire_date`, `weight`" + "FROM `%s` WHERE " + "`expire_date` > '%d' AND `receiver_accountid` = '%d' AND `mail_id` > '%"PRId64"'" + "ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), account_id, mail_id) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return -1; + } + break; + + case RODEX_OPENTYPE_RETURN: + if (SQL_ERROR == SQL->StmtPrepare(stmt, + "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," + "`title`, `body`, `zeny`, `type`, `is_read`, `sender_read`, `send_date`, `expire_date`, `weight`" + "FROM `%s` WHERE (`is_read` = 0 AND `sender_id` = '%d' AND `expire_date` <= '%d' AND `expire_date` + '%d' > '%d' AND `mail_id` > '%"PRId64"')" + "ORDER BY `mail_id` ASC", rodex_db, char_id, (int)time(NULL), RODEX_EXPIRE, (int)time(NULL), mail_id) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return -1; + } + break; + + case RODEX_OPENTYPE_UNSET: + if (SQL_ERROR == SQL->StmtPrepare(stmt, + "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," + "`title`, `body`, `zeny`, `type`, `is_read`, `sender_read`, `send_date`, `expire_date`, `weight`" + "FROM `%s` WHERE " + "((`expire_date` > '%d' AND (`receiver_id` = '%d' OR `receiver_accountid` = '%d'))" + "OR (`is_read` = 0 AND `sender_id` = '%d' AND `expire_date` <= '%d' AND `expire_date` + '%d' > '%d'))" + "ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), char_id, account_id, char_id, (int)time(NULL), RODEX_EXPIRE, (int)time(NULL)) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return -1; + } + break; + } + + if (SQL_ERROR == SQL->StmtExecute(stmt) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT64, &msg.id, sizeof msg.id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_STRING, &msg.sender_name, sizeof msg.sender_name, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &msg.sender_id, sizeof msg.sender_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_STRING, &msg.receiver_name, sizeof msg.receiver_name, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_INT, &msg.receiver_id, sizeof msg.receiver_id, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &msg.receiver_accountid, sizeof msg.receiver_accountid, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_STRING, &msg.title, sizeof msg.title, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_STRING, &msg.body, sizeof msg.body, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_INT64, &msg.zeny, sizeof msg.zeny, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UINT8, &msg.type, sizeof msg.type, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_BOOL, &msg.is_read, sizeof msg.is_read, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_BOOL, &msg.sender_read, sizeof msg.sender_read, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_INT, &msg.send_date, sizeof msg.send_date, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_INT, &msg.expire_date, sizeof msg.expire_date, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 14, SQLDT_INT, &msg.weight, sizeof msg.weight, NULL, NULL) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return -1; + } + + { + struct item it = { 0 }; + StringBuf buf; + struct SqlStmt *stmt_items = SQL->StmtMalloc(inter->sql_handle); + int i; + + if (stmt_items == NULL) { + SQL->StmtFreeResult(stmt); + SQL->StmtFree(stmt); + return -1; + } + + StrBuf->Init(&buf); + + StrBuf->AppendStr(&buf, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`"); + for (i = 0; i < MAX_SLOTS; i++) { + StrBuf->Printf(&buf, ", `card%d`", i); + } + for (i = 0; i < MAX_ITEM_OPTIONS; i++) { + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", i, i); + } + StrBuf->Printf(&buf, "FROM `%s` WHERE mail_id = ? ORDER BY `mail_id` ASC", rodex_item_db); + + if (SQL_ERROR == SQL->StmtPrepareStr(stmt_items, StrBuf->Value(&buf)) + || SQL_ERROR == SQL->StmtBindParam(stmt_items, 0, SQLDT_INT64, &msg.id, sizeof msg.id) + ) { + SqlStmt_ShowDebug(stmt_items); + } + + // Read mails + while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { + + if (msg.type & MAIL_TYPE_ITEM) { + if (SQL_ERROR == SQL->StmtExecute(stmt_items) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 0, SQLDT_INT, &it.nameid, sizeof it.nameid, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 1, SQLDT_SHORT, &it.amount, sizeof it.amount, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 2, SQLDT_UINT, &it.equip, sizeof it.equip, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 3, SQLDT_CHAR, &it.identify, sizeof it.identify, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 4, SQLDT_CHAR, &it.refine, sizeof it.refine, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 5, SQLDT_CHAR, &it.attribute, sizeof it.attribute, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 6, SQLDT_UINT, &it.expire_time, sizeof it.expire_time, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 7, SQLDT_UCHAR, &it.bound, sizeof it.bound, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 8, SQLDT_UINT64, &it.unique_id, sizeof it.unique_id, NULL, NULL) + ) { + SqlStmt_ShowDebug(stmt_items); + } + for (i = 0; i < MAX_SLOTS; i++) { + if (SQL_ERROR == SQL->StmtBindColumn(stmt_items, 9 + i, SQLDT_INT, &it.card[i], sizeof it.card[i], NULL, NULL)) + SqlStmt_ShowDebug(stmt_items); + } + for (i = 0; i < MAX_ITEM_OPTIONS; i++) { + if (SQL_ERROR == SQL->StmtBindColumn(stmt_items, 9 + MAX_SLOTS + i * 2, SQLDT_INT16, &it.option[i].index, sizeof it.option[i].index, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 10 + MAX_SLOTS + i * 2, SQLDT_INT16, &it.option[i].value, sizeof it.option[i].value, NULL, NULL) + ) { + SqlStmt_ShowDebug(stmt_items); + } + } + + for (i = 0; i < RODEX_MAX_ITEM && SQL_SUCCESS == SQL->StmtNextRow(stmt_items); ++i) { + msg.items[i].item = it; + msg.items_count++; + } + } + + if (msg.items_count == 0) { + msg.type &= ~MAIL_TYPE_ITEM; + } + + if (msg.zeny == 0) { + msg.type &= ~MAIL_TYPE_ZENY; + } + +#if PACKETVER >= 20170419 + if (opentype == RODEX_OPENTYPE_UNSET) { + if (msg.receiver_id == 0) + msg.opentype = RODEX_OPENTYPE_ACCOUNT; + else if (msg.expire_date < time(NULL)) + msg.opentype = RODEX_OPENTYPE_RETURN; + else + msg.opentype = RODEX_OPENTYPE_MAIL; + } else { + msg.opentype = opentype; + } +#else + msg.opentype = opentype; +#endif +#if PACKETVER < 20160601 + // NPC Message Type isn't supported in old clients + msg.type &= ~MAIL_TYPE_NPC; +#endif + + ++count; + VECTOR_ENSURE(*mails, 1, 1); + VECTOR_PUSH(*mails, msg); + memset(&msg, 0, sizeof(struct rodex_message)); + + SQL->StmtFreeResult(stmt_items); + } + StrBuf->Destroy(&buf); + SQL->StmtFree(stmt_items); + } + + SQL->StmtFreeResult(stmt); + SQL->StmtFree(stmt); + + ShowInfo("rodex load complete from DB - id: %d (total: %d)\n", char_id, count); + return count; +} + +// Checks if user has non-read mails +static bool inter_rodex_hasnew(int char_id, int account_id) +{ + int count = 0; + char *data; + + if (SQL_ERROR == SQL->Query(inter->sql_handle, + "SELECT count(*) FROM `%s` WHERE (" + "(`expire_date` > '%d' AND (`receiver_id` = '%d' OR `receiver_accountid` = '%d')) OR" + "(`sender_id` = '%d' AND `expire_date` <= '%d' AND `send_date` + '%d' > '%d' AND `is_read` = 0)" // is_read is required in this line because of the OR in next condition + ") AND ((`is_read` = 0 AND `sender_read` = 0) OR (`type` > 0 AND `type` != 8))", + rodex_db, (int)time(NULL), char_id, account_id, + char_id, (int)time(NULL), 2 * RODEX_EXPIRE, (int)time(NULL)) + ) { + Sql_ShowDebug(inter->sql_handle); + return -1; + } + + if (SQL_SUCCESS != SQL->NextRow(inter->sql_handle)) + return false; + + SQL->GetData(inter->sql_handle, 0, &data, NULL); + count = atoi(data); + SQL->FreeResult(inter->sql_handle); + + return count > 0; +} + +/// Checks player name and retrieves some data +static bool inter_rodex_checkname(const char *name, int *target_char_id, short *target_class, int *target_level) +{ + char esc_name[NAME_LENGTH * 2 + 1]; + bool found = false; + + nullpo_retr(false, name); + nullpo_retr(false, target_char_id); + nullpo_retr(false, target_class); + nullpo_retr(false, target_level); + + // Try to find the Dest Char by Name + SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `char_id`, `class`, `base_level` FROM `%s` WHERE `name` = '%s'", char_db, esc_name)) { + Sql_ShowDebug(inter->sql_handle); + } else { + if (SQL_SUCCESS == SQL->NextRow(inter->sql_handle)) { + char *data; + SQL->GetData(inter->sql_handle, 0, &data, NULL); *target_char_id = atoi(data); + SQL->GetData(inter->sql_handle, 1, &data, NULL); *target_class = (short)atoi(data); + SQL->GetData(inter->sql_handle, 2, &data, NULL); *target_level = atoi(data); + found = true; + } + } + SQL->FreeResult(inter->sql_handle); + + return found; +} + +/// Stores a single message in the database. +/// Returns the message's ID if successful (or 0 if it fails). +static int64 inter_rodex_savemessage(struct rodex_message *msg) +{ + char sender_name[NAME_LENGTH * 2 + 1]; + char receiver_name[NAME_LENGTH * 2 + 1]; + char body[RODEX_BODY_LENGTH * 2 + 1]; + char title[RODEX_TITLE_LENGTH * 2 + 1]; + int i; + + nullpo_retr(false, msg); + + SQL->EscapeStringLen(inter->sql_handle, sender_name, msg->sender_name, strnlen(msg->sender_name, NAME_LENGTH)); + SQL->EscapeStringLen(inter->sql_handle, receiver_name, msg->receiver_name, strnlen(msg->receiver_name, NAME_LENGTH)); + SQL->EscapeStringLen(inter->sql_handle, body, msg->body, strnlen(msg->body, RODEX_BODY_LENGTH)); + SQL->EscapeStringLen(inter->sql_handle, title, msg->title, strnlen(msg->title, RODEX_TITLE_LENGTH)); + + if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`, `title`, `body`," + "`zeny`, `type`, `is_read`, `sender_read`, `send_date`, `expire_date`, `weight`) VALUES " + "('%s', '%d', '%s', '%d', '%d', '%s', '%s', '%"PRId64"', '%d', '%d', '%d', '%d', '%d', '%d')", + rodex_db, sender_name, msg->sender_id, receiver_name, msg->receiver_id, msg->receiver_accountid, + title, body, msg->zeny, msg->type, msg->is_read == true ? 1 : 0, msg->sender_read == true ? 1 : 0, msg->send_date, msg->expire_date, msg->weight)) { + Sql_ShowDebug(inter->sql_handle); + return 0; + } + + msg->id = (int64)SQL->LastInsertId(inter->sql_handle); + + for (i = 0; i < RODEX_MAX_ITEM; ++i) { + // Should we use statement here? [KIRIEZ] + struct item *it = &msg->items[i].item; + if (it->nameid == 0) + continue; + + if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`mail_id`, `nameid`, `amount`, `equip`, `identify`," + "`refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `opt_idx0`, `opt_val0`, `opt_idx1`, `opt_val1`, `opt_idx2`," + "`opt_val2`, `opt_idx3`, `opt_val3`, `opt_idx4`, `opt_val4`,`expire_time`, `bound`, `unique_id`) VALUES " + "('%"PRId64"', '%d', '%d', '%u', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%u', '%"PRIu64"')", + rodex_item_db, msg->id, it->nameid, it->amount, it->equip, it->identify, it->refine, it->attribute, it->card[0], it->card[1], it->card[2], it->card[3], + it->option[0].index, it->option[0].value, it->option[1].index, it->option[1].value, it->option[2].index, it->option[2].value, it->option[3].index, + it->option[3].value, it->option[4].index, it->option[4].value, it->expire_time, it->bound, it->unique_id) + ) { + Sql_ShowDebug(inter->sql_handle); + continue; + } + } + + return msg->id; +} + +/*========================================== + * Update/Delete mail + *------------------------------------------*/ +static bool inter_rodex_updatemail(int64 mail_id, int8 flag) +{ + Assert_retr(false, mail_id > 0); + Assert_retr(false, flag >= 0 && flag <= 4); + + switch (flag) { + case 0: // Read + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `is_read` = 1 WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + break; + + case 1: // Get Zeny + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `zeny` = 0, `type` = `type` & (~2) WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + break; + + case 2: // Get Items + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_item_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `zeny` = 0, `type` = `type` & (~4) WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + break; + + case 3: // Delete Mail + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_item_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + break; + + case 4: // Sender Read + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `sender_read` = 1 WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + break; + default: + return false; + } + return true; +} + +/*========================================== + * Packets From Map Server + *------------------------------------------*/ +static int inter_rodex_parse_frommap(int fd) +{ + switch(RFIFOW(fd,0)) + { + case 0x3095: mapif->parse_rodex_requestinbox(fd); break; + case 0x3096: mapif->parse_rodex_checkhasnew(fd); break; + case 0x3097: mapif->parse_rodex_updatemail(fd); break; + case 0x3098: mapif->parse_rodex_send(fd); break; + case 0x3099: mapif->parse_rodex_checkname(fd); break; + default: + return 0; + } + return 1; +} + +static int inter_rodex_sql_init(void) +{ + return 1; +} + +static void inter_rodex_sql_final(void) +{ + return; +} + +void inter_rodex_defaults(void) +{ + inter_rodex = &inter_rodex_s; + + inter_rodex->savemessage = inter_rodex_savemessage; + inter_rodex->parse_frommap = inter_rodex_parse_frommap; + inter_rodex->sql_init = inter_rodex_sql_init; + inter_rodex->sql_final = inter_rodex_sql_final; + inter_rodex->fromsql = inter_rodex_fromsql; + inter_rodex->hasnew = inter_rodex_hasnew; + inter_rodex->checkname = inter_rodex_checkname; + inter_rodex->updatemail = inter_rodex_updatemail; +} diff --git a/src/char/int_rodex.h b/src/char/int_rodex.h new file mode 100644 index 000000000..43e2d891c --- /dev/null +++ b/src/char/int_rodex.h @@ -0,0 +1,48 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2017 Hercules Dev Team + * + * Hercules is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef CHAR_INT_RODEX_H +#define CHAR_INT_RODEX_H + +#include "common/mmo.h" +#include "common/db.h" + +struct item; + +/** + * inter_rodex interface + **/ +struct inter_rodex_interface { + int (*sql_init) (void); + void (*sql_final) (void); + int (*parse_frommap) (int fd); + int (*fromsql) (int char_id, int account_id, int8 opentype, int64 mail_id, struct rodex_maillist *mails); + bool (*hasnew) (int char_id, int account_id); + bool (*checkname) (const char *name, int *target_char_id, short *target_class, int *target_level); + int64 (*savemessage) (struct rodex_message* msg); + bool (*updatemail) (int64 mail_id, int8 flag); +}; + +#ifdef HERCULES_CORE +void inter_rodex_defaults(void); +#endif // HERCULES_CORE + +HPShared struct inter_rodex_interface *inter_rodex; + +#endif /* CHAR_INT_RODEX_H */ diff --git a/src/char/int_storage.c b/src/char/int_storage.c index 8e3ebdff3..130df5515 100644 --- a/src/char/int_storage.c +++ b/src/char/int_storage.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -37,34 +37,154 @@ #include <stdio.h> #include <stdlib.h> -struct inter_storage_interface inter_storage_s; +static struct inter_storage_interface inter_storage_s; struct inter_storage_interface *inter_storage; /// Save storage data to sql -int inter_storage_tosql(int account_id, struct storage_data* p) +static int inter_storage_tosql(int account_id, const struct storage_data *p) { + int i = 0, j = 0; + bool matched_p[MAX_STORAGE] = { false }; + int delete[MAX_STORAGE] = { 0 }; + int total_deletes = 0, total_updates = 0, total_inserts = 0; + int total_changes = 0; + struct storage_data cp = { 0 }; + StringBuf buf; + nullpo_ret(p); - chr->memitemdata_to_sql(p->items, MAX_STORAGE, account_id, TABLE_STORAGE); - return 0; + + VECTOR_INIT(cp.item); + inter_storage->fromsql(account_id, &cp); + + StrBuf->Init(&buf); + + if (VECTOR_LENGTH(cp.item) > 0) { + /** + * Compare and update items, if any. + */ + for (i = 0; i < VECTOR_LENGTH(cp.item); i++) { + struct item *cp_it = &VECTOR_INDEX(cp.item, i); + struct item *p_it = NULL; + + ARR_FIND(0, VECTOR_LENGTH(p->item), j, + matched_p[j] != true + && (p_it = &VECTOR_INDEX(p->item, j)) != NULL + && cp_it->nameid == p_it->nameid + && cp_it->unique_id == p_it->unique_id + && memcmp(p_it->card, cp_it->card, sizeof(int) * MAX_SLOTS) == 0 + && memcmp(p_it->option, cp_it->option, 5 * MAX_ITEM_OPTIONS) == 0); + + if (j < VECTOR_LENGTH(p->item)) { + int k = 0; + if (memcmp(cp_it, p_it, sizeof(struct item)) != 0) { + if (total_updates == 0) { + StrBuf->Printf(&buf, "REPLACE INTO `%s` (`id`, `account_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`", storage_db); + for (k = 0; k < MAX_SLOTS; k++) + StrBuf->Printf(&buf, ", `card%d`", k); + for (k = 0; k < MAX_ITEM_OPTIONS; k++) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", k, k); + StrBuf->AppendStr(&buf, ", `expire_time`, `bound`, `unique_id`) VALUES"); + } + + StrBuf->Printf(&buf, "%s('%d', '%d', '%d', '%d', '%u', '%d', '%d', '%d'", + total_updates > 0 ? ", " : "", cp_it->id, account_id, p_it->nameid, p_it->amount, p_it->equip, p_it->identify, p_it->refine, p_it->attribute); + for (k = 0; k < MAX_SLOTS; k++) + StrBuf->Printf(&buf, ", '%d'", p_it->card[k]); + for (k = 0; k < MAX_ITEM_OPTIONS; ++k) + StrBuf->Printf(&buf, ", '%d', '%d'", p_it->option[k].index, p_it->option[k].value); + StrBuf->Printf(&buf, ", '%u', '%d', '%"PRIu64"')", p_it->expire_time, p_it->bound, p_it->unique_id); + + total_updates++; + } + matched_p[j] = true; + } else { // Does not exist, so set for deletion. + delete[total_deletes++] = cp_it->id; + } + } + + if (total_updates > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) + Sql_ShowDebug(inter->sql_handle); + + /** + * Handle deletions, if any. + */ + if (total_deletes > 0) { + StrBuf->Clear(&buf); + StrBuf->Printf(&buf, "DELETE FROM `%s` WHERE `id` IN (", storage_db); + for (i = 0; i < total_deletes; i++) { + StrBuf->Printf(&buf, "%s'%d'", i == 0 ? "" : ", ", delete[i]); + } + StrBuf->AppendStr(&buf, ");"); + + if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) + Sql_ShowDebug(inter->sql_handle); + } + } + + /** + * Check for new items. + */ + for (i = 0; i < VECTOR_LENGTH(p->item); i++) { + struct item *p_it = &VECTOR_INDEX(p->item, i); + + if (matched_p[i]) + continue; // Item is not a new entry, so lets continue. + + // Store the remaining items. + if (total_inserts == 0) { + StrBuf->Clear(&buf); + StrBuf->Printf(&buf, "INSERT INTO `%s` (`account_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", storage_db); + for (j = 0; j < MAX_SLOTS; ++j) + StrBuf->Printf(&buf, ", `card%d`", j); + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j); + StrBuf->AppendStr(&buf, ") VALUES "); + } + + StrBuf->Printf(&buf, "%s('%d', '%d', '%d', '%u', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'", + total_inserts > 0 ? ", " : "", account_id, p_it->nameid, p_it->amount, p_it->equip, p_it->identify, p_it->refine, + p_it->attribute, p_it->expire_time, p_it->bound, p_it->unique_id); + for (j = 0; j < MAX_SLOTS; ++j) + StrBuf->Printf(&buf, ", '%d'", p_it->card[j]); + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) + StrBuf->Printf(&buf, ", '%d', '%d'", p_it->option[j].index, p_it->option[j].value); + StrBuf->AppendStr(&buf, ")"); + + total_inserts++; + } + + if (total_inserts > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) + Sql_ShowDebug(inter->sql_handle); + + StrBuf->Destroy(&buf); + VECTOR_CLEAR(cp.item); + + total_changes = total_inserts + total_updates + total_deletes; + ShowInfo("storage save complete - id: %d (total: %d)\n", account_id, total_changes); + return total_changes; } /// Load storage data to mem -int inter_storage_fromsql(int account_id, struct storage_data* p) +static int inter_storage_fromsql(int account_id, struct storage_data *p) { StringBuf buf; char* data; int i; int j; + int num_rows = 0; nullpo_ret(p); - memset(p, 0, sizeof(struct storage_data)); //clean up memory - p->storage_amount = 0; + + if (VECTOR_LENGTH(p->item) > 0) + VECTOR_CLEAR(p->item); // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} StrBuf->Init(&buf); StrBuf->AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`bound`,`unique_id`"); - for( j = 0; j < MAX_SLOTS; ++j ) + for (j = 0; j < MAX_SLOTS; ++j) StrBuf->Printf(&buf, ",`card%d`", j); + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) + StrBuf->Printf(&buf, ",`opt_idx%d`,`opt_val%d`", j, j); StrBuf->Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id); if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) @@ -72,41 +192,70 @@ int inter_storage_fromsql(int account_id, struct storage_data* p) StrBuf->Destroy(&buf); - for (i = 0; i < MAX_STORAGE && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i) { - struct item *item = &p->items[i]; - SQL->GetData(inter->sql_handle, 0, &data, NULL); item->id = atoi(data); - SQL->GetData(inter->sql_handle, 1, &data, NULL); item->nameid = atoi(data); - SQL->GetData(inter->sql_handle, 2, &data, NULL); item->amount = atoi(data); - SQL->GetData(inter->sql_handle, 3, &data, NULL); item->equip = atoi(data); - SQL->GetData(inter->sql_handle, 4, &data, NULL); item->identify = atoi(data); - SQL->GetData(inter->sql_handle, 5, &data, NULL); item->refine = atoi(data); - SQL->GetData(inter->sql_handle, 6, &data, NULL); item->attribute = atoi(data); - SQL->GetData(inter->sql_handle, 7, &data, NULL); item->expire_time = (unsigned int)atoi(data); - SQL->GetData(inter->sql_handle, 8, &data, NULL); item->bound = atoi(data); - SQL->GetData(inter->sql_handle, 9, &data, NULL); item->unique_id = strtoull(data, NULL, 10); - for( j = 0; j < MAX_SLOTS; ++j ) - { - SQL->GetData(inter->sql_handle, 10+j, &data, NULL); item->card[j] = atoi(data); + if ((num_rows = (int)SQL->NumRows(inter->sql_handle)) != 0) { + + VECTOR_ENSURE(p->item, num_rows > MAX_STORAGE ? MAX_STORAGE : num_rows, 1); + + for (i = 0; i < MAX_STORAGE && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i) { + struct item item = { 0 }; + SQL->GetData(inter->sql_handle, 0, &data, NULL); item.id = atoi(data); + SQL->GetData(inter->sql_handle, 1, &data, NULL); item.nameid = atoi(data); + SQL->GetData(inter->sql_handle, 2, &data, NULL); item.amount = atoi(data); + SQL->GetData(inter->sql_handle, 3, &data, NULL); item.equip = atoi(data); + SQL->GetData(inter->sql_handle, 4, &data, NULL); item.identify = atoi(data); + SQL->GetData(inter->sql_handle, 5, &data, NULL); item.refine = atoi(data); + SQL->GetData(inter->sql_handle, 6, &data, NULL); item.attribute = atoi(data); + SQL->GetData(inter->sql_handle, 7, &data, NULL); item.expire_time = (unsigned int)atoi(data); + SQL->GetData(inter->sql_handle, 8, &data, NULL); item.bound = atoi(data); + SQL->GetData(inter->sql_handle, 9, &data, NULL); item.unique_id = strtoull(data, NULL, 10); + + /* Card Slots */ + for (j = 0; j < MAX_SLOTS; ++j) { + SQL->GetData(inter->sql_handle, 10 + j, &data, NULL); + item.card[j] = atoi(data); + } + + /* Item Options */ + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) { + SQL->GetData(inter->sql_handle, 10 + MAX_SLOTS + j * 2, &data, NULL); + item.option[j].index = atoi(data); + SQL->GetData(inter->sql_handle, 11 + MAX_SLOTS + j * 2, &data, NULL); + item.option[j].value = atoi(data); + } + + VECTOR_PUSH(p->item, item); } } - p->storage_amount = i; + SQL->FreeResult(inter->sql_handle); - ShowInfo("storage load complete from DB - id: %d (total: %d)\n", account_id, p->storage_amount); - return 1; + ShowInfo("storage load complete from DB - id: %d (total: %d)\n", account_id, VECTOR_LENGTH(p->item)); + + return VECTOR_LENGTH(p->item); } /// Save guild_storage data to sql -int inter_storage_guild_storage_tosql(int guild_id, const struct guild_storage *p) +static bool inter_storage_guild_storage_tosql(int guild_id, const struct guild_storage *p) { nullpo_ret(p); - chr->memitemdata_to_sql(p->items, MAX_GUILD_STORAGE, guild_id, TABLE_GUILD_STORAGE); + + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", guild_db, guild_id)) { + Sql_ShowDebug(inter->sql_handle); + return false; + } else if (SQL->NumRows(inter->sql_handle) < 1) { + // guild doesn't exist + SQL->FreeResult(inter->sql_handle); + return false; + } + SQL->FreeResult(inter->sql_handle); + + chr->memitemdata_to_sql(p->items, guild_id, TABLE_GUILD_STORAGE); ShowInfo ("guild storage save to DB - guild: %d\n", guild_id); - return 0; + return true; } /// Load guild_storage data to mem -int inter_storage_guild_storage_fromsql(int guild_id, struct guild_storage* p) +static int inter_storage_guild_storage_fromsql(int guild_id, struct guild_storage* p) { StringBuf buf; char* data; @@ -121,8 +270,10 @@ int inter_storage_guild_storage_fromsql(int guild_id, struct guild_storage* p) // storage {`guild_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} StrBuf->Init(&buf); StrBuf->AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`bound`,`unique_id`"); - for( j = 0; j < MAX_SLOTS; ++j ) + for (j = 0; j < MAX_SLOTS; ++j) StrBuf->Printf(&buf, ",`card%d`", j); + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j); StrBuf->Printf(&buf, " FROM `%s` WHERE `guild_id`='%d' ORDER BY `nameid`", guild_storage_db, guild_id); if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) @@ -142,9 +293,17 @@ int inter_storage_guild_storage_fromsql(int guild_id, struct guild_storage* p) SQL->GetData(inter->sql_handle, 7, &data, NULL); item->bound = atoi(data); SQL->GetData(inter->sql_handle, 8, &data, NULL); item->unique_id = strtoull(data, NULL, 10); item->expire_time = 0; - - for( j = 0; j < MAX_SLOTS; ++j ) { - SQL->GetData(inter->sql_handle, 9+j, &data, NULL); item->card[j] = atoi(data); + /* Card Slots */ + for (j = 0; j < MAX_SLOTS; ++j) { + SQL->GetData(inter->sql_handle, 9 + j, &data, NULL); + item->card[j] = atoi(data); + } + /* Item Options */ + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) { + SQL->GetData(inter->sql_handle, 9 + MAX_SLOTS + j * 2, &data, NULL); + item->option[j].index = atoi(data); + SQL->GetData(inter->sql_handle, 10 + MAX_SLOTS + j * 2, &data, NULL); + item->option[j].value = atoi(data); } } p->storage_amount = i; @@ -156,124 +315,35 @@ int inter_storage_guild_storage_fromsql(int guild_id, struct guild_storage* p) //--------------------------------------------------------- // storage data initialize -int inter_storage_sql_init(void) +static int inter_storage_sql_init(void) { return 1; } // storage data finalize -void inter_storage_sql_final(void) +static void inter_storage_sql_final(void) { return; } // q?f[^? -int inter_storage_delete(int account_id) +static int inter_storage_delete(int account_id) { if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `account_id`='%d'", storage_db, account_id) ) Sql_ShowDebug(inter->sql_handle); return 0; } -int inter_storage_guild_storage_delete(int guild_id) +static int inter_storage_guild_storage_delete(int guild_id) { if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `guild_id`='%d'", guild_storage_db, guild_id) ) Sql_ShowDebug(inter->sql_handle); return 0; } -//--------------------------------------------------------- -// packet from map server - -int mapif_load_guild_storage(int fd, int account_id, int guild_id, char flag) -{ - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", guild_db, guild_id) ) - Sql_ShowDebug(inter->sql_handle); - else if( SQL->NumRows(inter->sql_handle) > 0 ) - {// guild exists - WFIFOHEAD(fd, sizeof(struct guild_storage)+13); - WFIFOW(fd,0) = 0x3818; - WFIFOW(fd,2) = sizeof(struct guild_storage)+13; - WFIFOL(fd,4) = account_id; - WFIFOL(fd,8) = guild_id; - WFIFOB(fd,12) = flag; //1 open storage, 0 don't open - inter_storage->guild_storage_fromsql(guild_id, WFIFOP(fd,13)); - WFIFOSET(fd, WFIFOW(fd,2)); - return 0; - } - // guild does not exist - SQL->FreeResult(inter->sql_handle); - WFIFOHEAD(fd, 12); - WFIFOW(fd,0) = 0x3818; - WFIFOW(fd,2) = 12; - WFIFOL(fd,4) = account_id; - WFIFOL(fd,8) = 0; - WFIFOSET(fd, 12); - return 0; -} -int mapif_save_guild_storage_ack(int fd, int account_id, int guild_id, int fail) -{ - WFIFOHEAD(fd,11); - WFIFOW(fd,0)=0x3819; - WFIFOL(fd,2)=account_id; - WFIFOL(fd,6)=guild_id; - WFIFOB(fd,10)=fail; - WFIFOSET(fd,11); - return 0; -} - -//--------------------------------------------------------- -// packet from map server - -int mapif_parse_LoadGuildStorage(int fd) -{ - RFIFOHEAD(fd); - mapif->load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6),1); - return 0; -} - -int mapif_parse_SaveGuildStorage(int fd) -{ - int guild_id; - int len; - - RFIFOHEAD(fd); - guild_id = RFIFOL(fd,8); - len = RFIFOW(fd,2); - - if (sizeof(struct guild_storage) != len - 12) { - ShowError("inter storage: data size mismatch: %d != %"PRIuS"\n", len - 12, sizeof(struct guild_storage)); - } else { - if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", guild_db, guild_id)) { - Sql_ShowDebug(inter->sql_handle); - } else if(SQL->NumRows(inter->sql_handle) > 0) { - // guild exists - SQL->FreeResult(inter->sql_handle); - inter_storage->guild_storage_tosql(guild_id, RFIFOP(fd,12)); - mapif->save_guild_storage_ack(fd, RFIFOL(fd,4), guild_id, 0); - return 0; - } - SQL->FreeResult(inter->sql_handle); - } - mapif->save_guild_storage_ack(fd, RFIFOL(fd,4), guild_id, 1); - return 0; -} - -int mapif_itembound_ack(int fd, int aid, int guild_id) -{ -#ifdef GP_BOUND_ITEMS - WFIFOHEAD(fd,8); - WFIFOW(fd,0) = 0x3856; - WFIFOL(fd,2) = aid;/* the value is not being used, drop? */ - WFIFOW(fd,6) = guild_id; - WFIFOSET(fd,8); -#endif - return 0; -} - //------------------------------------------------ //Guild bound items pull for offline characters [Akinari] //Revised by [Mhalicot] //------------------------------------------------ -int mapif_parse_ItemBoundRetrieve_sub(int fd) +static bool inter_storage_retrieve_bound_items(int char_id, int account_id, int guild_id) { #ifdef GP_BOUND_ITEMS StringBuf buf; @@ -282,14 +352,13 @@ int mapif_parse_ItemBoundRetrieve_sub(int fd) int j, i=0, s=0, bound_qt=0; struct item items[MAX_INVENTORY]; unsigned int bound_item[MAX_INVENTORY] = {0}; - int char_id = RFIFOL(fd,2); - int aid = RFIFOL(fd,6); - int guild_id = RFIFOW(fd,10); StrBuf->Init(&buf); StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`"); - for( j = 0; j < MAX_SLOTS; ++j ) + for (j = 0; j < MAX_SLOTS; ++j) StrBuf->Printf(&buf, ", `card%d`", j); + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j); StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`='%d' AND `bound` = '%d'",inventory_db,char_id,IBT_GUILD); stmt = SQL->StmtMalloc(inter->sql_handle); @@ -299,34 +368,39 @@ int mapif_parse_ItemBoundRetrieve_sub(int fd) Sql_ShowDebug(inter->sql_handle); SQL->StmtFree(stmt); StrBuf->Destroy(&buf); - return 1; + return false; } memset(&item, 0, sizeof(item)); - SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &item.equip, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &item.identify, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &item.bound, 0, NULL, NULL); - SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &item.unique_id, 0, NULL, NULL); - for( j = 0; j < MAX_SLOTS; ++j ) - SQL->StmtBindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); - - while( SQL_SUCCESS == SQL->StmtNextRow(stmt)) { + SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &item.id, sizeof item.id, NULL, NULL); + SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &item.nameid, sizeof item.nameid, NULL, NULL); + SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &item.amount, sizeof item.amount, NULL, NULL); + SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &item.equip, sizeof item.equip, NULL, NULL); + SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &item.identify, sizeof item.identify, NULL, NULL); + SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, sizeof item.refine, NULL, NULL); + SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, sizeof item.attribute, NULL, NULL); + SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, sizeof item.expire_time, NULL, NULL); + SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &item.bound, sizeof item.bound, NULL, NULL); + SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &item.unique_id, sizeof item.unique_id, NULL, NULL); + /* Card Slots */ + for (j = 0; j < MAX_SLOTS; ++j) + SQL->StmtBindColumn(stmt, 10 + j, SQLDT_INT, &item.card[j], sizeof item.card[j], NULL, NULL); + /* Item Options */ + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) { + SQL->StmtBindColumn(stmt, 10 + MAX_SLOTS + j * 2, SQLDT_INT16, &item.option[j].index, sizeof item.option[j].index, NULL, NULL); + SQL->StmtBindColumn(stmt, 11 + MAX_SLOTS + j * 2, SQLDT_INT16, &item.option[j].value, sizeof item.option[j].value, NULL, NULL); + } + while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { Assert_retb(i < MAX_INVENTORY); memcpy(&items[i],&item,sizeof(struct item)); i++; } SQL->FreeResult(inter->sql_handle); - if(!i) { //No items found - No need to continue + if (i == 0) { //No items found - No need to continue StrBuf->Destroy(&buf); SQL->StmtFree(stmt); - return 0; + return true; } //First we delete the character's items @@ -359,7 +433,7 @@ int mapif_parse_ItemBoundRetrieve_sub(int fd) Sql_ShowDebug(inter->sql_handle); SQL->StmtFree(stmt); StrBuf->Destroy(&buf); - return 1; + return false; } // Removes any view id that was set by an item that was removed @@ -393,7 +467,7 @@ int mapif_parse_ItemBoundRetrieve_sub(int fd) Sql_ShowDebug(inter->sql_handle); SQL->StmtFree(stmt); StrBuf->Destroy(&buf); - return 1; + return false; } #undef CHECK_REMOVE } @@ -408,54 +482,47 @@ int mapif_parse_ItemBoundRetrieve_sub(int fd) StrBuf->Printf(&buf,"INSERT INTO `%s` (`guild_id`,`nameid`,`amount`,`equip`,`identify`,`refine`," "`attribute`,`expire_time`,`bound`,`unique_id`", guild_storage_db); - for( s = 0; s < MAX_SLOTS; ++s ) + for (s = 0; s < MAX_SLOTS; ++s) StrBuf->Printf(&buf, ", `card%d`", s); + for (s = 0; s < MAX_ITEM_OPTIONS; ++s) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", s, s); StrBuf->AppendStr(&buf," ) VALUES "); - for( j = 0; j < i; ++j ) { - if( j ) + for (j = 0; j < i; ++j) { + if (j != 0) StrBuf->AppendStr(&buf, ","); StrBuf->Printf(&buf, "('%d', '%d', '%d', '%u', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'", guild_id, items[j].nameid, items[j].amount, items[j].equip, items[j].identify, items[j].refine, items[j].attribute, items[j].expire_time, items[j].bound, items[j].unique_id); - for( s = 0; s < MAX_SLOTS; ++s ) + for (s = 0; s < MAX_SLOTS; ++s) StrBuf->Printf(&buf, ", '%d'", items[j].card[s]); + for (s = 0; s < MAX_ITEM_OPTIONS; ++s) + StrBuf->Printf(&buf, ", '%d', '%d'", items[j].option[s].index, items[j].option[s].value); StrBuf->AppendStr(&buf, ")"); } - if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) - || SQL_ERROR == SQL->StmtExecute(stmt) ) + if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { Sql_ShowDebug(inter->sql_handle); SQL->StmtFree(stmt); StrBuf->Destroy(&buf); - return 1; + return false; } StrBuf->Destroy(&buf); SQL->StmtFree(stmt); - - //Finally reload storage and tell map we're done - mapif->load_guild_storage(fd,aid,guild_id,0); - - // If character is logged in char, disconnect - chr->disconnect_player(aid); #endif - return 0; -} - -void mapif_parse_ItemBoundRetrieve(int fd) -{ - mapif->parse_ItemBoundRetrieve_sub(fd); - /* tell map server the operation is over and it can unlock the storage */ - mapif->itembound_ack(fd,RFIFOL(fd,6),RFIFOW(fd,10)); + return true; } -int inter_storage_parse_frommap(int fd) +static int inter_storage_parse_frommap(int fd) { RFIFOHEAD(fd); switch(RFIFOW(fd,0)){ + case 0x3010: mapif->pAccountStorageLoad(fd); break; + case 0x3011: mapif->pAccountStorageSave(fd); break; case 0x3018: mapif->parse_LoadGuildStorage(fd); break; case 0x3019: mapif->parse_SaveGuildStorage(fd); break; #ifdef GP_BOUND_ITEMS @@ -480,4 +547,5 @@ void inter_storage_defaults(void) inter_storage->delete_ = inter_storage_delete; inter_storage->guild_storage_delete = inter_storage_guild_storage_delete; inter_storage->parse_frommap = inter_storage_parse_frommap; + inter_storage->retrieve_bound_items = inter_storage_retrieve_bound_items; } diff --git a/src/char/int_storage.h b/src/char/int_storage.h index 8c6341e85..918927620 100644 --- a/src/char/int_storage.h +++ b/src/char/int_storage.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -21,6 +21,7 @@ #ifndef CHAR_INT_STORAGE_H #define CHAR_INT_STORAGE_H +#include "common/db.h" #include "common/hercules.h" struct storage_data; @@ -30,15 +31,16 @@ struct guild_storage; * inter_storage interface **/ struct inter_storage_interface { - int (*tosql) (int account_id, struct storage_data* p); - int (*fromsql) (int account_id, struct storage_data* p); - int (*guild_storage_tosql) (int guild_id, const struct guild_storage *p); + int (*tosql) (int account_id, const struct storage_data *p); + int (*fromsql) (int account_id, struct storage_data *p); + bool (*guild_storage_tosql) (int guild_id, const struct guild_storage *p); int (*guild_storage_fromsql) (int guild_id, struct guild_storage* p); int (*sql_init) (void); void (*sql_final) (void); int (*delete_) (int account_id); int (*guild_storage_delete) (int guild_id); int (*parse_frommap) (int fd); + bool (*retrieve_bound_items) (int char_id, int account_id, int guild_id); }; #ifdef HERCULES_CORE diff --git a/src/char/inter.c b/src/char/inter.c index d076bb657..418c9b0a1 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -25,6 +25,7 @@ #include "char/char.h" #include "char/geoip.h" #include "char/int_auction.h" +#include "char/int_clan.h" #include "char/int_elemental.h" #include "char/int_guild.h" #include "char/int_homun.h" @@ -33,7 +34,9 @@ #include "char/int_party.h" #include "char/int_pet.h" #include "char/int_quest.h" +#include "char/int_rodex.h" #include "char/int_storage.h" +#include "char/int_achievement.h" #include "char/mapif.h" #include "common/cbasetypes.h" #include "common/conf.h" @@ -53,44 +56,40 @@ #define WISDATA_TTL (60*1000) // Wis data Time To Live (60 seconds) #define WISDELLIST_MAX 256 // Number of elements in the list Delete data Wis -struct inter_interface inter_s; +static struct inter_interface inter_s; struct inter_interface *inter; -int char_server_port = 3306; -char char_server_ip[32] = "127.0.0.1"; -char char_server_id[32] = "ragnarok"; -char char_server_pw[100] = "ragnarok"; -char char_server_db[32] = "ragnarok"; -char default_codepage[32] = ""; //Feature by irmin. +static int char_server_port = 3306; +static char char_server_ip[32] = "127.0.0.1"; +static char char_server_id[32] = "ragnarok"; +static char char_server_pw[100] = "ragnarok"; +static char char_server_db[32] = "ragnarok"; +static char default_codepage[32] = ""; //Feature by irmin. int party_share_level = 10; // recv. packet list -int inter_recv_packet_length[] = { +static int inter_recv_packet_length[] = { -1,-1, 7,-1, -1,13,36, (2 + 4 + 4 + 4 + NAME_LENGTH), 0, 0, 0, 0, 0, 0, 0, 0, // 3000- - 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- + 6,-1, 6,-1, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- Account Storage, Achievements [Smokexyz] -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- - -1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- + -1, 9, 0, 0, 10,10, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- Clan System(3044-3045) -1,-1,10,10, 0,-1,12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus], Item Bound [Mhalicot] 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish] -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil] - 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080- - -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator] + 52,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080- + -1,10,-1, 6, 0, 20,10,11, -1,6 + NAME_LENGTH, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator], RoDEX packets }; -struct WisData { - int id, fd, count, len; - int64 tick; - unsigned char src[24], dst[24], msg[512]; -}; static struct DBMap *wis_db = NULL; // int wis_id -> struct WisData* static int wis_dellist[WISDELLIST_MAX], wis_delnum; #define MAX_JOB_NAMES 150 -static char* msg_table[MAX_JOB_NAMES]; // messages 550 ~ 699 are job names +static char *msg_table[MAX_JOB_NAMES]; // messages 550 ~ 699 are job names -const char* inter_msg_txt(int msg_number) { +static const char *inter_msg_txt(int msg_number) +{ msg_number -= 550; if (msg_number >= 0 && msg_number < MAX_JOB_NAMES && msg_table[msg_number] != NULL && msg_table[msg_number][0] != '\0') @@ -109,7 +108,7 @@ const char* inter_msg_txt(int msg_number) { * @param[in] allow_override whether to allow duplicate message IDs to override the original value. * @return success state. */ -bool inter_msg_config_read(const char *cfg_name, bool allow_override) +static bool inter_msg_config_read(const char *cfg_name, bool allow_override) { int msg_number; char line[1024], w1[1024], w2[1024]; @@ -161,14 +160,14 @@ bool inter_msg_config_read(const char *cfg_name, bool allow_override) /*========================================== * Cleanup Message Data *------------------------------------------*/ -void inter_do_final_msg(void) +static void inter_do_final_msg(void) { int i; for (i = 0; i < MAX_JOB_NAMES; i++) aFree(msg_table[i]); } /* from pc.c due to @accinfo. any ideas to replace this crap are more than welcome. */ -const char* inter_job_name(int class) +static const char *inter_job_name(int class) { switch (class) { case JOB_NOVICE: // 550 @@ -401,7 +400,7 @@ const char* inter_job_name(int class) * Argument-list version of inter_msg_to_fd * @see inter_msg_to_fd */ -void inter_vmsg_to_fd(int fd, int u_fd, int aid, char* msg, va_list ap) +static void inter_vmsg_to_fd(int fd, int u_fd, int aid, char *msg, va_list ap) { char msg_out[512]; va_list apcopy; @@ -434,8 +433,8 @@ void inter_vmsg_to_fd(int fd, int u_fd, int aid, char* msg, va_list ap) * @param msg Message format string * @param ... Additional parameters for (v)sprinf */ -void inter_msg_to_fd(int fd, int u_fd, int aid, char *msg, ...) __attribute__((format(printf, 4, 5))); -void inter_msg_to_fd(int fd, int u_fd, int aid, char *msg, ...) +static void inter_msg_to_fd(int fd, int u_fd, int aid, char *msg, ...) __attribute__((format(printf, 4, 5))); +static void inter_msg_to_fd(int fd, int u_fd, int aid, char *msg, ...) { va_list ap; va_start(ap,msg); @@ -444,15 +443,12 @@ void inter_msg_to_fd(int fd, int u_fd, int aid, char *msg, ...) } /* [Dekamaster/Nightroad] */ -void mapif_parse_accinfo(int fd) +static void inter_accinfo(int u_fd, int aid, int castergroup, const char *query, int map_fd) { - int u_fd = RFIFOL(fd,2), aid = RFIFOL(fd,6), castergroup = RFIFOL(fd,10); - char query[NAME_LENGTH], query_esq[NAME_LENGTH*2+1]; + char query_esq[NAME_LENGTH*2+1]; int account_id; char *data; - safestrncpy(query, RFIFOP(fd,14), NAME_LENGTH); - SQL->EscapeString(inter->sql_handle, query_esq, query); account_id = atoi(query); @@ -462,10 +458,10 @@ void mapif_parse_accinfo(int fd) if ( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`,`name`,`class`,`base_level`,`job_level`,`online` FROM `%s` WHERE `name` LIKE '%s' LIMIT 10", char_db, query_esq) || SQL->NumRows(inter->sql_handle) == 0 ) { if( SQL->NumRows(inter->sql_handle) == 0 ) { - inter->msg_to_fd(fd, u_fd, aid, "No matches were found for your criteria, '%s'",query); + inter->msg_to_fd(map_fd, u_fd, aid, "No matches were found for your criteria, '%s'",query); } else { Sql_ShowDebug(inter->sql_handle); - inter->msg_to_fd(fd, u_fd, aid, "An error occurred, bother your admin about it."); + inter->msg_to_fd(map_fd, u_fd, aid, "An error occurred, bother your admin about it."); } SQL->FreeResult(inter->sql_handle); return; @@ -475,7 +471,7 @@ void mapif_parse_accinfo(int fd) SQL->GetData(inter->sql_handle, 0, &data, NULL); account_id = atoi(data); SQL->FreeResult(inter->sql_handle); } else {// more than one, listing... [Dekamaster/Nightroad] - inter->msg_to_fd(fd, u_fd, aid, "Your query returned the following %d results, please be more specific...",(int)SQL->NumRows(inter->sql_handle)); + inter->msg_to_fd(map_fd, u_fd, aid, "Your query returned the following %d results, please be more specific...",(int)SQL->NumRows(inter->sql_handle)); while ( SQL_SUCCESS == SQL->NextRow(inter->sql_handle) ) { int class; int base_level, job_level, online; @@ -488,7 +484,7 @@ void mapif_parse_accinfo(int fd) SQL->GetData(inter->sql_handle, 4, &data, NULL); job_level = atoi(data); SQL->GetData(inter->sql_handle, 5, &data, NULL); online = atoi(data); - inter->msg_to_fd(fd, u_fd, aid, "[AID: %d] %s | %s | Level: %d/%d | %s", account_id, name, inter->job_name(class), base_level, job_level, online?"Online":"Offline"); + inter->msg_to_fd(map_fd, u_fd, aid, "[AID: %d] %s | %s | Level: %d/%d | %s", account_id, name, inter->job_name(class), base_level, job_level, online?"Online":"Offline"); } SQL->FreeResult(inter->sql_handle); return; @@ -499,12 +495,13 @@ void mapif_parse_accinfo(int fd) /* it will only get here if we have a single match */ /* and we will send packet with account id to login server asking for account info */ if( account_id ) { - mapif->on_parse_accinfo(account_id, u_fd, aid, castergroup, fd); + mapif->on_parse_accinfo(account_id, u_fd, aid, castergroup, map_fd); } return; } -void mapif_parse_accinfo2(bool success, int map_fd, int u_fd, int u_aid, int account_id, const char *userid, const char *user_pass, + +static void inter_accinfo2(bool success, int map_fd, int u_fd, int u_aid, int account_id, const char *userid, const char *user_pass, const char *email, const char *last_ip, const char *lastlogin, const char *pin_code, const char *birthdate, int group_id, int logincount, int state) { @@ -525,12 +522,15 @@ void mapif_parse_accinfo2(bool success, int map_fd, int u_fd, int u_aid, int acc inter->msg_to_fd(map_fd, u_fd, u_aid, "-- Account %d --", account_id); inter->msg_to_fd(map_fd, u_fd, u_aid, "User: %s | GM Group: %d | State: %d", userid, group_id, state); +// enable this if you really know what you doing. +#if 0 if (*user_pass != '\0') { /* password is only received if your gm level is greater than the one you're searching for */ if (pin_code && *pin_code != '\0') inter->msg_to_fd(map_fd, u_fd, u_aid, "Password: %s (PIN:%s)", user_pass, pin_code); else inter->msg_to_fd(map_fd, u_fd, u_aid, "Password: %s", user_pass ); } +#endif inter->msg_to_fd(map_fd, u_fd, u_aid, "Account e-mail: %s | Birthdate: %s", email, birthdate); inter->msg_to_fd(map_fd, u_fd, u_aid, "Last IP: %s (%s)", last_ip, geoip->getcountry(sockt->str2ip(last_ip))); @@ -574,7 +574,7 @@ void mapif_parse_accinfo2(bool success, int map_fd, int u_fd, int u_aid, int acc * @param val either str or int, depending on type * @param type false when int, true otherwise **/ -void inter_savereg(int account_id, int char_id, const char *key, unsigned int index, intptr_t val, bool is_string) +static void inter_savereg(int account_id, int char_id, const char *key, unsigned int index, intptr_t val, bool is_string) { char val_esq[1000]; nullpo_retv(key); @@ -627,7 +627,7 @@ void inter_savereg(int account_id, int char_id, const char *key, unsigned int in } // Load account_reg from sql (type=2) -int inter_accreg_fromsql(int account_id,int char_id, int fd, int type) +static int inter_accreg_fromsql(int account_id, int char_id, int fd, int type) { char* data; size_t len; @@ -804,7 +804,7 @@ int inter_accreg_fromsql(int account_id,int char_id, int fd, int type) * * @retval false in case of error. */ -bool inter_config_read_log(const char *filename, const struct config_t *config, bool imported) +static bool inter_config_read_log(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -832,7 +832,7 @@ bool inter_config_read_log(const char *filename, const struct config_t *config, * * @retval false in case of error. */ -bool inter_config_read_connection(const char *filename, const struct config_t *config, bool imported) +static bool inter_config_read_connection(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; @@ -865,7 +865,7 @@ bool inter_config_read_connection(const char *filename, const struct config_t *c * * @retval false in case of error. */ -bool inter_config_read(const char *filename, bool imported) +static bool inter_config_read(const char *filename, bool imported) { struct config_t config; const struct config_setting_t *setting = NULL; @@ -909,7 +909,7 @@ bool inter_config_read(const char *filename, bool imported) * Save interlog into sql (arglist version) * @see inter_log */ -int inter_vlog(char* fmt, va_list ap) +static int inter_vlog(char *fmt, va_list ap) { char str[255]; char esc_str[sizeof(str)*2+1];// escaped str @@ -932,7 +932,7 @@ int inter_vlog(char* fmt, va_list ap) * @param ... Additional (printf-like) arguments * @return Always 0 // FIXME */ -int inter_log(char* fmt, ...) +static int inter_log(char *fmt, ...) { va_list ap; int ret; @@ -945,7 +945,7 @@ int inter_log(char* fmt, ...) } // initialize -int inter_init_sql(const char *file) +static int inter_init_sql(const char *file) { inter->config_read(file, false); @@ -974,6 +974,8 @@ int inter_init_sql(const char *file) inter_elemental->sql_init(); inter_mail->sql_init(); inter_auction->sql_init(); + inter_rodex->sql_init(); + inter_achievement->sql_init(); geoip->init(); inter->msg_config_read("conf/messages.conf", false); @@ -981,7 +983,7 @@ int inter_init_sql(const char *file) } // finalize -void inter_final(void) +static void inter_final(void) { wis_db->destroy(wis_db, NULL); @@ -994,120 +996,26 @@ void inter_final(void) inter_elemental->sql_final(); inter_mail->sql_final(); inter_auction->sql_final(); + inter_rodex->sql_final(); + inter_achievement->sql_final(); geoip->final(true); inter->do_final_msg(); return; } -int inter_mapif_init(int fd) -{ - return 0; -} - - -//-------------------------------------------------------- - -// broadcast sending -int mapif_broadcast(const unsigned char *mes, int len, unsigned int fontColor, short fontType, short fontSize, short fontAlign, short fontY, int sfd) -{ - unsigned char *buf = (unsigned char*)aMalloc((len)*sizeof(unsigned char)); - - nullpo_ret(mes); - Assert_ret(len >= 16); - WBUFW(buf,0) = 0x3800; - WBUFW(buf,2) = len; - WBUFL(buf,4) = fontColor; - WBUFW(buf,8) = fontType; - WBUFW(buf,10) = fontSize; - WBUFW(buf,12) = fontAlign; - WBUFW(buf,14) = fontY; - memcpy(WBUFP(buf,16), mes, len - 16); - mapif->sendallwos(sfd, buf, len); - - aFree(buf); - return 0; -} - -// Wis sending -int mapif_wis_message(struct WisData *wd) -{ - unsigned char buf[2048]; - nullpo_ret(wd); - //if (wd->len > 2047-56) wd->len = 2047-56; //Force it to fit to avoid crashes. [Skotlex] - if (wd->len < 0) - wd->len = 0; - if (wd->len >= (int)sizeof(wd->msg) - 1) - wd->len = (int)sizeof(wd->msg) - 1; - - WBUFW(buf, 0) = 0x3801; - WBUFW(buf, 2) = 56 +wd->len; - WBUFL(buf, 4) = wd->id; - memcpy(WBUFP(buf, 8), wd->src, NAME_LENGTH); - memcpy(WBUFP(buf,32), wd->dst, NAME_LENGTH); - memcpy(WBUFP(buf,56), wd->msg, wd->len); - wd->count = mapif->sendall(buf,WBUFW(buf,2)); - - return 0; -} - -void mapif_wis_response(int fd, const unsigned char *src, int flag) -{ - unsigned char buf[27]; - nullpo_retv(src); - WBUFW(buf, 0)=0x3802; - memcpy(WBUFP(buf, 2),src,24); - WBUFB(buf,26)=flag; - mapif->send(fd,buf,27); -} - -// Wis sending result -int mapif_wis_end(struct WisData *wd, int flag) +static int inter_mapif_init(int fd) { - nullpo_ret(wd); - mapif->wis_response(wd->fd, wd->src, flag); return 0; } -#if 0 -// Account registry transfer to map-server -static void mapif_account_reg(int fd, unsigned char *src) -{ - nullpo_retv(src); - WBUFW(src,0)=0x3804; //NOTE: writing to RFIFO - mapif->sendallwos(fd, src, WBUFW(src,2)); -} -#endif // 0 - -// Send the requested account_reg -int mapif_account_reg_reply(int fd,int account_id,int char_id, int type) -{ - inter->accreg_fromsql(account_id,char_id,fd,type); - return 0; -} - -//Request to kick char from a certain map server. [Skotlex] -int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason) -{ - if (fd >= 0) - { - WFIFOHEAD(fd,7); - WFIFOW(fd,0) = 0x2b1f; - WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = reason; - WFIFOSET(fd,7); - return 0; - } - return -1; -} - //-------------------------------------------------------- /** * Existence check of WISP data * @see DBApply */ -int inter_check_ttl_wisdata_sub(union DBKey key, struct DBData *data, va_list ap) +static int inter_check_ttl_wisdata_sub(union DBKey key, struct DBData *data, va_list ap) { int64 tick; struct WisData *wd = DB->data2ptr(data); @@ -1120,7 +1028,7 @@ int inter_check_ttl_wisdata_sub(union DBKey key, struct DBData *data, va_list ap return 0; } -int inter_check_ttl_wisdata(void) +static int inter_check_ttl_wisdata(void) { int64 tick = timer->gettick(); int i; @@ -1140,225 +1048,36 @@ int inter_check_ttl_wisdata(void) return 0; } -//-------------------------------------------------------- - -// broadcast sending -int mapif_parse_broadcast(int fd) -{ - mapif->broadcast(RFIFOP(fd,16), RFIFOW(fd,2), RFIFOL(fd,4), RFIFOW(fd,8), RFIFOW(fd,10), RFIFOW(fd,12), RFIFOW(fd,14), fd); - return 0; -} - - -// Wisp/page request to send -int mapif_parse_WisRequest(int fd) -{ - struct WisData* wd; - char name[NAME_LENGTH]; - char esc_name[NAME_LENGTH*2+1];// escaped name - char* data; - size_t len; - - - if ( fd <= 0 ) {return 0;} // check if we have a valid fd - - if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) { - ShowWarning("inter: Wis message size too long.\n"); - return 0; - } else if (RFIFOW(fd,2)-52 <= 0) { // normally, impossible, but who knows... - ShowError("inter: Wis message doesn't exist.\n"); - return 0; - } - - safestrncpy(name, RFIFOP(fd,28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex] - - SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `name` FROM `%s` WHERE `name`='%s'", char_db, esc_name) ) - Sql_ShowDebug(inter->sql_handle); - - // search if character exists before to ask all map-servers - if( SQL_SUCCESS != SQL->NextRow(inter->sql_handle) ) - { - mapif->wis_response(fd, RFIFOP(fd, 4), 1); - } - else - {// Character exists. So, ask all map-servers - // to be sure of the correct name, rewrite it - SQL->GetData(inter->sql_handle, 0, &data, &len); - memset(name, 0, NAME_LENGTH); - memcpy(name, data, min(len, NAME_LENGTH)); - // if source is destination, don't ask other servers. - if (strncmp(RFIFOP(fd,4), name, NAME_LENGTH) == 0) { - mapif->wis_response(fd, RFIFOP(fd, 4), 1); - } - else - { - static int wisid = 0; - CREATE(wd, struct WisData, 1); - - // Whether the failure of previous wisp/page transmission (timeout) - inter->check_ttl_wisdata(); - - wd->id = ++wisid; - wd->fd = fd; - wd->len= RFIFOW(fd,2)-52; - memcpy(wd->src, RFIFOP(fd, 4), NAME_LENGTH); - memcpy(wd->dst, RFIFOP(fd,28), NAME_LENGTH); - memcpy(wd->msg, RFIFOP(fd,52), wd->len); - wd->tick = timer->gettick(); - idb_put(wis_db, wd->id, wd); - mapif->wis_message(wd); - } - } - - SQL->FreeResult(inter->sql_handle); - return 0; -} - - -// Wisp/page transmission result -int mapif_parse_WisReply(int fd) +static struct WisData *inter_add_wisdata(int fd, const unsigned char *src, const unsigned char *dst, const unsigned char *msg, int msg_len) { - int id, flag; + static int wisid = 0; struct WisData *wd; - id = RFIFOL(fd,2); - flag = RFIFOB(fd,6); - wd = (struct WisData*)idb_get(wis_db, id); - if (wd == NULL) - return 0; // This wisp was probably suppress before, because it was timeout of because of target was found on another map-server - - if ((--wd->count) <= 0 || flag != 1) { - mapif->wis_end(wd, flag); // flag: 0: success to send whisper, 1: target character is not logged in?, 2: ignored by target - idb_remove(wis_db, id); - } - - return 0; -} - -// Received wisp message from map-server for ALL gm (just copy the message and resends it to ALL map-servers) -int mapif_parse_WisToGM(int fd) -{ - unsigned char buf[2048]; // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B + CREATE(wd, struct WisData, 1); - memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2)); // Message contains the NUL terminator (see intif_wis_message_to_gm()) - WBUFW(buf, 0) = 0x3803; - mapif->sendall(buf, RFIFOW(fd,2)); + // Whether the failure of previous wisp/page transmission (timeout) + inter->check_ttl_wisdata(); - return 0; -} - -// Save account_reg into sql (type=2) -int mapif_parse_Registry(int fd) -{ - int account_id = RFIFOL(fd, 4), char_id = RFIFOL(fd, 8), count = RFIFOW(fd, 12); - - if( count ) { - int cursor = 14, i; - char key[SCRIPT_VARNAME_LENGTH+1], sval[254]; - bool isLoginActive = sockt->session_is_active(chr->login_fd); - - if( isLoginActive ) - chr->global_accreg_to_login_start(account_id,char_id); - - for(i = 0; i < count; i++) { - unsigned int index; - int len = RFIFOB(fd, cursor); - safestrncpy(key, RFIFOP(fd, cursor + 1), min((int)sizeof(key), len)); - cursor += len + 1; - - index = RFIFOL(fd, cursor); - cursor += 4; - - switch (RFIFOB(fd, cursor++)) { - /* int */ - case 0: - inter->savereg(account_id,char_id,key,index,RFIFOL(fd, cursor),false); - cursor += 4; - break; - case 1: - inter->savereg(account_id,char_id,key,index,0,false); - break; - /* str */ - case 2: - len = RFIFOB(fd, cursor); - safestrncpy(sval, RFIFOP(fd, cursor + 1), min((int)sizeof(sval), len)); - cursor += len + 1; - inter->savereg(account_id,char_id,key,index,(intptr_t)sval,true); - break; - case 3: - inter->savereg(account_id,char_id,key,index,0,true); - break; - default: - ShowError("mapif->parse_Registry: unknown type %d\n",RFIFOB(fd, cursor - 1)); - return 1; - } - - } - - if (isLoginActive) - chr->global_accreg_to_login_send(); - } - return 0; -} + wd->id = ++wisid; + wd->fd = fd; + wd->len = msg_len; + memcpy(wd->src, src, NAME_LENGTH); + memcpy(wd->dst, dst, NAME_LENGTH); + memcpy(wd->msg, msg, wd->len); + wd->tick = timer->gettick(); + idb_put(wis_db, wd->id, wd); -// Request the value of all registries. -int mapif_parse_RegistryRequest(int fd) -{ - //Load Char Registry - if (RFIFOB(fd,12)) mapif->account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),3); // 3: char reg - //Load Account Registry - if (RFIFOB(fd,11)) mapif->account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),2); // 2: account reg - //Ask Login Server for Account2 values. - if (RFIFOB(fd,10)) chr->request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6)); - return 1; + return wd; } -void mapif_namechange_ack(int fd, int account_id, int char_id, int type, int flag, const char *const name) +static struct WisData *inter_get_wisdata(int id) { - nullpo_retv(name); - WFIFOHEAD(fd, NAME_LENGTH+13); - WFIFOW(fd, 0) = 0x3806; - WFIFOL(fd, 2) = account_id; - WFIFOL(fd, 6) = char_id; - WFIFOB(fd,10) = type; - WFIFOB(fd,11) = flag; - memcpy(WFIFOP(fd, 12), name, NAME_LENGTH); - WFIFOSET(fd, NAME_LENGTH+13); + return idb_get(wis_db, id); } -int mapif_parse_NameChangeRequest(int fd) +static void inter_remove_wisdata(int id) { - int account_id, char_id, type; - const char *name; - int i; - - account_id = RFIFOL(fd,2); - char_id = RFIFOL(fd,6); - type = RFIFOB(fd,10); - name = RFIFOP(fd,11); - - // Check Authorized letters/symbols in the name - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorized - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - mapif->namechange_ack(fd, account_id, char_id, type, 0, name); - return 0; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - mapif->namechange_ack(fd, account_id, char_id, type, 0, name); - return 0; - } - } - //TODO: type holds the type of object to rename. - //If it were a player, it needs to have the guild information and db information - //updated here, because changing it on the map won't make it be saved [Skotlex] - - //name allowed. - mapif->namechange_ack(fd, account_id, char_id, type, 1, name); - return 0; + idb_remove(wis_db, id); } //-------------------------------------------------------- @@ -1367,7 +1086,7 @@ int mapif_parse_NameChangeRequest(int fd) /// or 0 if no complete packet exists in the queue. /// /// @param length The minimum allowed length, or -1 for dynamic lookup -int inter_check_length(int fd, int length) +static int inter_check_length(int fd, int length) { if( length == -1 ) {// variable-length packet @@ -1382,7 +1101,7 @@ int inter_check_length(int fd, int length) return length; } -int inter_parse_frommap(int fd) +static int inter_parse_frommap(int fd) { int cmd; int len = 0; @@ -1404,7 +1123,6 @@ int inter_parse_frommap(int fd) case 0x3005: mapif->parse_RegistryRequest(fd); break; case 0x3006: mapif->parse_NameChangeRequest(fd); break; case 0x3007: mapif->parse_accinfo(fd); break; - /* 0x3008 is used by the report stuff */ default: if( inter_party->parse_frommap(fd) || inter_guild->parse_frommap(fd) @@ -1416,6 +1134,9 @@ int inter_parse_frommap(int fd) || inter_mail->parse_frommap(fd) || inter_auction->parse_frommap(fd) || inter_quest->parse_frommap(fd) + || inter_rodex->parse_frommap(fd) + || inter_clan->parse_frommap(fd) + || inter_achievement->parse_frommap(fd) ) break; else @@ -1453,4 +1174,9 @@ void inter_defaults(void) inter->final = inter_final; inter->config_read_log = inter_config_read_log; inter->config_read_connection = inter_config_read_connection; + inter->accinfo = inter_accinfo; + inter->accinfo2 = inter_accinfo2; + inter->add_wisdata = inter_add_wisdata; + inter->get_wisdata = inter_get_wisdata; + inter->remove_wisdata = inter_remove_wisdata; } diff --git a/src/char/inter.h b/src/char/inter.h index 796f89a96..94ee3ab60 100644 --- a/src/char/inter.h +++ b/src/char/inter.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -30,6 +30,12 @@ struct Sql; // common/sql.h struct config_t; // common/conf.h +struct WisData { + int id, fd, count, len; + int64 tick; + unsigned char src[24], dst[24], msg[512]; +}; + /** * inter interface **/ @@ -56,6 +62,13 @@ struct inter_interface { bool (*config_read) (const char *filename, bool imported); bool (*config_read_log) (const char *filename, const struct config_t *config, bool imported); bool (*config_read_connection) (const char *filename, const struct config_t *config, bool imported); + void (*accinfo) (int u_fd, int aid, int castergroup, const char *query, int map_fd); + void (*accinfo2) (bool success, int map_fd, int u_fd, int u_aid, int account_id, const char *userid, const char *user_pass, + const char *email, const char *last_ip, const char *lastlogin, const char *pin_code, const char *birthdate, + int group_id, int logincount, int state); + struct WisData *(*add_wisdata) (int fd, const unsigned char *src, const unsigned char *dst, const unsigned char *msg, int msg_len); + struct WisData *(*get_wisdata) (int id); + void (*remove_wisdata) (int id); }; #ifdef HERCULES_CORE diff --git a/src/char/loginif.c b/src/char/loginif.c index b0e18eb1f..a093c8cf5 100644 --- a/src/char/loginif.c +++ b/src/char/loginif.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -35,12 +35,12 @@ #include <stdlib.h> #include <string.h> -struct loginif_interface loginif_s; +static struct loginif_interface loginif_s; struct loginif_interface *loginif; /// Resets all the data. -void loginif_reset(void) __attribute__ ((noreturn)); -void loginif_reset(void) +static void loginif_reset(void) __attribute__ ((noreturn)); +static void loginif_reset(void) { int id; // TODO kick everyone out and reset everything or wait for connect and try to reacquire locks [FlavioJS] @@ -54,7 +54,7 @@ void loginif_reset(void) /// Checks the conditions for the server to stop. /// Releases the cookie when all characters are saved. /// If all the conditions are met, it stops the core loop. -void loginif_check_shutdown(void) +static void loginif_check_shutdown(void) { if( core->runflag != CHARSERVER_ST_SHUTDOWN ) return; @@ -63,14 +63,14 @@ void loginif_check_shutdown(void) /// Called when the connection to Login Server is disconnected. -void loginif_on_disconnect(void) +static void loginif_on_disconnect(void) { ShowWarning("Connection to Login Server lost.\n\n"); } /// Called when all the connection steps are completed. -void loginif_on_ready(void) +static void loginif_on_ready(void) { int i; @@ -85,7 +85,7 @@ void loginif_on_ready(void) ShowStatus("Awaiting maps from map-server.\n"); } -void do_init_loginif(void) +static void do_init_loginif(void) { // establish char-login connection if not present timer->add_func_list(chr->check_connect_login_server, "chr->check_connect_login_server"); @@ -96,7 +96,7 @@ void do_init_loginif(void) timer->add_interval(timer->gettick() + 1000, chr->send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour } -void do_final_loginif(void) +static void do_final_loginif(void) { if (chr->login_fd != -1) { sockt->close(chr->login_fd); @@ -104,7 +104,7 @@ void do_final_loginif(void) } } -void loginif_block_account(int account_id, int flag) +static void loginif_block_account(int account_id, int flag) { Assert_retv(chr->login_fd != -1); WFIFOHEAD(chr->login_fd,10); @@ -114,7 +114,7 @@ void loginif_block_account(int account_id, int flag) WFIFOSET(chr->login_fd,10); } -void loginif_ban_account(int account_id, short year, short month, short day, short hour, short minute, short second) +static void loginif_ban_account(int account_id, short year, short month, short day, short hour, short minute, short second) { Assert_retv(chr->login_fd != -1); WFIFOHEAD(chr->login_fd,18); @@ -129,7 +129,7 @@ void loginif_ban_account(int account_id, short year, short month, short day, sho WFIFOSET(chr->login_fd,18); } -void loginif_unban_account(int account_id) +static void loginif_unban_account(int account_id) { Assert_retv(chr->login_fd != -1); WFIFOHEAD(chr->login_fd,6); @@ -138,7 +138,7 @@ void loginif_unban_account(int account_id) WFIFOSET(chr->login_fd,6); } -void loginif_changesex(int account_id) +static void loginif_changesex(int account_id) { Assert_retv(chr->login_fd != -1); WFIFOHEAD(chr->login_fd,6); @@ -147,7 +147,7 @@ void loginif_changesex(int account_id) WFIFOSET(chr->login_fd,6); } -void loginif_auth(int fd, struct char_session_data* sd, uint32 ipl) +static void loginif_auth(int fd, struct char_session_data *sd, uint32 ipl) { Assert_retv(chr->login_fd != -1); nullpo_retv(sd); @@ -162,7 +162,7 @@ void loginif_auth(int fd, struct char_session_data* sd, uint32 ipl) WFIFOSET(chr->login_fd,23); } -void loginif_send_users_count(int users) +static void loginif_send_users_count(int users) { Assert_retv(chr->login_fd != -1); WFIFOHEAD(chr->login_fd,6); @@ -171,7 +171,7 @@ void loginif_send_users_count(int users) WFIFOSET(chr->login_fd,6); } -void loginif_connect_to_server(void) +static void loginif_connect_to_server(void) { Assert_retv(chr->login_fd != -1); WFIFOHEAD(chr->login_fd,86); diff --git a/src/char/loginif.h b/src/char/loginif.h index daafdf4ac..5dfedb952 100644 --- a/src/char/loginif.h +++ b/src/char/loginif.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify diff --git a/src/char/mapif.c b/src/char/mapif.c index 1dafb79c3..dc5735550 100644 --- a/src/char/mapif.c +++ b/src/char/mapif.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -20,197 +20,2449 @@ */ #define HERCULES_CORE +#include "config/core.h" // GP_BOUND_ITEMS #include "mapif.h" #include "char/char.h" +#include "char/int_achievement.h" #include "char/int_auction.h" +#include "char/int_clan.h" #include "char/int_guild.h" #include "char/int_homun.h" +#include "char/int_elemental.h" +#include "char/int_mail.h" +#include "char/int_mercenary.h" +#include "char/int_party.h" +#include "char/int_pet.h" +#include "char/int_quest.h" +#include "char/int_rodex.h" +#include "char/int_storage.h" +#include "char/inter.h" #include "common/cbasetypes.h" +#include "common/memmgr.h" #include "common/mmo.h" +#include "common/nullpo.h" #include "common/random.h" #include "common/showmsg.h" #include "common/socket.h" +#include "common/sql.h" #include "common/strlib.h" #include <stdlib.h> -void mapif_ban(int id, unsigned int flag, int status); -void mapif_server_init(int id); -void mapif_server_destroy(int id); -void mapif_server_reset(int id); -void mapif_on_disconnect(int id); -void mapif_on_parse_accinfo(int account_id, int u_fd, int u_aid, int u_group, int map_fd); -void mapif_char_ban(int char_id, time_t timestamp); -int mapif_sendall(const unsigned char *buf, unsigned int len); -int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len); -int mapif_send(int fd, unsigned char *buf, unsigned int len); -void mapif_send_users_count(int users); -void mapif_auction_message(int char_id, unsigned char result); -void mapif_auction_sendlist(int fd, int char_id, short count, short pages, unsigned char *buf); -void mapif_parse_auction_requestlist(int fd); -void mapif_auction_register(int fd, struct auction_data *auction); -void mapif_parse_auction_register(int fd); -void mapif_auction_cancel(int fd, int char_id, unsigned char result); -void mapif_parse_auction_cancel(int fd); -void mapif_auction_close(int fd, int char_id, unsigned char result); -void mapif_parse_auction_close(int fd); -void mapif_auction_bid(int fd, int char_id, int bid, unsigned char result); -void mapif_parse_auction_bid(int fd); -bool mapif_elemental_create(struct s_elemental *ele); -bool mapif_elemental_save(const struct s_elemental *ele); -bool mapif_elemental_load(int ele_id, int char_id, struct s_elemental *ele); -bool mapif_elemental_delete(int ele_id); -void mapif_elemental_send(int fd, struct s_elemental *ele, unsigned char flag); -void mapif_parse_elemental_create(int fd, const struct s_elemental *ele); -void mapif_parse_elemental_load(int fd, int ele_id, int char_id); -void mapif_elemental_deleted(int fd, unsigned char flag); -void mapif_parse_elemental_delete(int fd, int ele_id); -void mapif_elemental_saved(int fd, unsigned char flag); -void mapif_parse_elemental_save(int fd, const struct s_elemental *ele); -int mapif_guild_created(int fd, int account_id, struct guild *g); -int mapif_guild_noinfo(int fd, int guild_id); -int mapif_guild_info(int fd, struct guild *g); -int mapif_guild_memberadded(int fd, int guild_id, int account_id, int char_id, int flag); -int mapif_guild_withdraw(int guild_id, int account_id, int char_id, int flag, const char *name, const char *mes); -int mapif_guild_memberinfoshort(struct guild *g, int idx); -int mapif_guild_broken(int guild_id, int flag); -int mapif_guild_message(int guild_id, int account_id, const char *mes, int len, int sfd); -int mapif_guild_basicinfochanged(int guild_id, int type, const void *data, int len); -int mapif_guild_memberinfochanged(int guild_id, int account_id, int char_id, int type, const void *data, int len); -int mapif_guild_skillupack(int guild_id, uint16 skill_id, int account_id); -int mapif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag, const char *name1, const char *name2); -int mapif_guild_position(struct guild *g, int idx); -int mapif_guild_notice(struct guild *g); -int mapif_guild_emblem(struct guild *g); -int mapif_guild_master_changed(struct guild *g, int aid, int cid); -int mapif_guild_castle_dataload(int fd, int sz, const int *castle_ids); -int mapif_parse_CreateGuild(int fd, int account_id, const char *name, const struct guild_member *master); -int mapif_parse_GuildInfo(int fd, int guild_id); -int mapif_parse_GuildAddMember(int fd, int guild_id, const struct guild_member *m); -int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, int flag, const char *mes); -int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, int char_id, int online, int lv, int16 class); -int mapif_parse_BreakGuild(int fd, int guild_id); -int mapif_parse_GuildMessage(int fd, int guild_id, int account_id, const char *mes, int len); -int mapif_parse_GuildBasicInfoChange(int fd, int guild_id, int type, const void *data, int len); -int mapif_parse_GuildMemberInfoChange(int fd, int guild_id, int account_id, int char_id, int type, const char *data, int len); -int mapif_parse_GuildPosition(int fd, int guild_id, int idx, const struct guild_position *p); -int mapif_parse_GuildSkillUp(int fd, int guild_id, uint16 skill_id, int account_id, int max); -int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int account_id1, int account_id2, int flag); -int mapif_parse_GuildAlliance(int fd, int guild_id1, int guild_id2, int account_id1, int account_id2, int flag); -int mapif_parse_GuildNotice(int fd, int guild_id, const char *mes1, const char *mes2); -int mapif_parse_GuildEmblem(int fd, int len, int guild_id, int dummy, const char *data); -int mapif_parse_GuildCastleDataLoad(int fd, int len, const int *castle_ids); -int mapif_parse_GuildCastleDataSave(int fd, int castle_id, int index, int value); -int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int len); -void mapif_homunculus_created(int fd, int account_id, const struct s_homunculus *sh, unsigned char flag); -void mapif_homunculus_deleted(int fd, int flag); -void mapif_homunculus_loaded(int fd, int account_id, struct s_homunculus *hd); -void mapif_homunculus_saved(int fd, int account_id, bool flag); -void mapif_homunculus_renamed(int fd, int account_id, int char_id, unsigned char flag, const char *name); -bool mapif_homunculus_create(struct s_homunculus *hd); -bool mapif_homunculus_save(const struct s_homunculus *hd); -bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd); -bool mapif_homunculus_delete(int homun_id); -bool mapif_homunculus_rename(const char *name); -void mapif_parse_homunculus_create(int fd, int len, int account_id, const struct s_homunculus *phd); -void mapif_parse_homunculus_delete(int fd, int homun_id); -void mapif_parse_homunculus_load(int fd, int account_id, int homun_id); -void mapif_parse_homunculus_save(int fd, int len, int account_id, const struct s_homunculus *phd); -void mapif_parse_homunculus_rename(int fd, int account_id, int char_id, const char *name); -void mapif_mail_sendinbox(int fd, int char_id, unsigned char flag, struct mail_data *md); -void mapif_parse_mail_requestinbox(int fd); -void mapif_parse_mail_read(int fd); -void mapif_mail_sendattach(int fd, int char_id, struct mail_message *msg); -void mapif_mail_getattach(int fd, int char_id, int mail_id); -void mapif_parse_mail_getattach(int fd); -void mapif_mail_delete(int fd, int char_id, int mail_id, bool failed); -void mapif_parse_mail_delete(int fd); -void mapif_mail_new(struct mail_message *msg); -void mapif_mail_return(int fd, int char_id, int mail_id, int new_mail); -void mapif_parse_mail_return(int fd); -void mapif_mail_send(int fd, struct mail_message* msg); -void mapif_parse_mail_send(int fd); -bool mapif_mercenary_create(struct s_mercenary *merc); -bool mapif_mercenary_save(const struct s_mercenary *merc); -bool mapif_mercenary_load(int merc_id, int char_id, struct s_mercenary *merc); -bool mapif_mercenary_delete(int merc_id); -void mapif_mercenary_send(int fd, struct s_mercenary *merc, unsigned char flag); -void mapif_parse_mercenary_create(int fd, const struct s_mercenary *merc); -void mapif_parse_mercenary_load(int fd, int merc_id, int char_id); -void mapif_mercenary_deleted(int fd, unsigned char flag); -void mapif_parse_mercenary_delete(int fd, int merc_id); -void mapif_mercenary_saved(int fd, unsigned char flag); -void mapif_parse_mercenary_save(int fd, const struct s_mercenary *merc); -int mapif_party_created(int fd, int account_id, int char_id, struct party *p); -void mapif_party_noinfo(int fd, int party_id, int char_id); -void mapif_party_info(int fd, struct party* p, int char_id); -int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag); -int mapif_party_optionchanged(int fd, struct party *p, int account_id, int flag); -int mapif_party_withdraw(int party_id,int account_id, int char_id); -int mapif_party_membermoved(struct party *p, int idx); -int mapif_party_broken(int party_id, int flag); -int mapif_party_message(int party_id, int account_id, const char *mes, int len, int sfd); -int mapif_parse_CreateParty(int fd, const char *name, int item, int item2, const struct party_member *leader); -void mapif_parse_PartyInfo(int fd, int party_id, int char_id); -int mapif_parse_PartyAddMember(int fd, int party_id, const struct party_member *member); -int mapif_parse_PartyChangeOption(int fd,int party_id,int account_id,int exp,int item); -int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id); -int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv); -int mapif_parse_BreakParty(int fd, int party_id); -int mapif_parse_PartyMessage(int fd, int party_id, int account_id, const char *mes, int len); -int mapif_parse_PartyLeaderChange(int fd, int party_id, int account_id, int char_id); -int mapif_pet_created(int fd, int account_id, struct s_pet *p); -int mapif_pet_info(int fd, int account_id, struct s_pet *p); -int mapif_pet_noinfo(int fd, int account_id); -int mapif_save_pet_ack(int fd, int account_id, int flag); -int mapif_delete_pet_ack(int fd, int flag); -int mapif_create_pet(int fd, int account_id, int char_id, short pet_class, short pet_lv, short pet_egg_id, - short pet_equip, short intimate, short hungry, char rename_flag, char incubate, const char *pet_name); -int mapif_load_pet(int fd, int account_id, int char_id, int pet_id); -int mapif_save_pet(int fd, int account_id, const struct s_pet *data); -int mapif_delete_pet(int fd, int pet_id); -int mapif_parse_CreatePet(int fd); -int mapif_parse_LoadPet(int fd); -int mapif_parse_SavePet(int fd); -int mapif_parse_DeletePet(int fd); -struct quest *mapif_quests_fromsql(int char_id, int *count); -bool mapif_quest_delete(int char_id, int quest_id); -bool mapif_quest_add(int char_id, struct quest qd); -bool mapif_quest_update(int char_id, struct quest qd); -void mapif_quest_save_ack(int fd, int char_id, bool success); -int mapif_parse_quest_save(int fd); -void mapif_send_quests(int fd, int char_id, struct quest *tmp_questlog, int num_quests); -int mapif_parse_quest_load(int fd); -int mapif_load_guild_storage(int fd,int account_id,int guild_id, char flag); -int mapif_save_guild_storage_ack(int fd, int account_id, int guild_id, int fail); -int mapif_parse_LoadGuildStorage(int fd); -int mapif_parse_SaveGuildStorage(int fd); -int mapif_itembound_ack(int fd, int aid, int guild_id); -int mapif_parse_ItemBoundRetrieve_sub(int fd); -void mapif_parse_ItemBoundRetrieve(int fd); -void mapif_parse_accinfo(int fd); -void mapif_parse_accinfo2(bool success, int map_fd, int u_fd, int u_aid, int account_id, const char *userid, const char *user_pass, - const char *email, const char *last_ip, const char *lastlogin, const char *pin_code, const char *birthdate, int group_id, int logincount, int state); -int mapif_broadcast(const unsigned char *mes, int len, unsigned int fontColor, short fontType, short fontSize, short fontAlign, short fontY, int sfd); -int mapif_wis_message(struct WisData *wd); -void mapif_wis_response(int fd, const unsigned char *src, int flag); -int mapif_wis_end(struct WisData *wd, int flag); -int mapif_account_reg_reply(int fd,int account_id,int char_id, int type); -int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason); -int mapif_parse_broadcast(int fd); -int mapif_parse_WisRequest(int fd); -int mapif_parse_WisReply(int fd); -int mapif_parse_WisToGM(int fd); -int mapif_parse_Registry(int fd); -int mapif_parse_RegistryRequest(int fd); -void mapif_namechange_ack(int fd, int account_id, int char_id, int type, int flag, const char *const name); -int mapif_parse_NameChangeRequest(int fd); - -struct mapif_interface mapif_s; +static struct mapif_interface mapif_s; struct mapif_interface *mapif; -void mapif_defaults(void) { +static void mapif_ban(int id, unsigned int flag, int status) +{ + // send to all map-servers to disconnect the player + unsigned char buf[11]; + WBUFW(buf,0) = 0x2b14; + WBUFL(buf,2) = id; + WBUFB(buf,6) = flag; // 0: change of status, 1: ban + WBUFL(buf,7) = status; // status or final date of a banishment + mapif->sendall(buf, 11); +} + +/// Initializes a server structure. +static void mapif_server_init(int id) +{ + //memset(&chr->server[id], 0, sizeof(server[id])); + chr->server[id].fd = -1; +} + +/// Destroys a server structure. +static void mapif_server_destroy(int id) +{ + if (chr->server[id].fd == -1) { + sockt->close(chr->server[id].fd); + chr->server[id].fd = -1; + } +} + +/// Resets all the data related to a server. +static void mapif_server_reset(int id) +{ + int i, j; + unsigned char buf[16384]; + int fd = chr->server[id].fd; + //Notify other map servers that this one is gone. [Skotlex] + WBUFW(buf, 0) = 0x2b20; + WBUFL(buf, 4) = htonl(chr->server[id].ip); + WBUFW(buf, 8) = htons(chr->server[id].port); + j = 0; + for (i = 0; i < VECTOR_LENGTH(chr->server[id].maps); i++) { + uint16 m = VECTOR_INDEX(chr->server[id].maps, i); + if (m != 0) + WBUFW(buf, 10 + (j++) * 4) = m; + } + if (j > 0) { + WBUFW(buf, 2) = j * 4 + 10; + mapif->sendallwos(fd, buf, WBUFW(buf, 2)); + } + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `index`='%d'", ragsrvinfo_db, chr->server[id].fd)) + Sql_ShowDebug(inter->sql_handle); + chr->online_char_db->foreach(chr->online_char_db, chr->db_setoffline, id); //Tag relevant chars as 'in disconnected' server. + mapif->server_destroy(id); + mapif->server_init(id); +} + +/// Called when the connection to a Map Server is disconnected. +static void mapif_on_disconnect(int id) +{ + ShowStatus("Map-server #%d has disconnected.\n", id); + mapif->server_reset(id); +} + +static void mapif_on_parse_accinfo(int account_id, int u_fd, int u_aid, int u_group, int map_fd) +{ + Assert_retv(chr->login_fd > 0); + WFIFOHEAD(chr->login_fd, 22); + WFIFOW(chr->login_fd, 0) = 0x2740; + WFIFOL(chr->login_fd, 2) = account_id; + WFIFOL(chr->login_fd, 6) = u_fd; + WFIFOL(chr->login_fd, 10) = u_aid; + WFIFOL(chr->login_fd, 14) = u_group; + WFIFOL(chr->login_fd, 18) = map_fd; + WFIFOSET(chr->login_fd, 22); +} + +static void mapif_char_ban(int char_id, time_t timestamp) +{ + unsigned char buf[11]; + WBUFW(buf, 0) = 0x2b14; + WBUFL(buf, 2) = char_id; + WBUFB(buf, 6) = 2; + WBUFL(buf, 7) = (unsigned int)timestamp; + mapif->sendall(buf, 11); +} + +static int mapif_sendall(const unsigned char *buf, unsigned int len) +{ + int i, c; + + nullpo_ret(buf); + c = 0; + for (i = 0; i < ARRAYLENGTH(chr->server); i++) { + int fd; + if ((fd = chr->server[i].fd) > 0) { + WFIFOHEAD(fd, len); + memcpy(WFIFOP(fd, 0), buf, len); + WFIFOSET(fd, len); + c++; + } + } + + return c; +} + +static int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) +{ + int i, c; + + nullpo_ret(buf); + c = 0; + for (i = 0; i < ARRAYLENGTH(chr->server); i++) { + int fd; + if ((fd = chr->server[i].fd) > 0 && fd != sfd) { + WFIFOHEAD(fd, len); + memcpy(WFIFOP(fd, 0), buf, len); + WFIFOSET(fd, len); + c++; + } + } + + return c; +} + + +static int mapif_send(int fd, unsigned char *buf, unsigned int len) +{ + nullpo_ret(buf); + if (fd >= 0) { + int i; + ARR_FIND (0, ARRAYLENGTH(chr->server), i, fd == chr->server[i].fd); + if (i < ARRAYLENGTH(chr->server)) { + WFIFOHEAD(fd, len); + memcpy(WFIFOP(fd, 0), buf, len); + WFIFOSET(fd, len); + return 1; + } + } + return 0; +} + +static void mapif_send_users_count(int users) +{ + uint8 buf[6]; + // send number of players to all map-servers + WBUFW(buf, 0) = 0x2b00; + WBUFL(buf, 2) = users; + mapif->sendall(buf, 6); +} + + +static void mapif_auction_message(int char_id, unsigned char result) +{ + unsigned char buf[74]; + + WBUFW(buf, 0) = 0x3854; + WBUFL(buf, 2) = char_id; + WBUFL(buf, 6) = result; + mapif->sendall(buf, 7); +} + +static void mapif_auction_sendlist(int fd, int char_id, short count, short pages, unsigned char *buf) +{ + int len = (sizeof(struct auction_data) * count) + 12; + + nullpo_retv(buf); + + WFIFOHEAD(fd, len); + WFIFOW(fd, 0) = 0x3850; + WFIFOW(fd, 2) = len; + WFIFOL(fd, 4) = char_id; + WFIFOW(fd, 8) = count; + WFIFOW(fd, 10) = pages; + memcpy(WFIFOP(fd, 12), buf, len - 12); + WFIFOSET(fd, len); +} + +static void mapif_parse_auction_requestlist(int fd) +{ + char searchtext[NAME_LENGTH]; + int char_id = RFIFOL(fd, 4), len = sizeof(struct auction_data); + int price = RFIFOL(fd, 10); + short type = RFIFOW(fd, 8), page = max(1, RFIFOW(fd, 14)); + unsigned char buf[5 * sizeof(struct auction_data)]; + struct DBIterator *iter = db_iterator(inter_auction->db); + struct auction_data *auction; + short i = 0, j = 0, pages = 1; + + memcpy(searchtext, RFIFOP(fd, 16), NAME_LENGTH); + + for (auction = dbi_first(iter); dbi_exists(iter); auction = dbi_next(iter)) { + if ((type == 0 && auction->type != IT_ARMOR && auction->type != IT_PETARMOR) + || (type == 1 && auction->type != IT_WEAPON) + || (type == 2 && auction->type != IT_CARD) + || (type == 3 && auction->type != IT_ETC) + || (type == 4 && !strstr(auction->item_name, searchtext)) + || (type == 5 && auction->price > price) + || (type == 6 && auction->seller_id != char_id) + || (type == 7 && auction->buyer_id != char_id)) + continue; + + i++; + if (i > 5) { + // Counting Pages of Total Results (5 Results per Page) + pages++; + i = 1; // First Result of This Page + } + + if (page != pages) + continue; // This is not the requested Page + + memcpy(WBUFP(buf, j * len), auction, len); + j++; // Found Results + } + dbi_destroy(iter); + + mapif->auction_sendlist(fd, char_id, j, pages, buf); +} + +static void mapif_auction_register(int fd, struct auction_data *auction) +{ + int len = sizeof(struct auction_data) + 4; + + nullpo_retv(auction); + + WFIFOHEAD(fd,len); + WFIFOW(fd, 0) = 0x3851; + WFIFOW(fd, 2) = len; + memcpy(WFIFOP(fd, 4), auction, sizeof(struct auction_data)); + WFIFOSET(fd, len); +} + +static void mapif_parse_auction_register(int fd) +{ + struct auction_data auction; + if( RFIFOW(fd, 2) != sizeof(struct auction_data) + 4 ) + return; + + memcpy(&auction, RFIFOP(fd, 4), sizeof(struct auction_data)); + if( inter_auction->count(auction.seller_id, false) < 5 ) + auction.auction_id = inter_auction->create(&auction); + + mapif->auction_register(fd, &auction); +} + +static void mapif_auction_cancel(int fd, int char_id, unsigned char result) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x3852; + WFIFOL(fd, 2) = char_id; + WFIFOB(fd, 6) = result; + WFIFOSET(fd, 7); +} + +static void mapif_parse_auction_cancel(int fd) +{ + int char_id = RFIFOL(fd, 2), auction_id = RFIFOL(fd, 6); + struct auction_data *auction; + + if ((auction = (struct auction_data *)idb_get(inter_auction->db, auction_id)) == NULL) { + mapif->auction_cancel(fd, char_id, 1); // Bid Number is Incorrect + return; + } + + if (auction->seller_id != char_id) { + mapif->auction_cancel(fd, char_id, 2); // You cannot end the auction + return; + } + + if (auction->buyer_id > 0) { + mapif->auction_cancel(fd, char_id, 3); // An auction with at least one bidder cannot be canceled + return; + } + + inter_mail->sendmail(0, "Auction Manager", auction->seller_id, auction->seller_name, "Auction", "Auction canceled.", 0, &auction->item); + inter_auction->delete_(auction); + + mapif->auction_cancel(fd, char_id, 0); // The auction has been canceled +} + + +static void mapif_auction_close(int fd, int char_id, unsigned char result) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x3853; + WFIFOL(fd, 2) = char_id; + WFIFOB(fd, 6) = result; + WFIFOSET(fd, 7); +} + +static void mapif_parse_auction_close(int fd) +{ + int char_id = RFIFOL(fd, 2), auction_id = RFIFOL(fd, 6); + struct auction_data *auction; + + if ((auction = (struct auction_data *)idb_get(inter_auction->db, auction_id)) == NULL) { + mapif->auction_close(fd, char_id, 2); // Bid Number is Incorrect + return; + } + + if (auction->seller_id != char_id) { + mapif->auction_close(fd, char_id, 1); // You cannot end the auction + return; + } + + if (auction->buyer_id == 0) { + mapif->auction_close(fd, char_id, 1); // You cannot end the auction + return; + } + + // Send Money to Seller + inter_mail->sendmail(0, "Auction Manager", auction->seller_id, auction->seller_name, "Auction", "Auction closed.", auction->price, NULL); + // Send Item to Buyer + inter_mail->sendmail(0, "Auction Manager", auction->buyer_id, auction->buyer_name, "Auction", "Auction winner.", 0, &auction->item); + mapif->auction_message(auction->buyer_id, 6); // You have won the auction + inter_auction->delete_(auction); + + mapif->auction_close(fd, char_id, 0); // You have ended the auction +} + +static void mapif_auction_bid(int fd, int char_id, int bid, unsigned char result) +{ + WFIFOHEAD(fd, 11); + WFIFOW(fd, 0) = 0x3855; + WFIFOL(fd, 2) = char_id; + WFIFOL(fd, 6) = bid; // To Return Zeny + WFIFOB(fd, 10) = result; + WFIFOSET(fd, 11); +} + +static void mapif_parse_auction_bid(int fd) +{ + int char_id = RFIFOL(fd, 4), bid = RFIFOL(fd, 12); + unsigned int auction_id = RFIFOL(fd, 8); + struct auction_data *auction; + + if ((auction = (struct auction_data *)idb_get(inter_auction->db, auction_id)) == NULL || auction->price >= bid || auction->seller_id == char_id) { + mapif->auction_bid(fd, char_id, bid, 0); // You have failed to bid in the auction + return; + } + + if (inter_auction->count(char_id, true) > 4 && bid < auction->buynow && auction->buyer_id != char_id) { + mapif->auction_bid(fd, char_id, bid, 9); // You cannot place more than 5 bids at a time + return; + } + + if (auction->buyer_id > 0) { + // Send Money back to the previous Buyer + if (auction->buyer_id != char_id) { + inter_mail->sendmail(0, "Auction Manager", auction->buyer_id, auction->buyer_name, "Auction", "Someone has placed a higher bid.", auction->price, NULL); + mapif->auction_message(auction->buyer_id, 7); // You have failed to win the auction + } else { + inter_mail->sendmail(0, "Auction Manager", auction->buyer_id, auction->buyer_name, "Auction", "You have placed a higher bid.", auction->price, NULL); + } + } + + auction->buyer_id = char_id; + safestrncpy(auction->buyer_name, RFIFOP(fd, 16), NAME_LENGTH); + auction->price = bid; + + if (bid >= auction->buynow) { + // Automatic won the auction + mapif->auction_bid(fd, char_id, bid - auction->buynow, 1); // You have successfully bid in the auction + + inter_mail->sendmail(0, "Auction Manager", auction->buyer_id, auction->buyer_name, "Auction", "You have won the auction.", 0, &auction->item); + mapif->auction_message(char_id, 6); // You have won the auction + inter_mail->sendmail(0, "Auction Manager", auction->seller_id, auction->seller_name, "Auction", "Payment for your auction!.", auction->buynow, NULL); + + inter_auction->delete_(auction); + return; + } + + inter_auction->save(auction); + + mapif->auction_bid(fd, char_id, 0, 1); // You have successfully bid in the auction +} + +static void mapif_elemental_send(int fd, struct s_elemental *ele, unsigned char flag) +{ + int size = sizeof(struct s_elemental) + 5; + + nullpo_retv(ele); + WFIFOHEAD(fd, size); + WFIFOW(fd, 0) = 0x387c; + WFIFOW(fd, 2) = size; + WFIFOB(fd, 4) = flag; + memcpy(WFIFOP(fd, 5), ele, sizeof(struct s_elemental)); + WFIFOSET(fd, size); +} + +static void mapif_parse_elemental_create(int fd, const struct s_elemental *ele) +{ + struct s_elemental ele_; + bool result; + + memcpy(&ele_, ele, sizeof(ele_)); + + result = inter_elemental->create(&ele_); + mapif->elemental_send(fd, &ele_, result); +} + +static void mapif_parse_elemental_load(int fd, int ele_id, int char_id) +{ + struct s_elemental ele; + bool result = inter_elemental->load(ele_id, char_id, &ele); + mapif->elemental_send(fd, &ele, result); +} + +static void mapif_elemental_deleted(int fd, unsigned char flag) +{ + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x387d; + WFIFOB(fd, 2) = flag; + WFIFOSET(fd, 3); +} + +static void mapif_parse_elemental_delete(int fd, int ele_id) +{ + bool result = inter_elemental->delete(ele_id); + mapif->elemental_deleted(fd, result); +} + +static void mapif_elemental_saved(int fd, unsigned char flag) +{ + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x387e; + WFIFOB(fd, 2) = flag; + WFIFOSET(fd, 3); +} + +static void mapif_parse_elemental_save(int fd, const struct s_elemental *ele) +{ + bool result = inter_elemental->save(ele); + mapif->elemental_saved(fd, result); +} + +static int mapif_guild_created(int fd, int account_id, struct guild *g) +{ + WFIFOHEAD(fd, 10); + WFIFOW(fd, 0) = 0x3830; + WFIFOL(fd, 2) = account_id; + if (g != NULL) { + WFIFOL(fd, 6) = g->guild_id; + ShowInfo("int_guild: Guild created (%d - %s)\n", g->guild_id, g->name); + } else { + WFIFOL(fd, 6) = 0; + } + + WFIFOSET(fd, 10); + return 0; +} + +// Guild not found +static int mapif_guild_noinfo(int fd, int guild_id) +{ + unsigned char buf[12]; + WBUFW(buf, 0) = 0x3831; + WBUFW(buf, 2) = 8; + WBUFL(buf, 4) = guild_id; + ShowWarning("int_guild: info not found %d\n", guild_id); + if (fd < 0) + mapif->sendall(buf, 8); + else + mapif->send(fd,buf, 8); + return 0; +} + +// Send guild info +static int mapif_guild_info(int fd, struct guild *g) +{ + unsigned char buf[8 + sizeof(struct guild)]; + nullpo_ret(g); + WBUFW(buf, 0) = 0x3831; + WBUFW(buf, 2) = 4 + sizeof(struct guild); + memcpy(buf + 4, g, sizeof(struct guild)); + if (fd < 0) + mapif->sendall(buf, WBUFW(buf, 2)); + else + mapif->send(fd, buf, WBUFW(buf, 2)); + return 0; +} + +// ACK member add +static int mapif_guild_memberadded(int fd, int guild_id, int account_id, int char_id, int flag) +{ + WFIFOHEAD(fd, 15); + WFIFOW(fd, 0) = 0x3832; + WFIFOL(fd, 2) = guild_id; + WFIFOL(fd, 6) = account_id; + WFIFOL(fd, 10) = char_id; + WFIFOB(fd, 14) = flag; + WFIFOSET(fd, 15); + return 0; +} + +// ACK member leave +static int mapif_guild_withdraw(int guild_id, int account_id, int char_id, int flag, const char *name, const char *mes) +{ + unsigned char buf[55 + NAME_LENGTH]; + + nullpo_ret(name); + nullpo_ret(mes); + + WBUFW(buf, 0) = 0x3834; + WBUFL(buf, 2) = guild_id; + WBUFL(buf, 6) = account_id; + WBUFL(buf, 10) = char_id; + WBUFB(buf, 14) = flag; + memcpy(WBUFP(buf, 15), mes, 40); + memcpy(WBUFP(buf, 55), name, NAME_LENGTH); + mapif->sendall(buf, 55 + NAME_LENGTH); + ShowInfo("int_guild: guild withdraw (%d - %d: %s - %s)\n", guild_id, account_id, name, mes); + return 0; +} + +// Send short member's info +static int mapif_guild_memberinfoshort(struct guild *g, int idx) +{ + unsigned char buf[23]; + nullpo_ret(g); + Assert_ret(idx >= 0 && idx < MAX_GUILD); + WBUFW(buf, 0) = 0x3835; + WBUFL(buf, 2) = g->guild_id; + WBUFL(buf, 6) = g->member[idx].account_id; + WBUFL(buf, 10) = g->member[idx].char_id; + WBUFB(buf, 14) = (unsigned char)g->member[idx].online; + WBUFW(buf, 15) = g->member[idx].lv; + WBUFW(buf, 17) = g->member[idx].class; + WBUFL(buf, 19) = g->member[idx].last_login; + mapif->sendall(buf, 23); + return 0; +} + +// Send guild broken +static int mapif_guild_broken(int guild_id, int flag) +{ + unsigned char buf[7]; + WBUFW(buf, 0) = 0x3836; + WBUFL(buf, 2) = guild_id; + WBUFB(buf, 6) = flag; + mapif->sendall(buf, 7); + ShowInfo("int_guild: Guild broken (%d)\n", guild_id); + return 0; +} + +// Send guild message +static int mapif_guild_message(int guild_id, int account_id, const char *mes, int len, int sfd) +{ + unsigned char buf[512]; + nullpo_ret(mes); + if (len > 500) + len = 500; + WBUFW(buf, 0) = 0x3837; + WBUFW(buf, 2) = len + 12; + WBUFL(buf, 4) = guild_id; + WBUFL(buf, 8) = account_id; + memcpy(WBUFP(buf, 12), mes, len); + mapif->sendallwos(sfd, buf, len + 12); + return 0; +} + +// Send basic info +static int mapif_guild_basicinfochanged(int guild_id, int type, const void *data, int len) +{ + unsigned char buf[2048]; + nullpo_ret(data); + if (len > 2038) + len = 2038; + WBUFW(buf, 0) = 0x3839; + WBUFW(buf, 2) = len + 10; + WBUFL(buf, 4) = guild_id; + WBUFW(buf, 8) = type; + memcpy(WBUFP(buf, 10), data, len); + mapif->sendall(buf, len + 10); + return 0; +} + +// Send member info +static int mapif_guild_memberinfochanged(int guild_id, int account_id, int char_id, int type, const void *data, int len) +{ + unsigned char buf[2048]; + nullpo_ret(data); + if (len > 2030) + len = 2030; + WBUFW(buf, 0) = 0x383a; + WBUFW(buf, 2) = len + 18; + WBUFL(buf, 4) = guild_id; + WBUFL(buf, 8) = account_id; + WBUFL(buf, 12) = char_id; + WBUFW(buf, 16) = type; + memcpy(WBUFP(buf, 18), data, len); + mapif->sendall(buf, len + 18); + return 0; +} + +// ACK guild skill up +static int mapif_guild_skillupack(int guild_id, uint16 skill_id, int account_id) +{ + unsigned char buf[14]; + WBUFW(buf, 0) = 0x383c; + WBUFL(buf, 2) = guild_id; + WBUFL(buf, 6) = skill_id; + WBUFL(buf,10) = account_id; + mapif->sendall(buf, 14); + return 0; +} + +// ACK guild alliance +static int mapif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag, const char *name1, const char *name2) +{ + unsigned char buf[19 + 2 * NAME_LENGTH]; + nullpo_ret(name1); + nullpo_ret(name2); + WBUFW(buf, 0) = 0x383d; + WBUFL(buf, 2) = guild_id1; + WBUFL(buf, 6) = guild_id2; + WBUFL(buf, 10) = account_id1; + WBUFL(buf, 14) = account_id2; + WBUFB(buf, 18) = flag; + memcpy(WBUFP(buf, 19), name1, NAME_LENGTH); + memcpy(WBUFP(buf, 19 + NAME_LENGTH), name2, NAME_LENGTH); + mapif->sendall(buf,19 + 2 * NAME_LENGTH); + return 0; +} + +// Send a guild position desc +static int mapif_guild_position(struct guild *g, int idx) +{ + unsigned char buf[12 + sizeof(struct guild_position)]; + nullpo_ret(g); + Assert_ret(idx >= 0 && idx < MAX_GUILDPOSITION); + WBUFW(buf, 0) = 0x383b; + WBUFW(buf, 2) = sizeof(struct guild_position)+12; + WBUFL(buf, 4) = g->guild_id; + WBUFL(buf, 8) = idx; + memcpy(WBUFP(buf, 12), &g->position[idx], sizeof(struct guild_position)); + mapif->sendall(buf, WBUFW(buf, 2)); + return 0; +} + +// Send the guild notice +static int mapif_guild_notice(struct guild *g) +{ + unsigned char buf[256]; + nullpo_ret(g); + WBUFW(buf, 0) = 0x383e; + WBUFL(buf, 2) = g->guild_id; + memcpy(WBUFP(buf, 6), g->mes1, MAX_GUILDMES1); + memcpy(WBUFP(buf, 66), g->mes2, MAX_GUILDMES2); + mapif->sendall(buf, 186); + return 0; +} + +// Send emblem data +static int mapif_guild_emblem(struct guild *g) +{ + unsigned char buf[12 + sizeof(g->emblem_data)]; + nullpo_ret(g); + WBUFW(buf, 0) = 0x383f; + WBUFW(buf, 2) = g->emblem_len+12; + WBUFL(buf, 4) = g->guild_id; + WBUFL(buf, 8) = g->emblem_id; + memcpy(WBUFP(buf, 12), g->emblem_data, g->emblem_len); + mapif->sendall(buf, WBUFW(buf, 2)); + return 0; +} + +static int mapif_guild_master_changed(struct guild *g, int aid, int cid) +{ + unsigned char buf[14]; + nullpo_ret(g); + WBUFW(buf, 0) = 0x3843; + WBUFL(buf, 2) = g->guild_id; + WBUFL(buf, 6) = aid; + WBUFL(buf, 10) = cid; + mapif->sendall(buf, 14); + return 0; +} + +static int mapif_guild_castle_dataload(int fd, int sz, const int *castle_ids) +{ + struct guild_castle *gc = NULL; + int num = (sz - 4) / sizeof(int); + int len = 4 + num * sizeof(*gc); + int i; + + nullpo_ret(castle_ids); + WFIFOHEAD(fd, len); + WFIFOW(fd, 0) = 0x3840; + WFIFOW(fd, 2) = len; + for (i = 0; i < num; i++) { + gc = inter_guild->castle_fromsql(*(castle_ids++)); + memcpy(WFIFOP(fd, 4 + i * sizeof(*gc)), gc, sizeof(*gc)); + } + WFIFOSET(fd, len); + return 0; +} + +// Guild creation request +static int mapif_parse_CreateGuild(int fd, int account_id, const char *name, const struct guild_member *master) +{ + struct guild *g; + nullpo_ret(name); + nullpo_ret(master); + + g = inter_guild->create(name, master); + + // Report to client + mapif->guild_created(fd,account_id,g); + if (g != NULL) { + mapif->guild_info(fd,g); + } + + return 0; +} + +// Return guild info to client +static int mapif_parse_GuildInfo(int fd, int guild_id) +{ + struct guild * g = inter_guild->fromsql(guild_id); //We use this because on start-up the info of castle-owned guilds is required. [Skotlex] + if (g != NULL) { + if (!inter_guild->calcinfo(g)) + mapif->guild_info(fd, g); + } else { + mapif->guild_noinfo(fd, guild_id); // Failed to load info + } + return 0; +} + +// Add member to guild +static int mapif_parse_GuildAddMember(int fd, int guild_id, const struct guild_member *m) +{ + nullpo_ret(m); + + inter_guild->add_member(guild_id, m, fd); + return 0; +} + +// Delete member from guild +static int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, int flag, const char *mes) +{ + inter_guild->leave(guild_id, account_id, char_id, flag, mes, fd); + return 0; +} + +// Change member info +static int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, int char_id, int online, int lv, int16 class) +{ + inter_guild->update_member_info_short(guild_id, account_id, char_id, online, lv, class); + return 0; +} + +// BreakGuild +static int mapif_parse_BreakGuild(int fd, int guild_id) +{ + inter_guild->disband(guild_id); + return 0; +} + +// Forward Guild message to others map servers +static int mapif_parse_GuildMessage(int fd, int guild_id, int account_id, const char *mes, int len) +{ + return mapif->guild_message(guild_id,account_id,mes,len, fd); +} + +/** + * Changes basic guild information + * The types are available in mmo.h::guild_basic_info + **/ +static int mapif_parse_GuildBasicInfoChange(int fd, int guild_id, int type, const void *data, int len) +{ + inter_guild->update_basic_info(guild_id, type, data, len); + // Information is already sent in mapif->guild_info + //mapif->guild_basicinfochanged(guild_id,type,data,len); + return 0; +} + +// Modification of the guild +static int mapif_parse_GuildMemberInfoChange(int fd, int guild_id, int account_id, int char_id, int type, const char *data, int len) +{ + inter_guild->update_member_info(guild_id, account_id, char_id, type, data, len); + return 0; +} + +// Change a position desc +static int mapif_parse_GuildPosition(int fd, int guild_id, int idx, const struct guild_position *p) +{ + nullpo_ret(p); + inter_guild->update_position(guild_id, idx, p); + return 0; +} + +// Guild Skill UP +static int mapif_parse_GuildSkillUp(int fd, int guild_id, uint16 skill_id, int account_id, int max) +{ + inter_guild->use_skill_point(guild_id, skill_id, account_id, max); + return 0; +} + +// Alliance modification +static int mapif_parse_GuildAlliance(int fd, int guild_id1, int guild_id2, int account_id1, int account_id2, int flag) +{ + inter_guild->change_alliance(guild_id1, guild_id2, account_id1, account_id2, flag); + return 0; +} + +// Change guild message +static int mapif_parse_GuildNotice(int fd, int guild_id, const char *mes1, const char *mes2) +{ + inter_guild->update_notice(guild_id, mes1, mes2); + return 0; +} + +static int mapif_parse_GuildEmblem(int fd, int len, int guild_id, int dummy, const char *data) +{ + inter_guild->update_emblem(len, guild_id, data); + return 0; +} + +static int mapif_parse_GuildCastleDataLoad(int fd, int len, const int *castle_ids) +{ + return mapif->guild_castle_dataload(fd, len, castle_ids); +} + +static int mapif_parse_GuildCastleDataSave(int fd, int castle_id, int index, int value) +{ + inter_guild->update_castle_data(castle_id, index, value); + return 0; +} + +static int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int len) +{ + inter_guild->change_leader(guild_id, name, len); + return 0; +} + +static void mapif_homunculus_created(int fd, int account_id, const struct s_homunculus *sh, unsigned char flag) +{ + nullpo_retv(sh); + WFIFOHEAD(fd, sizeof(struct s_homunculus) + 9); + WFIFOW(fd, 0) = 0x3890; + WFIFOW(fd, 2) = sizeof(struct s_homunculus) + 9; + WFIFOL(fd, 4) = account_id; + WFIFOB(fd, 8) = flag; + memcpy(WFIFOP(fd, 9), sh, sizeof(struct s_homunculus)); + WFIFOSET(fd, WFIFOW(fd, 2)); +} + +static void mapif_homunculus_deleted(int fd, int flag) +{ + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x3893; + WFIFOB(fd,2) = flag; //Flag 1 = success + WFIFOSET(fd, 3); +} + +static void mapif_homunculus_loaded(int fd, int account_id, struct s_homunculus *hd) +{ + WFIFOHEAD(fd, sizeof(struct s_homunculus) + 9); + WFIFOW(fd, 0) = 0x3891; + WFIFOW(fd, 2) = sizeof(struct s_homunculus) + 9; + WFIFOL(fd, 4) = account_id; + if (hd != NULL) { + WFIFOB(fd, 8) = 1; // success + memcpy(WFIFOP(fd, 9), hd, sizeof(struct s_homunculus)); + } else { + WFIFOB(fd, 8) = 0; // not found. + memset(WFIFOP(fd, 9), 0, sizeof(struct s_homunculus)); + } + WFIFOSET(fd, sizeof(struct s_homunculus) + 9); +} + +static void mapif_homunculus_saved(int fd, int account_id, bool flag) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x3892; + WFIFOL(fd, 2) = account_id; + WFIFOB(fd, 6) = flag; // 1:success, 0:failure + WFIFOSET(fd, 7); +} + +static void mapif_homunculus_renamed(int fd, int account_id, int char_id, unsigned char flag, const char *name) +{ + nullpo_retv(name); + WFIFOHEAD(fd, NAME_LENGTH + 12); + WFIFOW(fd, 0) = 0x3894; + WFIFOL(fd, 2) = account_id; + WFIFOL(fd, 6) = char_id; + WFIFOB(fd, 10) = flag; + safestrncpy(WFIFOP(fd, 11), name, NAME_LENGTH); + WFIFOSET(fd, NAME_LENGTH + 12); +} + +static void mapif_parse_homunculus_create(int fd, int len, int account_id, const struct s_homunculus *phd) +{ + struct s_homunculus shd; + bool result; + + memcpy(&shd, phd, sizeof(shd)); + + result = inter_homunculus->create(&shd); + mapif->homunculus_created(fd, account_id, &shd, result); +} + +static void mapif_parse_homunculus_delete(int fd, int homun_id) +{ + bool result = inter_homunculus->delete(homun_id); + mapif->homunculus_deleted(fd, result); +} + +static void mapif_parse_homunculus_load(int fd, int account_id, int homun_id) +{ + struct s_homunculus hd; + bool result = inter_homunculus->load(homun_id, &hd); + mapif->homunculus_loaded(fd, account_id, (result ? &hd : NULL)); +} + +static void mapif_parse_homunculus_save(int fd, int len, int account_id, const struct s_homunculus *phd) +{ + bool result = inter_homunculus->save(phd); + mapif->homunculus_saved(fd, account_id, result); +} + +static void mapif_parse_homunculus_rename(int fd, int account_id, int char_id, const char *name) +{ + bool result = inter_homunculus->rename(name); + mapif->homunculus_renamed(fd, account_id, char_id, result, name); +} + +static void mapif_mail_sendinbox(int fd, int char_id, unsigned char flag, struct mail_data *md) +{ + nullpo_retv(md); + //FIXME: dumping the whole structure like this is unsafe [ultramage] + WFIFOHEAD(fd, sizeof(struct mail_data) + 9); + WFIFOW(fd, 0) = 0x3848; + WFIFOW(fd, 2) = sizeof(struct mail_data) + 9; + WFIFOL(fd, 4) = char_id; + WFIFOB(fd, 8) = flag; + memcpy(WFIFOP(fd, 9),md,sizeof(struct mail_data)); + WFIFOSET(fd,WFIFOW(fd, 2)); +} + +/*========================================== + * Client Inbox Request + *------------------------------------------*/ +static void mapif_parse_mail_requestinbox(int fd) +{ + int char_id = RFIFOL(fd, 2); + unsigned char flag = RFIFOB(fd, 6); + struct mail_data md; + memset(&md, 0, sizeof(md)); + inter_mail->fromsql(char_id, &md); + mapif->mail_sendinbox(fd, char_id, flag, &md); +} + +/*========================================== + * Mark mail as 'Read' + *------------------------------------------*/ +static void mapif_parse_mail_read(int fd) +{ + int mail_id = RFIFOL(fd, 2); + inter_mail->mark_read(mail_id); +} + +static void mapif_mail_sendattach(int fd, int char_id, struct mail_message *msg) +{ + nullpo_retv(msg); + WFIFOHEAD(fd, sizeof(struct item) + 12); + WFIFOW(fd, 0) = 0x384a; + WFIFOW(fd, 2) = sizeof(struct item) + 12; + WFIFOL(fd, 4) = char_id; + WFIFOL(fd, 8) = (msg->zeny > 0) ? msg->zeny : 0; + memcpy(WFIFOP(fd, 12), &msg->item, sizeof(struct item)); + WFIFOSET(fd,WFIFOW(fd, 2)); +} + +static void mapif_parse_mail_getattach(int fd) +{ + struct mail_message msg = { 0 }; + int char_id = RFIFOL(fd, 2); + int mail_id = RFIFOL(fd, 6); + + if (!inter_mail->get_attachment(char_id, mail_id, &msg)) + return; + + mapif->mail_sendattach(fd, char_id, &msg); +} + +/*========================================== + * Delete Mail + *------------------------------------------*/ +static void mapif_mail_delete(int fd, int char_id, int mail_id, bool failed) +{ + WFIFOHEAD(fd, 11); + WFIFOW(fd, 0) = 0x384b; + WFIFOL(fd, 2) = char_id; + WFIFOL(fd, 6) = mail_id; + WFIFOB(fd, 10) = failed; + WFIFOSET(fd, 11); +} + +static void mapif_parse_mail_delete(int fd) +{ + int char_id = RFIFOL(fd, 2); + int mail_id = RFIFOL(fd, 6); + bool failed = !inter_mail->delete(char_id, mail_id); + mapif->mail_delete(fd, char_id, mail_id, failed); +} + +/*========================================== + * Report New Mail to Map Server + *------------------------------------------*/ +static void mapif_mail_new(struct mail_message *msg) +{ + unsigned char buf[74]; + + if (msg == NULL || msg->id == 0) + return; + + WBUFW(buf, 0) = 0x3849; + WBUFL(buf, 2) = msg->dest_id; + WBUFL(buf, 6) = msg->id; + memcpy(WBUFP(buf, 10), msg->send_name, NAME_LENGTH); + memcpy(WBUFP(buf, 34), msg->title, MAIL_TITLE_LENGTH); + mapif->sendall(buf, 74); +} + +/*========================================== + * Return Mail + *------------------------------------------*/ +static void mapif_mail_return(int fd, int char_id, int mail_id, int new_mail) +{ + WFIFOHEAD(fd, 11); + WFIFOW(fd, 0) = 0x384c; + WFIFOL(fd, 2) = char_id; + WFIFOL(fd, 6) = mail_id; + WFIFOB(fd, 10) = (new_mail == 0); + WFIFOSET(fd, 11); +} + +static void mapif_parse_mail_return(int fd) +{ + int char_id = RFIFOL(fd, 2); + int mail_id = RFIFOL(fd, 6); + int new_mail = 0; + + if (!inter_mail->return_message(char_id, mail_id, &new_mail)) + return; + + mapif->mail_return(fd, char_id, mail_id, new_mail); +} + +/*========================================== + * Send Mail + *------------------------------------------*/ +static void mapif_mail_send(int fd, struct mail_message* msg) +{ + int len = sizeof(struct mail_message) + 4; + + nullpo_retv(msg); + WFIFOHEAD(fd, len); + WFIFOW(fd, 0) = 0x384d; + WFIFOW(fd, 2) = len; + memcpy(WFIFOP(fd, 4), msg, sizeof(struct mail_message)); + WFIFOSET(fd,len); +} + +static void mapif_parse_mail_send(int fd) +{ + struct mail_message msg; + int account_id = 0; + + if (RFIFOW(fd, 2) != 8 + sizeof(struct mail_message)) + return; + + account_id = RFIFOL(fd, 4); + memcpy(&msg, RFIFOP(fd, 8), sizeof(struct mail_message)); + + inter_mail->send(account_id, &msg); + + mapif->mail_send(fd, &msg); // notify sender + mapif->mail_new(&msg); // notify recipient +} + +static void mapif_mercenary_send(int fd, struct s_mercenary *merc, unsigned char flag) +{ + int size = sizeof(struct s_mercenary) + 5; + + nullpo_retv(merc); + WFIFOHEAD(fd, size); + WFIFOW(fd, 0) = 0x3870; + WFIFOW(fd, 2) = size; + WFIFOB(fd, 4) = flag; + memcpy(WFIFOP(fd, 5), merc, sizeof(struct s_mercenary)); + WFIFOSET(fd,size); +} + +static void mapif_parse_mercenary_create(int fd, const struct s_mercenary *merc) +{ + struct s_mercenary merc_; + bool result; + + memcpy(&merc_, merc, sizeof(merc_)); + + result = inter_mercenary->create(&merc_); + mapif->mercenary_send(fd, &merc_, result); +} + +static void mapif_parse_mercenary_load(int fd, int merc_id, int char_id) +{ + struct s_mercenary merc; + bool result = inter_mercenary->load(merc_id, char_id, &merc); + mapif->mercenary_send(fd, &merc, result); +} + +static void mapif_mercenary_deleted(int fd, unsigned char flag) +{ + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x3871; + WFIFOB(fd, 2) = flag; + WFIFOSET(fd, 3); +} + +static void mapif_parse_mercenary_delete(int fd, int merc_id) +{ + bool result = inter_mercenary->delete(merc_id); + mapif->mercenary_deleted(fd, result); +} + +static void mapif_mercenary_saved(int fd, unsigned char flag) +{ + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x3872; + WFIFOB(fd, 2) = flag; + WFIFOSET(fd, 3); +} + +static void mapif_parse_mercenary_save(int fd, const struct s_mercenary *merc) +{ + bool result = inter_mercenary->save(merc); + mapif->mercenary_saved(fd, result); +} + +// Create a party whether or not +static int mapif_party_created(int fd, int account_id, int char_id, struct party *p) +{ + WFIFOHEAD(fd, 39); + WFIFOW(fd, 0) = 0x3820; + WFIFOL(fd, 2) = account_id; + WFIFOL(fd, 6) = char_id; + if (p != NULL) { + WFIFOB(fd, 10) = 0; + WFIFOL(fd, 11) = p->party_id; + memcpy(WFIFOP(fd, 15), p->name, NAME_LENGTH); + ShowInfo("int_party: Party created (%d - %s)\n", p->party_id, p->name); + } else { + WFIFOB(fd, 10) = 1; + WFIFOL(fd, 11) = 0; + memset(WFIFOP(fd, 15), 0, NAME_LENGTH); + } + WFIFOSET(fd, 39); + + return 0; +} + +//Party information not found +static void mapif_party_noinfo(int fd, int party_id, int char_id) +{ + WFIFOHEAD(fd, 12); + WFIFOW(fd, 0) = 0x3821; + WFIFOW(fd, 2) = 12; + WFIFOL(fd, 4) = char_id; + WFIFOL(fd, 8) = party_id; + WFIFOSET(fd, 12); + ShowWarning("int_party: info not found (party_id=%d char_id=%d)\n", party_id, char_id); +} + +//Digest party information +static void mapif_party_info(int fd, struct party* p, int char_id) +{ + unsigned char buf[8 + sizeof(struct party)]; + nullpo_retv(p); + WBUFW(buf, 0) = 0x3821; + WBUFW(buf, 2) = 8 + sizeof(struct party); + WBUFL(buf, 4) = char_id; + memcpy(WBUFP(buf, 8), p, sizeof(struct party)); + + if (fd < 0) + mapif->sendall(buf, WBUFW(buf, 2)); + else + mapif->send(fd, buf, WBUFW(buf, 2)); +} + +//Whether or not additional party members +static int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) +{ + WFIFOHEAD(fd, 15); + WFIFOW(fd, 0) = 0x3822; + WFIFOL(fd, 2) = party_id; + WFIFOL(fd, 6) = account_id; + WFIFOL(fd, 10) = char_id; + WFIFOB(fd, 14) = flag; + WFIFOSET(fd, 15); + + return 0; +} + +// Party setting change notification +static int mapif_party_optionchanged(int fd, struct party *p, int account_id, int flag) +{ + unsigned char buf[16]; + nullpo_ret(p); + WBUFW(buf, 0) = 0x3823; + WBUFL(buf, 2) = p->party_id; + WBUFL(buf, 6) = account_id; + WBUFW(buf, 10) = p->exp; + WBUFW(buf, 12) = p->item; + WBUFB(buf, 14) = flag; + if (flag == 0) + mapif->sendall(buf, 15); + else + mapif->send(fd, buf, 15); + return 0; +} + +//Withdrawal notification party +static int mapif_party_withdraw(int party_id, int account_id, int char_id) +{ + unsigned char buf[16]; + + WBUFW(buf, 0) = 0x3824; + WBUFL(buf, 2) = party_id; + WBUFL(buf, 6) = account_id; + WBUFL(buf, 10) = char_id; + mapif->sendall(buf, 14); + return 0; +} + +//Party map update notification +static int mapif_party_membermoved(struct party *p, int idx) +{ + unsigned char buf[20]; + + nullpo_ret(p); + Assert_ret(idx >= 0 && idx < MAX_PARTY); + WBUFW(buf, 0) = 0x3825; + WBUFL(buf, 2) = p->party_id; + WBUFL(buf, 6) = p->member[idx].account_id; + WBUFL(buf, 10) = p->member[idx].char_id; + WBUFW(buf, 14) = p->member[idx].map; + WBUFB(buf, 16) = p->member[idx].online; + WBUFW(buf, 17) = p->member[idx].lv; + mapif->sendall(buf, 19); + return 0; +} + +//Dissolution party notification +static int mapif_party_broken(int party_id, int flag) +{ + unsigned char buf[16]; + WBUFW(buf, 0) = 0x3826; + WBUFL(buf, 2) = party_id; + WBUFB(buf, 6) = flag; + mapif->sendall(buf, 7); + //printf("int_party: broken %d\n",party_id); + return 0; +} + +//Remarks in the party +static int mapif_party_message(int party_id, int account_id, const char *mes, int len, int sfd) +{ + unsigned char buf[512]; + nullpo_ret(mes); + WBUFW(buf, 0) = 0x3827; + WBUFW(buf, 2) = len + 12; + WBUFL(buf, 4) = party_id; + WBUFL(buf, 8) = account_id; + memcpy(WBUFP(buf, 12), mes, len); + mapif->sendallwos(sfd, buf, len + 12); + return 0; +} + +// Create Party +static int mapif_parse_CreateParty(int fd, const char *name, int item, int item2, const struct party_member *leader) +{ + struct party_data *p; + + nullpo_ret(name); + nullpo_ret(leader); + + p = inter_party->create(name, item, item2, leader); + + if (p == NULL) { + mapif->party_created(fd, leader->account_id, leader->char_id, NULL); + return 0; + } + + mapif->party_info(fd, &p->party, 0); + mapif->party_created(fd, leader->account_id, leader->char_id, &p->party); + + return 0; +} + +// Party information request +static void mapif_parse_PartyInfo(int fd, int party_id, int char_id) +{ + struct party_data *p; + p = inter_party->fromsql(party_id); + + if (p != NULL) + mapif->party_info(fd, &p->party, char_id); + else + mapif->party_noinfo(fd, party_id, char_id); +} + +// Add a player to party request +static int mapif_parse_PartyAddMember(int fd, int party_id, const struct party_member *member) +{ + nullpo_ret(member); + + if (!inter_party->add_member(party_id, member)) { + mapif->party_memberadded(fd, party_id, member->account_id, member->char_id, 1); + return 0; + } + mapif->party_memberadded(fd, party_id, member->account_id, member->char_id, 0); + + return 0; +} + +//Party setting change request +static int mapif_parse_PartyChangeOption(int fd, int party_id, int account_id, int exp, int item) +{ + inter_party->change_option(party_id, account_id, exp, item, fd); + return 0; +} + +//Request leave party +static int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) +{ + inter_party->leave(party_id, account_id, char_id); + return 0; +} + +// When member goes to other map or levels up. +static int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv) +{ + inter_party->change_map(party_id, account_id, char_id, map, online, lv); + return 0; +} + +//Request party dissolution +static int mapif_parse_BreakParty(int fd, int party_id) +{ + inter_party->disband(party_id); + return 0; +} + +//Party sending the message +static int mapif_parse_PartyMessage(int fd, int party_id, int account_id, const char *mes, int len) +{ + return mapif->party_message(party_id, account_id, mes, len, fd); +} + +static int mapif_parse_PartyLeaderChange(int fd, int party_id, int account_id, int char_id) +{ + if (!inter_party->change_leader(party_id, account_id, char_id)) + return 0; + return 1; +} + +static int mapif_pet_created(int fd, int account_id, struct s_pet *p) +{ + WFIFOHEAD(fd, 12); + WFIFOW(fd, 0) = 0x3880; + WFIFOL(fd, 2) = account_id; + if (p != NULL){ + WFIFOW(fd, 6) = p->class_; + WFIFOL(fd, 8) = p->pet_id; + ShowInfo("int_pet: created pet %d - %s\n", p->pet_id, p->name); + } else { + WFIFOB(fd, 6) = 0; + WFIFOL(fd, 8) = 0; + } + WFIFOSET(fd, 12); + + return 0; +} + +static int mapif_pet_info(int fd, int account_id, struct s_pet *p) +{ + nullpo_ret(p); + WFIFOHEAD(fd, sizeof(struct s_pet) + 9); + WFIFOW(fd, 0) = 0x3881; + WFIFOW(fd, 2) = sizeof(struct s_pet) + 9; + WFIFOL(fd, 4) = account_id; + WFIFOB(fd, 8) = 0; + memcpy(WFIFOP(fd, 9), p, sizeof(struct s_pet)); + WFIFOSET(fd, WFIFOW(fd, 2)); + + return 0; +} + +static int mapif_pet_noinfo(int fd, int account_id) +{ + WFIFOHEAD(fd, sizeof(struct s_pet) + 9); + WFIFOW(fd, 0) = 0x3881; + WFIFOW(fd, 2) = sizeof(struct s_pet) + 9; + WFIFOL(fd, 4) = account_id; + WFIFOB(fd, 8) = 1; + memset(WFIFOP(fd, 9), 0, sizeof(struct s_pet)); + WFIFOSET(fd, WFIFOW(fd, 2)); + + return 0; +} + +static int mapif_save_pet_ack(int fd, int account_id, int flag) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x3882; + WFIFOL(fd, 2) = account_id; + WFIFOB(fd, 6) = flag; + WFIFOSET(fd, 7); + + return 0; +} + +static int mapif_delete_pet_ack(int fd, int flag) +{ + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x3883; + WFIFOB(fd, 2) = flag; + WFIFOSET(fd, 3); + + return 0; +} + +static int mapif_save_pet(int fd, int account_id, const struct s_pet *data) +{ + //here process pet save request. + int len; + nullpo_ret(data); + RFIFOHEAD(fd); + len = RFIFOW(fd, 2); + if (sizeof(struct s_pet) != len-8) { + ShowError("inter pet: data size mismatch: %d != %"PRIuS"\n", len-8, sizeof(struct s_pet)); + return 0; + } + + inter_pet->tosql(data); + mapif->save_pet_ack(fd, account_id, 0); + + return 0; +} + +static int mapif_delete_pet(int fd, int pet_id) +{ + mapif->delete_pet_ack(fd, inter_pet->delete_(pet_id)); + + return 0; +} + +static int mapif_parse_CreatePet(int fd) +{ + int account_id; + struct s_pet *pet; + + RFIFOHEAD(fd); + account_id = RFIFOL(fd, 2); + pet = inter_pet->create(account_id, + RFIFOL(fd, 6), + RFIFOW(fd, 10), + RFIFOW(fd, 12), + RFIFOL(fd, 14), + RFIFOL(fd, 18), + RFIFOW(fd, 22), + RFIFOW(fd, 24), + RFIFOB(fd, 26), + RFIFOB(fd, 27), + RFIFOP(fd, 28)); + + if (pet != NULL) + mapif->pet_created(fd, account_id, pet); + else + mapif->pet_created(fd, account_id, NULL); + + return 0; +} + +static int mapif_parse_LoadPet(int fd) +{ + int account_id; + struct s_pet *pet; + + RFIFOHEAD(fd); + account_id = RFIFOL(fd, 2); + pet = inter_pet->load(account_id, RFIFOL(fd, 6), RFIFOL(fd, 10)); + + if (pet != NULL) + mapif->pet_info(fd, account_id, pet); + else + mapif->pet_noinfo(fd, account_id); + return 0; +} + +static int mapif_parse_SavePet(int fd) +{ + RFIFOHEAD(fd); + mapif->save_pet(fd, RFIFOL(fd, 4), RFIFOP(fd, 8)); + return 0; +} + +static int mapif_parse_DeletePet(int fd) +{ + RFIFOHEAD(fd); + mapif->delete_pet(fd, RFIFOL(fd, 2)); + return 0; +} + +static void mapif_quest_save_ack(int fd, int char_id, bool success) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x3861; + WFIFOL(fd, 2) = char_id; + WFIFOB(fd, 6) = success ? 1 : 0; + WFIFOSET(fd, 7); +} + +/** + * Handles the save request from mapserver for a character's questlog. + * + * Received quests are saved, and an ack is sent back to the map server. + * + * @see inter_parse_frommap + */ +static int mapif_parse_quest_save(int fd) +{ + int num = (RFIFOW(fd, 2) - 8) / sizeof(struct quest); + int char_id = RFIFOL(fd, 4); + const struct quest *qd = NULL; + bool success; + + if (num > 0) + qd = RFIFOP(fd,8); + + success = inter_quest->save(char_id, qd, num); + + // Send ack + mapif->quest_save_ack(fd, char_id, success); + + return 0; +} + +static void mapif_send_quests(int fd, int char_id, struct quest *tmp_questlog, int num_quests) +{ + WFIFOHEAD(fd,num_quests*sizeof(struct quest) + 8); + WFIFOW(fd, 0) = 0x3860; + WFIFOW(fd, 2) = num_quests*sizeof(struct quest) + 8; + WFIFOL(fd, 4) = char_id; + + if (num_quests > 0) { + nullpo_retv(tmp_questlog); + memcpy(WFIFOP(fd, 8), tmp_questlog, sizeof(struct quest) * num_quests); + } + + WFIFOSET(fd, num_quests * sizeof(struct quest) + 8); +} + +/** + * Sends questlog to the map server + * + * Note: Completed quests (state == Q_COMPLETE) are guaranteed to be sent last + * and the map server relies on this behavior (once the first Q_COMPLETE quest, + * all of them are considered to be Q_COMPLETE) + * + * @see inter_parse_frommap + */ +static int mapif_parse_quest_load(int fd) +{ + int char_id = RFIFOL(fd,2); + struct quest *tmp_questlog = NULL; + int num_quests; + + tmp_questlog = inter_quest->fromsql(char_id, &num_quests); + mapif->send_quests(fd, char_id, tmp_questlog, num_quests); + + if (tmp_questlog != NULL) + aFree(tmp_questlog); + + return 0; +} + +/* RoDEX */ + +/*========================================== + * Inbox Request + *------------------------------------------*/ +static void mapif_parse_rodex_requestinbox(int fd) +{ + int count; + int char_id = RFIFOL(fd,2); + int account_id = RFIFOL(fd, 6); + int8 flag = RFIFOB(fd, 10); + int8 opentype = RFIFOB(fd, 11); + int64 mail_id = RFIFOQ(fd, 12); + struct rodex_maillist mails = { 0 }; + + VECTOR_INIT(mails); + if (flag == 0) + count = inter_rodex->fromsql(char_id, account_id, opentype, 0, &mails); + else + count = inter_rodex->fromsql(char_id, account_id, opentype, mail_id, &mails); + mapif->rodex_sendinbox(fd, char_id, opentype, flag, count, mail_id, &mails); + VECTOR_CLEAR(mails); +} + +static void mapif_rodex_sendinbox(int fd, int char_id, int8 opentype, int8 flag, int count, int64 mail_id, struct rodex_maillist *mails) +{ + int per_packet = (UINT16_MAX - 24) / sizeof(struct rodex_message); + int sent = 0; + bool is_first = true; + nullpo_retv(mails); + Assert_retv(char_id > 0); + Assert_retv(count >= 0); + Assert_retv(mail_id >= 0); + + do { + int i = 24, j, size, limit; + int to_send = count - sent; + bool is_last = true; + + if (to_send <= per_packet) { + size = to_send * sizeof(struct rodex_message) + 24; + limit = to_send; + is_last = true; + } else { + limit = min(to_send, per_packet); + if (limit != to_send) { + is_last = false; + } + size = limit * sizeof(struct rodex_message) + 24; + } + + WFIFOHEAD(fd, size); + WFIFOW(fd, 0) = 0x3895; + WFIFOW(fd, 2) = size; + WFIFOL(fd, 4) = char_id; + WFIFOB(fd, 8) = opentype; + WFIFOB(fd, 9) = flag; + WFIFOB(fd, 10) = is_last; + WFIFOB(fd, 11) = is_first; + WFIFOL(fd, 12) = limit; + WFIFOQ(fd, 16) = mail_id; + for (j = 0; j < limit; ++j, ++sent, i += sizeof(struct rodex_message)) { + memcpy(WFIFOP(fd, i), &VECTOR_INDEX(*mails, sent), sizeof(struct rodex_message)); + } + WFIFOSET(fd, size); + + is_first = false; + } while (sent < count); +} + +/*========================================== + * Checks if there are new mails + *------------------------------------------*/ +static void mapif_parse_rodex_checkhasnew(int fd) +{ + int char_id = RFIFOL(fd, 2); + int account_id = RFIFOL(fd, 6); + bool has_new; + + Assert_retv(account_id >= START_ACCOUNT_NUM && account_id <= END_ACCOUNT_NUM); + Assert_retv(char_id >= START_CHAR_NUM); + + has_new = inter_rodex->hasnew(char_id, account_id); + mapif->rodex_sendhasnew(fd, char_id, has_new); +} + +static void mapif_rodex_sendhasnew(int fd, int char_id, bool has_new) +{ + Assert_retv(char_id > 0); + + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x3896; + WFIFOL(fd, 2) = char_id; + WFIFOB(fd, 6) = has_new; + WFIFOSET(fd, 7); +} + +/*========================================== + * Update/Delete mail + *------------------------------------------*/ +static void mapif_parse_rodex_updatemail(int fd) +{ + int64 mail_id = RFIFOL(fd, 2); + int8 flag = RFIFOB(fd, 10); + + inter_rodex->updatemail(mail_id, flag); +} + +/*========================================== + * Send Mail + *------------------------------------------*/ +static void mapif_parse_rodex_send(int fd) +{ + struct rodex_message msg = { 0 }; + + if (RFIFOW(fd,2) != 4 + sizeof(struct rodex_message)) + return; + + memcpy(&msg, RFIFOP(fd,4), sizeof(struct rodex_message)); + if (msg.receiver_id > 0 || msg.receiver_accountid > 0) + msg.id = inter_rodex->savemessage(&msg); + + mapif->rodex_send(fd, msg.sender_id, msg.receiver_id, msg.receiver_accountid, msg.id > 0 ? true : false); +} + +static void mapif_rodex_send(int fd, int sender_id, int receiver_id, int receiver_accountid, bool result) +{ + Assert_retv(sender_id >= 0); + Assert_retv(receiver_id + receiver_accountid > 0); + + WFIFOHEAD(fd,15); + WFIFOW(fd,0) = 0x3897; + WFIFOL(fd,2) = sender_id; + WFIFOL(fd,6) = receiver_id; + WFIFOL(fd,10) = receiver_accountid; + WFIFOB(fd,14) = result; + WFIFOSET(fd,15); +} + +/*------------------------------------------ + * Check Player + *------------------------------------------*/ +static void mapif_parse_rodex_checkname(int fd) +{ + int reqchar_id = RFIFOL(fd, 2); + char name[NAME_LENGTH]; + int target_char_id, target_level; + short target_class; + + safestrncpy(name, RFIFOP(fd, 6), NAME_LENGTH); + + if (inter_rodex->checkname(name, &target_char_id, &target_class, &target_level) == true) + mapif->rodex_checkname(fd, reqchar_id, target_char_id, target_class, target_level, name); + else + mapif->rodex_checkname(fd, reqchar_id, 0, 0, 0, name); +} + +static void mapif_rodex_checkname(int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char *name) +{ + nullpo_retv(name); + Assert_retv(reqchar_id > 0); + Assert_retv(target_char_id >= 0); + + WFIFOHEAD(fd, 16 + NAME_LENGTH); + WFIFOW(fd, 0) = 0x3898; + WFIFOL(fd, 2) = reqchar_id; + WFIFOL(fd, 6) = target_char_id; + WFIFOW(fd, 10) = target_class; + WFIFOL(fd, 12) = target_level; + safestrncpy(WFIFOP(fd, 16), name, NAME_LENGTH); + WFIFOSET(fd, 16 + NAME_LENGTH); +} + +static int mapif_load_guild_storage(int fd, int account_id, int guild_id, char flag) +{ + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", guild_db, guild_id)) { + Sql_ShowDebug(inter->sql_handle); + } else if (SQL->NumRows(inter->sql_handle) > 0) { + // guild exists + WFIFOHEAD(fd, sizeof(struct guild_storage) + 13); + WFIFOW(fd, 0) = 0x3818; + WFIFOW(fd, 2) = sizeof(struct guild_storage)+13; + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = guild_id; + WFIFOB(fd, 12) = flag; //1 open storage, 0 don't open + inter_storage->guild_storage_fromsql(guild_id, WFIFOP(fd, 13)); + WFIFOSET(fd, WFIFOW(fd, 2)); + return 0; + } + // guild does not exist + SQL->FreeResult(inter->sql_handle); + WFIFOHEAD(fd, 12); + WFIFOW(fd, 0) = 0x3818; + WFIFOW(fd, 2) = 12; + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = 0; + WFIFOSET(fd, 12); + return 0; +} + +static int mapif_save_guild_storage_ack(int fd, int account_id, int guild_id, int fail) +{ + WFIFOHEAD(fd, 11); + WFIFOW(fd, 0) = 0x3819; + WFIFOL(fd, 2) = account_id; + WFIFOL(fd, 6) = guild_id; + WFIFOB(fd, 10) = fail; + WFIFOSET(fd, 11); + return 0; +} + +/** + * Loads the account storage and send to the map server. + * @packet 0x3805 [out] <account_id>.L <struct item[]>.P + * @param fd [in] file/socket descriptor. + * @param account_id [in] account id of the session. + * @return 1 on success, 0 on failure. + */ +static int mapif_account_storage_load(int fd, int account_id) +{ + struct storage_data stor = { 0 }; + int count = 0, i = 0, len = 0; + + Assert_ret(account_id > 0); + + VECTOR_INIT(stor.item); + count = inter_storage->fromsql(account_id, &stor); + + len = 8 + count * sizeof(struct item); + + WFIFOHEAD(fd, len); + WFIFOW(fd, 0) = 0x3805; + WFIFOW(fd, 2) = (uint16) len; + WFIFOL(fd, 4) = account_id; + for (i = 0; i < count; i++) + memcpy(WFIFOP(fd, 8 + i * sizeof(struct item)), &VECTOR_INDEX(stor.item, i), sizeof(struct item)); + WFIFOSET(fd, len); + + VECTOR_CLEAR(stor.item); + + return 1; +} + +/** + * Parses account storage load request from map server. + * @packet 0x3010 [in] <account_id>.L + * @param fd [in] file/socket descriptor + * @return 1 on success, 0 on failure. + */ +static int mapif_parse_AccountStorageLoad(int fd) +{ + int account_id = RFIFOL(fd, 2); + + Assert_ret(fd > 0); + Assert_ret(account_id > 0); + + mapif->account_storage_load(fd, account_id); + + return 1; +} + +/** + * Parses an account storage save request from the map server. + * @packet 0x3011 [in] <packet_len>.W <account_id>.L <struct item[]>.P + * @param fd [in] file/socket descriptor. + * @return 1 on success, 0 on failure. + */ +static int mapif_parse_AccountStorageSave(int fd) +{ + int payload_size = RFIFOW(fd, 2) - 8, account_id = RFIFOL(fd, 4); + int i = 0, count = 0; + struct storage_data p_stor = { 0 }; + + Assert_ret(fd > 0); + Assert_ret(account_id > 0); + + count = payload_size/sizeof(struct item); + + VECTOR_INIT(p_stor.item); + + if (count > 0) { + VECTOR_ENSURE(p_stor.item, count, 1); + + for (i = 0; i < count; i++) { + const struct item *it = RFIFOP(fd, 8 + i * sizeof(struct item)); + + VECTOR_PUSH(p_stor.item, *it); + } + + p_stor.aggregate = count; + } + + inter_storage->tosql(account_id, &p_stor); + + VECTOR_CLEAR(p_stor.item); + + mapif->sAccountStorageSaveAck(fd, account_id, true); + + return 1; +} + +/** + * Sends an acknowledgement for the save + * status of the account storage. + * @packet 0x3808 [out] <account_id>.L <save_flag>.B + * @param fd [in] File/Socket Descriptor. + * @param account_id [in] Account ID of the storage in question. + * @param flag [in] Save flag, true for success and false for failure. + */ +static void mapif_send_AccountStorageSaveAck(int fd, int account_id, bool flag) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x3808; + WFIFOL(fd, 2) = account_id; + WFIFOB(fd, 6) = flag ? 1 : 0; + WFIFOSET(fd, 7); +} + +static int mapif_parse_LoadGuildStorage(int fd) +{ + RFIFOHEAD(fd); + + mapif->load_guild_storage(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), 1); + + return 0; +} + +static int mapif_parse_SaveGuildStorage(int fd) +{ + int guild_id; + int len; + + RFIFOHEAD(fd); + guild_id = RFIFOL(fd, 8); + len = RFIFOW(fd, 2); + + if (sizeof(struct guild_storage) != len - 12) { + ShowError("inter storage: data size mismatch: %d != %"PRIuS"\n", len - 12, sizeof(struct guild_storage)); + } else if (inter_storage->guild_storage_tosql(guild_id, RFIFOP(fd, 12))) { + mapif->save_guild_storage_ack(fd, RFIFOL(fd, 4), guild_id, 0); + return 0; + } + mapif->save_guild_storage_ack(fd, RFIFOL(fd, 4), guild_id, 1); + + return 0; +} + +static int mapif_itembound_ack(int fd, int aid, int guild_id) +{ +#ifdef GP_BOUND_ITEMS + WFIFOHEAD(fd, 8); + WFIFOW(fd, 0) = 0x3856; + WFIFOL(fd, 2) = aid;/* the value is not being used, drop? */ + WFIFOW(fd, 6) = guild_id; + WFIFOSET(fd, 8); +#endif + return 0; +} + +static void mapif_parse_ItemBoundRetrieve(int fd) +{ +#ifdef GP_BOUND_ITEMS + int char_id = RFIFOL(fd, 2); + int account_id = RFIFOL(fd, 6); + int guild_id = RFIFOW(fd, 10); + + inter_storage->retrieve_bound_items(char_id, account_id, guild_id); + + //Finally reload storage and tell map we're done + mapif->load_guild_storage(fd, account_id, guild_id, 0); + + // If character is logged in char, disconnect + chr->disconnect_player(account_id); +#endif // GP_BOUND_ITEMS + + /* tell map server the operation is over and it can unlock the storage */ + mapif->itembound_ack(fd, RFIFOL(fd, 6), RFIFOW(fd, 10)); +} + +static void mapif_parse_accinfo(int fd) +{ + char query[NAME_LENGTH]; + int u_fd = RFIFOL(fd, 2), aid = RFIFOL(fd, 6), castergroup = RFIFOL(fd, 10); + + safestrncpy(query, RFIFOP(fd, 14), NAME_LENGTH); + + inter->accinfo(u_fd, aid, castergroup, query, fd); +} + +// broadcast sending +static int mapif_broadcast(const unsigned char *mes, int len, unsigned int fontColor, short fontType, short fontSize, short fontAlign, short fontY, int sfd) +{ + unsigned char *buf = (unsigned char*)aMalloc((len)*sizeof(unsigned char)); + + nullpo_ret(mes); + Assert_ret(len >= 16); + WBUFW(buf, 0) = 0x3800; + WBUFW(buf, 2) = len; + WBUFL(buf, 4) = fontColor; + WBUFW(buf, 8) = fontType; + WBUFW(buf, 10) = fontSize; + WBUFW(buf, 12) = fontAlign; + WBUFW(buf, 14) = fontY; + memcpy(WBUFP(buf, 16), mes, len - 16); + mapif->sendallwos(sfd, buf, len); + + aFree(buf); + return 0; +} + +// Wis sending +static int mapif_wis_message(struct WisData *wd) +{ + unsigned char buf[2048]; + nullpo_ret(wd); + //if (wd->len > 2047-56) wd->len = 2047-56; //Force it to fit to avoid crashes. [Skotlex] + if (wd->len < 0) + wd->len = 0; + if (wd->len >= (int)sizeof(wd->msg) - 1) + wd->len = (int)sizeof(wd->msg) - 1; + + WBUFW(buf, 0) = 0x3801; + WBUFW(buf, 2) = 56 + wd->len; + WBUFL(buf, 4) = wd->id; + memcpy(WBUFP(buf, 8), wd->src, NAME_LENGTH); + memcpy(WBUFP(buf, 32), wd->dst, NAME_LENGTH); + memcpy(WBUFP(buf, 56), wd->msg, wd->len); + wd->count = mapif->sendall(buf, WBUFW(buf, 2)); + + return 0; +} + +static void mapif_wis_response(int fd, const unsigned char *src, int flag) +{ + unsigned char buf[27]; + nullpo_retv(src); + WBUFW(buf, 0) = 0x3802; + memcpy(WBUFP(buf, 2), src, 24); + WBUFB(buf, 26) = flag; + mapif->send(fd, buf, 27); +} + +// Wis sending result +static int mapif_wis_end(struct WisData *wd, int flag) +{ + nullpo_ret(wd); + mapif->wis_response(wd->fd, wd->src, flag); + return 0; +} + +#if 0 +// Account registry transfer to map-server +static void mapif_account_reg(int fd, unsigned char *src) +{ + nullpo_retv(src); + WBUFW(src, 0) = 0x3804; //NOTE: writing to RFIFO + mapif->sendallwos(fd, src, WBUFW(src, 2)); +} +#endif // 0 + +// Send the requested account_reg +static int mapif_account_reg_reply(int fd,int account_id,int char_id, int type) +{ + inter->accreg_fromsql(account_id, char_id, fd, type); + return 0; +} + +//Request to kick char from a certain map server. [Skotlex] +static int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason) +{ + if (fd < 0) + return -1; + + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x2b1f; + WFIFOL(fd, 2) = account_id; + WFIFOB(fd, 6) = reason; + WFIFOSET(fd, 7); + return 0; +} + +// broadcast sending +static int mapif_parse_broadcast(int fd) +{ + mapif->broadcast(RFIFOP(fd, 16), RFIFOW(fd, 2), RFIFOL(fd, 4), RFIFOW(fd, 8), RFIFOW(fd, 10), RFIFOW(fd, 12), RFIFOW(fd, 14), fd); + return 0; +} + +// Wisp/page request to send +static int mapif_parse_WisRequest(int fd) +{ + struct WisData* wd; + char name[NAME_LENGTH]; + char *data; + size_t len; + + if (fd <= 0) // check if we have a valid fd + return 0; + + if (RFIFOW(fd, 2) - 52 >= sizeof(wd->msg)) { + ShowWarning("inter: Wis message size too long.\n"); + return 0; + } else if (RFIFOW(fd, 2) - 52 <= 0) { // normally, impossible, but who knows... + ShowError("inter: Wis message doesn't exist.\n"); + return 0; + } + + safestrncpy(name, RFIFOP(fd, 28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex] + + // search if character exists before to ask all map-servers + if (!chr->name_exists(name, NULL)) { + mapif->wis_response(fd, RFIFOP(fd, 4), 1); + } else { + // Character exists. So, ask all map-servers + + // to be sure of the correct name, rewrite it + SQL->GetData(inter->sql_handle, 0, &data, &len); + memset(name, 0, NAME_LENGTH); + memcpy(name, data, min(len, NAME_LENGTH)); + // if source is destination, don't ask other servers. + if (strncmp(RFIFOP(fd, 4), name, NAME_LENGTH) == 0) { + mapif->wis_response(fd, RFIFOP(fd, 4), 1); + } else { + wd = inter->add_wisdata(fd, RFIFOP(fd, 4), RFIFOP(fd, 28), RFIFOP(fd, 52), RFIFOW(fd, 2) - 52); + mapif->wis_message(wd); + } + } + + SQL->FreeResult(inter->sql_handle); + return 0; +} + +// Wisp/page transmission result +static int mapif_parse_WisReply(int fd) +{ + int id, flag; + struct WisData *wd; + + id = RFIFOL(fd,2); + flag = RFIFOB(fd,6); + wd = inter->get_wisdata(id); + if (wd == NULL) + return 0; // This wisp was probably suppress before, because it was timeout of because of target was found on another map-server + + if ((--wd->count) <= 0 || flag != 1) { + mapif->wis_end(wd, flag); // flag: 0: success to send whisper, 1: target character is not logged in?, 2: ignored by target + inter->remove_wisdata(id); + } + + return 0; +} + +// Received wisp message from map-server for ALL gm (just copy the message and resends it to ALL map-servers) +static int mapif_parse_WisToGM(int fd) +{ + unsigned char buf[2048]; // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B + + memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2)); // Message contains the NUL terminator (see intif_wis_message_to_gm()) + WBUFW(buf, 0) = 0x3803; + mapif->sendall(buf, RFIFOW(fd,2)); + + return 0; +} + +// Save account_reg into sql (type=2) +static int mapif_parse_Registry(int fd) +{ + int account_id = RFIFOL(fd, 4), char_id = RFIFOL(fd, 8), count = RFIFOW(fd, 12); + + if (count != 0) { + int cursor = 14, i; + char key[SCRIPT_VARNAME_LENGTH+1], sval[254]; + bool isLoginActive = sockt->session_is_active(chr->login_fd); + + if (isLoginActive) + chr->global_accreg_to_login_start(account_id, char_id); + + for (i = 0; i < count; i++) { + unsigned int index; + int len = RFIFOB(fd, cursor); + safestrncpy(key, RFIFOP(fd, cursor + 1), min((int)sizeof(key), len)); + cursor += len + 1; + + index = RFIFOL(fd, cursor); + cursor += 4; + + switch (RFIFOB(fd, cursor++)) { + /* int */ + case 0: + inter->savereg(account_id, char_id, key, index, RFIFOL(fd, cursor), false); + cursor += 4; + break; + case 1: + inter->savereg(account_id, char_id, key, index, 0, false); + break; + /* str */ + case 2: + len = RFIFOB(fd, cursor); + safestrncpy(sval, RFIFOP(fd, cursor + 1), min((int)sizeof(sval), len)); + cursor += len + 1; + inter->savereg(account_id, char_id, key, index, (intptr_t)sval, true); + break; + case 3: + inter->savereg(account_id, char_id, key, index, 0, true); + break; + default: + ShowError("mapif->parse_Registry: unknown type %d\n", RFIFOB(fd, cursor - 1)); + return 1; + } + } + + if (isLoginActive) + chr->global_accreg_to_login_send(); + } + return 0; +} + +// Request the value of all registries. +static int mapif_parse_RegistryRequest(int fd) +{ + //Load Char Registry + if (RFIFOB(fd, 12)) + mapif->account_reg_reply(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), 3); // 3: char reg + //Load Account Registry + if (RFIFOB(fd, 11) != 0) + mapif->account_reg_reply(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), 2); // 2: account reg + //Ask Login Server for Account2 values. + if (RFIFOB(fd, 10) != 0) + chr->request_accreg2(RFIFOL(fd, 2), RFIFOL(fd, 6)); + return 1; +} + +static void mapif_namechange_ack(int fd, int account_id, int char_id, int type, int flag, const char *const name) +{ + nullpo_retv(name); + WFIFOHEAD(fd, NAME_LENGTH+13); + WFIFOW(fd, 0) = 0x3806; + WFIFOL(fd, 2) = account_id; + WFIFOL(fd, 6) = char_id; + WFIFOB(fd, 10) = type; + WFIFOB(fd, 11) = flag; + memcpy(WFIFOP(fd, 12), name, NAME_LENGTH); + WFIFOSET(fd, NAME_LENGTH + 13); +} + +static int mapif_parse_NameChangeRequest(int fd) +{ + int account_id, char_id, type; + const char *name; + int i; + + account_id = RFIFOL(fd, 2); + char_id = RFIFOL(fd, 6); + type = RFIFOB(fd, 10); + name = RFIFOP(fd, 11); + + // Check Authorized letters/symbols in the name + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorized + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + mapif->namechange_ack(fd, account_id, char_id, type, 0, name); + return 0; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + mapif->namechange_ack(fd, account_id, char_id, type, 0, name); + return 0; + } + } + //TODO: type holds the type of object to rename. + //If it were a player, it needs to have the guild information and db information + //updated here, because changing it on the map won't make it be saved [Skotlex] + + //name allowed. + mapif->namechange_ack(fd, account_id, char_id, type, 1, name); + return 0; +} + +// Clan System +static int mapif_parse_ClanMemberKick(int fd, int clan_id, int kick_interval) +{ + int count = 0; + + if (inter_clan->kick_inactive_members(clan_id, kick_interval) == 1) + count = inter_clan->count_members(clan_id, kick_interval); + + WFIFOHEAD(fd, 10); + WFIFOW(fd, 0) = 0x3858; + WFIFOL(fd, 2) = clan_id; + WFIFOL(fd, 6) = count; + WFIFOSET(fd, 10); + return 0; +} + +static int mapif_parse_ClanMemberCount(int fd, int clan_id, int kick_interval) +{ + WFIFOHEAD(fd, 10); + WFIFOW(fd, 0) = 0x3858; + WFIFOL(fd, 2) = clan_id; + WFIFOL(fd, 6) = inter_clan->count_members(clan_id, kick_interval); + WFIFOSET(fd, 10); + return 0; +} + +// Achievement System +/** + * Parse achievement load request from the map server + * @param[in] fd socket descriptor. + */ +static void mapif_parse_load_achievements(int fd) +{ + int char_id = 0; + + /* Read received information from map-server. */ + RFIFOHEAD(fd); + char_id = RFIFOL(fd, 2); + + /* Load and send achievements to map */ + mapif->achievement_load(fd, char_id); +} + +/** + * Loads achievements and sends to the map server. + * @param[in] fd socket descriptor + * @param[in] char_id character Id. + */ +static void mapif_achievement_load(int fd, int char_id) +{ + struct char_achievements *cp = NULL; + + /* Ensure data exists */ + cp = idb_ensure(inter_achievement->char_achievements, char_id, inter_achievement->ensure_char_achievements); + + /* Load storage for char-server. */ + inter_achievement->fromsql(char_id, cp); + + /* Send Achievements to map server. */ + mapif->sAchievementsToMap(fd, char_id, cp); +} + +/** + * Sends achievement data of a character to the map server. + * @packet[out] 0x3810 <packet_id>.W <payload_size>.W <char_id>.L <char_achievements[]>.P + * @param[in] fd socket descriptor. + * @param[in] char_id Character ID. + * @param[in] cp Pointer to character's achievement data vector. + */ +static void mapif_send_achievements_to_map(int fd, int char_id, const struct char_achievements *cp) +{ + int i = 0; + int data_size = 0; + + nullpo_retv(cp); + + data_size = sizeof(struct achievement) * VECTOR_LENGTH(*cp); + +STATIC_ASSERT((sizeof(struct achievement) * MAX_ACHIEVEMENT_DB + 8 <= UINT16_MAX), + "The achievements data can potentially be larger than the maximum packet size. This may cause errors at run-time."); + + /* Send to the map server. */ + WFIFOHEAD(fd, (8 + data_size)); + WFIFOW(fd, 0) = 0x3810; + WFIFOW(fd, 2) = (8 + data_size); + WFIFOL(fd, 4) = char_id; + for (i = 0; i < VECTOR_LENGTH(*cp); i++) + memcpy(WFIFOP(fd, 8 + i * sizeof(struct achievement)), &VECTOR_INDEX(*cp, i), sizeof(struct achievement)); + WFIFOSET(fd, 8 + data_size); +} + +/** + * Handles achievement request and saves data from map server. + * @packet[in] 0x3013 <packet_size>.W <char_id>.L <char_achievement>.P + * @param[in] fd session socket descriptor. + */ +static void mapif_parse_save_achievements(int fd) +{ + int size = 0, char_id = 0, payload_count = 0, i = 0; + struct char_achievements p = { 0 }; + + RFIFOHEAD(fd); + size = RFIFOW(fd, 2); + char_id = RFIFOL(fd, 4); + + payload_count = (size - 8) / sizeof(struct achievement); + + VECTOR_INIT(p); + VECTOR_ENSURE(p, payload_count, 1); + + for (i = 0; i < payload_count; i++) { + struct achievement ach = { 0 }; + memcpy(&ach, RFIFOP(fd, 8 + i * sizeof(struct achievement)), sizeof(struct achievement)); + VECTOR_PUSH(p, ach); + } + + mapif->achievement_save(char_id, &p); + + VECTOR_CLEAR(p); +} + +/** + * Handles inter-server achievement db ensuring + * and saves current achievements to sql. + * @param[in] char_id character identifier. + * @param[out] p pointer to character achievements vector. + */ +static void mapif_achievement_save(int char_id, struct char_achievements *p) +{ + struct char_achievements *cp = NULL; + + nullpo_retv(p); + + /* Get loaded achievements. */ + cp = idb_ensure(inter_achievement->char_achievements, char_id, inter_achievement->ensure_char_achievements); + + if (VECTOR_LENGTH(*p)) /* Save current achievements. */ + inter_achievement->tosql(char_id, cp, p); +} + +void mapif_defaults(void) +{ mapif = &mapif_s; mapif->ban = mapif_ban; @@ -224,6 +2476,11 @@ void mapif_defaults(void) { mapif->sendallwos = mapif_sendallwos; mapif->send = mapif_send; mapif->send_users_count = mapif_send_users_count; + mapif->pLoadAchievements = mapif_parse_load_achievements; + mapif->sAchievementsToMap = mapif_send_achievements_to_map; + mapif->pSaveAchievements = mapif_parse_save_achievements; + mapif->achievement_load = mapif_achievement_load; + mapif->achievement_save = mapif_achievement_save; mapif->auction_message = mapif_auction_message; mapif->auction_sendlist = mapif_auction_sendlist; mapif->parse_auction_requestlist = mapif_parse_auction_requestlist; @@ -235,10 +2492,6 @@ void mapif_defaults(void) { mapif->parse_auction_close = mapif_parse_auction_close; mapif->auction_bid = mapif_auction_bid; mapif->parse_auction_bid = mapif_parse_auction_bid; - mapif->elemental_create = mapif_elemental_create; - mapif->elemental_save = mapif_elemental_save; - mapif->elemental_load = mapif_elemental_load; - mapif->elemental_delete = mapif_elemental_delete; mapif->elemental_send = mapif_elemental_send; mapif->parse_elemental_create = mapif_parse_elemental_create; mapif->parse_elemental_load = mapif_parse_elemental_load; @@ -274,7 +2527,6 @@ void mapif_defaults(void) { mapif->parse_GuildMemberInfoChange = mapif_parse_GuildMemberInfoChange; mapif->parse_GuildPosition = mapif_parse_GuildPosition; mapif->parse_GuildSkillUp = mapif_parse_GuildSkillUp; - mapif->parse_GuildDeleteAlliance = mapif_parse_GuildDeleteAlliance; mapif->parse_GuildAlliance = mapif_parse_GuildAlliance; mapif->parse_GuildNotice = mapif_parse_GuildNotice; mapif->parse_GuildEmblem = mapif_parse_GuildEmblem; @@ -286,11 +2538,6 @@ void mapif_defaults(void) { mapif->homunculus_loaded = mapif_homunculus_loaded; mapif->homunculus_saved = mapif_homunculus_saved; mapif->homunculus_renamed = mapif_homunculus_renamed; - mapif->homunculus_create = mapif_homunculus_create; - mapif->homunculus_save = mapif_homunculus_save; - mapif->homunculus_load = mapif_homunculus_load; - mapif->homunculus_delete = mapif_homunculus_delete; - mapif->homunculus_rename = mapif_homunculus_rename; mapif->parse_homunculus_create = mapif_parse_homunculus_create; mapif->parse_homunculus_delete = mapif_parse_homunculus_delete; mapif->parse_homunculus_load = mapif_parse_homunculus_load; @@ -300,7 +2547,6 @@ void mapif_defaults(void) { mapif->parse_mail_requestinbox = mapif_parse_mail_requestinbox; mapif->parse_mail_read = mapif_parse_mail_read; mapif->mail_sendattach = mapif_mail_sendattach; - mapif->mail_getattach = mapif_mail_getattach; mapif->parse_mail_getattach = mapif_parse_mail_getattach; mapif->mail_delete = mapif_mail_delete; mapif->parse_mail_delete = mapif_parse_mail_delete; @@ -309,10 +2555,6 @@ void mapif_defaults(void) { mapif->parse_mail_return = mapif_parse_mail_return; mapif->mail_send = mapif_mail_send; mapif->parse_mail_send = mapif_parse_mail_send; - mapif->mercenary_create = mapif_mercenary_create; - mapif->mercenary_save = mapif_mercenary_save; - mapif->mercenary_load = mapif_mercenary_load; - mapif->mercenary_delete = mapif_mercenary_delete; mapif->mercenary_send = mapif_mercenary_send; mapif->parse_mercenary_create = mapif_parse_mercenary_create; mapif->parse_mercenary_load = mapif_parse_mercenary_load; @@ -343,31 +2585,37 @@ void mapif_defaults(void) { mapif->pet_noinfo = mapif_pet_noinfo; mapif->save_pet_ack = mapif_save_pet_ack; mapif->delete_pet_ack = mapif_delete_pet_ack; - mapif->create_pet = mapif_create_pet; - mapif->load_pet = mapif_load_pet; mapif->save_pet = mapif_save_pet; mapif->delete_pet = mapif_delete_pet; mapif->parse_CreatePet = mapif_parse_CreatePet; mapif->parse_LoadPet = mapif_parse_LoadPet; mapif->parse_SavePet = mapif_parse_SavePet; mapif->parse_DeletePet = mapif_parse_DeletePet; - mapif->quests_fromsql = mapif_quests_fromsql; - mapif->quest_delete = mapif_quest_delete; - mapif->quest_add = mapif_quest_add; - mapif->quest_update = mapif_quest_update; mapif->quest_save_ack = mapif_quest_save_ack; mapif->parse_quest_save = mapif_parse_quest_save; mapif->send_quests = mapif_send_quests; mapif->parse_quest_load = mapif_parse_quest_load; + /* RoDEX */ + mapif->parse_rodex_requestinbox = mapif_parse_rodex_requestinbox; + mapif->rodex_sendinbox = mapif_rodex_sendinbox; + mapif->parse_rodex_checkhasnew = mapif_parse_rodex_checkhasnew; + mapif->rodex_sendhasnew = mapif_rodex_sendhasnew; + mapif->parse_rodex_updatemail = mapif_parse_rodex_updatemail; + mapif->parse_rodex_send = mapif_parse_rodex_send; + mapif->rodex_send = mapif_rodex_send; + mapif->parse_rodex_checkname = mapif_parse_rodex_checkname; + mapif->rodex_checkname = mapif_rodex_checkname; mapif->load_guild_storage = mapif_load_guild_storage; mapif->save_guild_storage_ack = mapif_save_guild_storage_ack; mapif->parse_LoadGuildStorage = mapif_parse_LoadGuildStorage; mapif->parse_SaveGuildStorage = mapif_parse_SaveGuildStorage; + mapif->pAccountStorageLoad = mapif_parse_AccountStorageLoad; + mapif->pAccountStorageSave = mapif_parse_AccountStorageSave; + mapif->sAccountStorageSaveAck = mapif_send_AccountStorageSaveAck; + mapif->account_storage_load = mapif_account_storage_load; mapif->itembound_ack = mapif_itembound_ack; - mapif->parse_ItemBoundRetrieve_sub = mapif_parse_ItemBoundRetrieve_sub; mapif->parse_ItemBoundRetrieve = mapif_parse_ItemBoundRetrieve; mapif->parse_accinfo = mapif_parse_accinfo; - mapif->parse_accinfo2 = mapif_parse_accinfo2; mapif->broadcast = mapif_broadcast; mapif->wis_message = mapif_wis_message; mapif->wis_response = mapif_wis_response; @@ -382,4 +2630,7 @@ void mapif_defaults(void) { mapif->parse_RegistryRequest = mapif_parse_RegistryRequest; mapif->namechange_ack = mapif_namechange_ack; mapif->parse_NameChangeRequest = mapif_parse_NameChangeRequest; + /* Clan System */ + mapif->parse_ClanMemberKick = mapif_parse_ClanMemberKick; + mapif->parse_ClanMemberCount = mapif_parse_ClanMemberCount; } diff --git a/src/char/mapif.h b/src/char/mapif.h index ebbddead0..bfdefe4ea 100644 --- a/src/char/mapif.h +++ b/src/char/mapif.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,6 +40,11 @@ struct mapif_interface { int (*sendallwos) (int sfd, unsigned char *buf, unsigned int len); int (*send) (int fd, unsigned char *buf, unsigned int len); void (*send_users_count) (int users); + void (*pLoadAchievements) (int fd); + void (*sAchievementsToMap) (int fd, int char_id, const struct char_achievements *p); + void (*pSaveAchievements) (int fd); + void (*achievement_load) (int fd, int char_id); + void (*achievement_save) (int char_id, struct char_achievements *p); void (*auction_message) (int char_id, unsigned char result); void (*auction_sendlist) (int fd, int char_id, short count, short pages, unsigned char *buf); void (*parse_auction_requestlist) (int fd); @@ -51,10 +56,6 @@ struct mapif_interface { void (*parse_auction_close) (int fd); void (*auction_bid) (int fd, int char_id, int bid, unsigned char result); void (*parse_auction_bid) (int fd); - bool (*elemental_create) (struct s_elemental *ele); - bool (*elemental_save) (const struct s_elemental *ele); - bool (*elemental_load) (int ele_id, int char_id, struct s_elemental *ele); - bool (*elemental_delete) (int ele_id); void (*elemental_send) (int fd, struct s_elemental *ele, unsigned char flag); void (*parse_elemental_create) (int fd, const struct s_elemental *ele); void (*parse_elemental_load) (int fd, int ele_id, int char_id); @@ -90,7 +91,6 @@ struct mapif_interface { int (*parse_GuildMemberInfoChange) (int fd, int guild_id, int account_id, int char_id, int type, const char *data, int len); int (*parse_GuildPosition) (int fd, int guild_id, int idx, const struct guild_position *p); int (*parse_GuildSkillUp) (int fd, int guild_id, uint16 skill_id, int account_id, int max); - int (*parse_GuildDeleteAlliance) (struct guild *g, int guild_id, int account_id1, int account_id2, int flag); int (*parse_GuildAlliance) (int fd, int guild_id1, int guild_id2, int account_id1, int account_id2, int flag); int (*parse_GuildNotice) (int fd, int guild_id, const char *mes1, const char *mes2); int (*parse_GuildEmblem) (int fd, int len, int guild_id, int dummy, const char *data); @@ -102,11 +102,6 @@ struct mapif_interface { void (*homunculus_loaded) (int fd, int account_id, struct s_homunculus *hd); void (*homunculus_saved) (int fd, int account_id, bool flag); void (*homunculus_renamed) (int fd, int account_id, int char_id, unsigned char flag, const char *name); - bool (*homunculus_create) (struct s_homunculus *hd); - bool (*homunculus_save) (const struct s_homunculus *hd); - bool (*homunculus_load) (int homun_id, struct s_homunculus* hd); - bool (*homunculus_delete) (int homun_id); - bool (*homunculus_rename) (const char *name); void (*parse_homunculus_create) (int fd, int len, int account_id, const struct s_homunculus *phd); void (*parse_homunculus_delete) (int fd, int homun_id); void (*parse_homunculus_load) (int fd, int account_id, int homun_id); @@ -116,7 +111,6 @@ struct mapif_interface { void (*parse_mail_requestinbox) (int fd); void (*parse_mail_read) (int fd); void (*mail_sendattach) (int fd, int char_id, struct mail_message *msg); - void (*mail_getattach) (int fd, int char_id, int mail_id); void (*parse_mail_getattach) (int fd); void (*mail_delete) (int fd, int char_id, int mail_id, bool failed); void (*parse_mail_delete) (int fd); @@ -125,10 +119,6 @@ struct mapif_interface { void (*parse_mail_return) (int fd); void (*mail_send) (int fd, struct mail_message* msg); void (*parse_mail_send) (int fd); - bool (*mercenary_create) (struct s_mercenary *merc); - bool (*mercenary_save) (const struct s_mercenary *merc); - bool (*mercenary_load) (int merc_id, int char_id, struct s_mercenary *merc); - bool (*mercenary_delete) (int merc_id); void (*mercenary_send) (int fd, struct s_mercenary *merc, unsigned char flag); void (*parse_mercenary_create) (int fd, const struct s_mercenary *merc); void (*parse_mercenary_load) (int fd, int merc_id, int char_id); @@ -159,33 +149,36 @@ struct mapif_interface { int (*pet_noinfo) (int fd, int account_id); int (*save_pet_ack) (int fd, int account_id, int flag); int (*delete_pet_ack) (int fd, int flag); - int (*create_pet) (int fd, int account_id, int char_id, short pet_class, short pet_lv, short pet_egg_id, - short pet_equip, short intimate, short hungry, char rename_flag, char incubate, const char *pet_name); - int (*load_pet) (int fd, int account_id, int char_id, int pet_id); int (*save_pet) (int fd, int account_id, const struct s_pet *data); int (*delete_pet) (int fd, int pet_id); int (*parse_CreatePet) (int fd); int (*parse_LoadPet) (int fd); int (*parse_SavePet) (int fd); int (*parse_DeletePet) (int fd); - struct quest *(*quests_fromsql) (int char_id, int *count); - bool (*quest_delete) (int char_id, int quest_id); - bool (*quest_add) (int char_id, struct quest qd); - bool (*quest_update) (int char_id, struct quest qd); void (*quest_save_ack) (int fd, int char_id, bool success); int (*parse_quest_save) (int fd); void (*send_quests) (int fd, int char_id, struct quest *tmp_questlog, int num_quests); int (*parse_quest_load) (int fd); + void (*parse_rodex_requestinbox) (int fd); + void (*rodex_sendinbox) (int fd, int char_id, int8 opentype, int8 flag, int count, int64 mail_id, struct rodex_maillist *mails); + void (*parse_rodex_checkhasnew) (int fd); + void (*rodex_sendhasnew) (int fd, int char_id, bool has_new); + void (*parse_rodex_updatemail) (int fd); + void (*parse_rodex_send) (int fd); + void (*rodex_send) (int fd, int sender_id, int receiver_id, int receiver_accountid, bool result); + void (*parse_rodex_checkname) (int fd); + void (*rodex_checkname) (int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char *name); int (*load_guild_storage) (int fd, int account_id, int guild_id, char flag); int (*save_guild_storage_ack) (int fd, int account_id, int guild_id, int fail); int (*parse_LoadGuildStorage) (int fd); int (*parse_SaveGuildStorage) (int fd); + int (*account_storage_load) (int fd, int account_id); + int (*pAccountStorageLoad) (int fd); + int (*pAccountStorageSave) (int fd); + void (*sAccountStorageSaveAck) (int fd, int account_id, bool save); int (*itembound_ack) (int fd, int aid, int guild_id); - int (*parse_ItemBoundRetrieve_sub) (int fd); void (*parse_ItemBoundRetrieve) (int fd); void (*parse_accinfo) (int fd); - void (*parse_accinfo2) (bool success, int map_fd, int u_fd, int u_aid, int account_id, const char *userid, const char *user_pass, - const char *email, const char *last_ip, const char *lastlogin, const char *pin_code, const char *birthdate, int group_id, int logincount, int state); int (*broadcast) (const unsigned char *mes, int len, unsigned int fontColor, short fontType, short fontSize, short fontAlign, short fontY, int sfd); int (*wis_message) (struct WisData *wd); void (*wis_response) (int fd, const unsigned char *src, int flag); @@ -200,6 +193,9 @@ struct mapif_interface { int (*parse_RegistryRequest) (int fd); void (*namechange_ack) (int fd, int account_id, int char_id, int type, int flag, const char *name); int (*parse_NameChangeRequest) (int fd); + // Clan System + int (*parse_ClanMemberKick) (int fd, int clan_id, int kick_interval); + int (*parse_ClanMemberCount) (int fd, int clan_id, int kick_interval); }; #ifdef HERCULES_CORE diff --git a/src/char/packets_hc_struct.h b/src/char/packets_hc_struct.h new file mode 100644 index 000000000..196493cac --- /dev/null +++ b/src/char/packets_hc_struct.h @@ -0,0 +1,45 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2016-2018 Hercules Dev Team + * + * Hercules is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef CHAR_PACKETS_HC_STRUCT_H +#define CHAR_PACKETS_HC_STRUCT_H + +#include "common/hercules.h" +#include "common/mmo.h" +#include "common/packetsstatic_len.h" + +/* Packets Structs */ +#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute +#pragma pack(push, 1) +#endif // not NetBSD < 6 / Solaris + +#if PACKETVER_MAIN_NUM >= 20130522 || PACKETVER_RE_NUM >= 20130327 || defined(PACKETVER_ZERO) +struct PACKET_HC_ACK_CHARINFO_PER_PAGE { + int16 packetId; + int16 packetLen; + // chars list[] +} __attribute__((packed)); +DEFINE_PACKET_HEADER(HC_ACK_CHARINFO_PER_PAGE, 0x099d); +#endif + +#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute +#pragma pack(pop) +#endif // not NetBSD < 6 / Solaris + +#endif // CHAR_PACKETS_HC_STRUCT_H diff --git a/src/char/pincode.c b/src/char/pincode.c index fc1a4c037..5a7eb1cab 100644 --- a/src/char/pincode.c +++ b/src/char/pincode.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -26,6 +26,7 @@ #include "common/cbasetypes.h" #include "common/conf.h" #include "common/db.h" +#include "common/memmgr.h" #include "common/mmo.h" #include "common/nullpo.h" #include "common/random.h" @@ -36,88 +37,200 @@ #include <stdio.h> #include <stdlib.h> -struct pincode_interface pincode_s; +static struct pincode_interface pincode_s; struct pincode_interface *pincode; -void pincode_handle (int fd, struct char_session_data* sd) { +static void pincode_handle(int fd, struct char_session_data *sd) +{ struct online_char_data* character; nullpo_retv(sd); + character = (struct online_char_data*)idb_get(chr->online_char_db, sd->account_id); - if( character && character->pincode_enable > pincode->charselect ){ + + if (character && character->pincode_enable > pincode->charselect) { character->pincode_enable = pincode->charselect * 2; - }else{ - pincode->sendstate( fd, sd, PINCODE_OK ); + } else { + pincode->loginstate(fd, sd, PINCODE_LOGIN_OK); return; } - if( strlen(sd->pincode) == 4 ){ - if( pincode->changetime && time(NULL) > (sd->pincode_change+pincode->changetime) ){ // User hasn't changed his PIN code for a long time - pincode->sendstate( fd, sd, PINCODE_EXPIRED ); + if (strlen(sd->pincode) == 4) { + if (pincode->check_blacklist && pincode->isBlacklisted(sd->pincode)) { + // Ask player to change pincode to be able to connect + pincode->loginstate(fd, sd, PINCODE_LOGIN_EXPIRED); + } else if (pincode->changetime && time(NULL) > (sd->pincode_change + pincode->changetime)) { + // User hasn't changed his PIN code for a long time + pincode->loginstate(fd, sd, PINCODE_LOGIN_EXPIRED); } else { // Ask user for his PIN code - pincode->sendstate( fd, sd, PINCODE_ASK ); + pincode->loginstate(fd, sd, PINCODE_LOGIN_ASK); } } else // No PIN code has been set yet - pincode->sendstate( fd, sd, PINCODE_NOTSET ); + pincode->loginstate(fd, sd, PINCODE_LOGIN_NOTSET); - if( character ) + if (character) character->pincode_enable = -1; } -void pincode_check(int fd, struct char_session_data* sd) { +static void pincode_check(int fd, struct char_session_data *sd) +{ char pin[5] = "\0\0\0\0"; nullpo_retv(sd); + + if (strlen(sd->pincode) != 4) + return; + safestrncpy(pin, RFIFOP(fd, 6), sizeof(pin)); pincode->decrypt(sd->pincode_seed, pin); - if( pincode->compare( fd, sd, pin ) ){ + + if (pincode->check_blacklist && pincode->isBlacklisted(pin)) { + pincode->loginstate(fd, sd, PINCODE_LOGIN_RESTRICT_PW); + return; + } + + if (pincode->compare(fd, sd, pin)) { struct online_char_data* character; - if( (character = (struct online_char_data*)idb_get(chr->online_char_db, sd->account_id)) ) + if ((character = (struct online_char_data*)idb_get(chr->online_char_db, sd->account_id))) character->pincode_enable = pincode->charselect * 2; - pincode->sendstate( fd, sd, PINCODE_OK ); + pincode->loginstate(fd, sd, PINCODE_LOGIN_OK); + } else { +#if PACKETVER_MAIN_NUM >= 20180124 || PACKETVER_RE_NUM >= 20180124 || PACKETVER_ZERO_NUM >= 20180131 + pincode->loginstate2(fd, sd, PINCODE_LOGIN_WRONG, PINCODE_LOGIN_FLAG_WRONG); +#else + pincode->loginstate(fd, sd, PINCODE_LOGIN_WRONG); +#endif } } -int pincode_compare(int fd, struct char_session_data* sd, char* pin) { +/** + * Check if this pincode is blacklisted or not + * + * @param (const char *) pin The pin to be verified + * @return bool + */ +static bool pincode_isBlacklisted(const char *pin) +{ + int i; + + nullpo_retr(false, pin); + + ARR_FIND(0, VECTOR_LENGTH(pincode->blacklist), i, strcmp(VECTOR_INDEX(pincode->blacklist, i), pin) == 0); + + if (i < VECTOR_LENGTH(pincode->blacklist)) { + return true; + } + + return false; +} + +static int pincode_compare(int fd, struct char_session_data *sd, char *pin) +{ nullpo_ret(sd); nullpo_ret(pin); - if( strcmp( sd->pincode, pin ) == 0 ){ + + if (strcmp(sd->pincode, pin) == 0) { sd->pincode_try = 0; return 1; } else { - pincode->sendstate( fd, sd, PINCODE_WRONG ); - if( pincode->maxtry && ++sd->pincode_try >= pincode->maxtry ){ - pincode->error( sd->account_id ); + if (pincode->maxtry && ++sd->pincode_try >= pincode->maxtry) { + pincode->error(sd->account_id); + chr->authfail_fd(fd, 0); + chr->disconnect_player(sd->account_id); } return 0; } } -void pincode_change(int fd, struct char_session_data* sd) { +static void pincode_change(int fd, struct char_session_data *sd) +{ char oldpin[5] = "\0\0\0\0", newpin[5] = "\0\0\0\0"; nullpo_retv(sd); - safestrncpy(oldpin, RFIFOP(fd,6), sizeof(oldpin)); - pincode->decrypt(sd->pincode_seed,oldpin); - if( !pincode->compare( fd, sd, oldpin ) ) + + if (strlen(sd->pincode) != 4) return; - safestrncpy(newpin, RFIFOP(fd,10), sizeof(newpin)); - pincode->decrypt(sd->pincode_seed,newpin); - pincode->update( sd->account_id, newpin ); + safestrncpy(oldpin, RFIFOP(fd, 6), sizeof(oldpin)); + pincode->decrypt(sd->pincode_seed, oldpin); + + if (!pincode->compare(fd, sd, oldpin)) { + pincode->editstate(fd, sd, PINCODE_EDIT_FAILED); + pincode->loginstate(fd, sd, PINCODE_LOGIN_ASK); + return; + } + + safestrncpy(newpin, RFIFOP(fd, 10), sizeof(newpin)); + pincode->decrypt(sd->pincode_seed, newpin); + + if (pincode->check_blacklist && pincode->isBlacklisted(newpin)) { + pincode->editstate(fd, sd, PINCODE_EDIT_RESTRICT_PW); + return; + } + + pincode->update(sd->account_id, newpin); safestrncpy(sd->pincode, newpin, sizeof(sd->pincode)); - pincode->sendstate( fd, sd, PINCODE_ASK ); + pincode->editstate(fd, sd, PINCODE_EDIT_SUCCESS); + pincode->loginstate(fd, sd, PINCODE_LOGIN_ASK); } -void pincode_setnew(int fd, struct char_session_data* sd) { +static void pincode_setnew(int fd, struct char_session_data *sd) +{ char newpin[5] = "\0\0\0\0"; nullpo_retv(sd); - safestrncpy(newpin, RFIFOP(fd,6), sizeof(newpin)); - pincode->decrypt(sd->pincode_seed,newpin); - pincode->update( sd->account_id, newpin ); + + if (strlen(sd->pincode) == 4) + return; + + safestrncpy(newpin, RFIFOP(fd, 6), sizeof(newpin)); + pincode->decrypt(sd->pincode_seed, newpin); + + if (pincode->check_blacklist && pincode->isBlacklisted(newpin)) { + pincode->makestate(fd, sd, PINCODE_MAKE_RESTRICT_PW); + return; + } + + pincode->update(sd->account_id, newpin); safestrncpy(sd->pincode, newpin, sizeof(sd->pincode)); - pincode->sendstate( fd, sd, PINCODE_ASK ); + pincode->makestate(fd, sd, PINCODE_MAKE_SUCCESS); + pincode->loginstate(fd, sd, PINCODE_LOGIN_ASK); +} + +/** + * Send state of making new pincode + * + * @param[in] fd + * @param[in, out] sd Session Data + * @param[in] state Pincode Edit State + */ +static void pincode_makestate(int fd, struct char_session_data *sd, enum pincode_make_response state) +{ + nullpo_retv(sd); + + WFIFOHEAD(fd, 8); + WFIFOW(fd, 0) = 0x8bb; + WFIFOW(fd, 2) = state; + WFIFOL(fd, 4) = sd->pincode_seed; + WFIFOSET(fd, 8); +} + +/** + * Send state of editing pincode + * + * @param[in] fd + * @param[in, out] sd Session Data + * @param[in] state Pincode Edit State + */ +static void pincode_editstate(int fd, struct char_session_data *sd, enum pincode_edit_response state) +{ + nullpo_retv(sd); + + WFIFOHEAD(fd, 8); + WFIFOW(fd, 0) = 0x8bf; + WFIFOW(fd, 2) = state; + WFIFOL(fd, 4) = sd->pincode_seed = rnd() % 0xFFFF; + WFIFOSET(fd, 8); } // 0 = pin is correct @@ -129,51 +242,87 @@ void pincode_setnew(int fd, struct char_session_data* sd) { // 6 = client shows msgstr(1897) Unable to use your KSSN number // 7 = char select window shows a button - client sends 0x8c5 // 8 = pincode was incorrect -void pincode_sendstate(int fd, struct char_session_data* sd, uint16 state) { +static void pincode_loginstate(int fd, struct char_session_data *sd, enum pincode_login_response state) +{ nullpo_retv(sd); + WFIFOHEAD(fd, 12); WFIFOW(fd, 0) = 0x8b9; WFIFOL(fd, 2) = sd->pincode_seed = rnd() % 0xFFFF; WFIFOL(fd, 6) = sd->account_id; - WFIFOW(fd,10) = state; - WFIFOSET(fd,12); + WFIFOW(fd, 10) = state; + WFIFOSET(fd, 12); } -void pincode_notifyLoginPinUpdate(int account_id, char* pin) { +// 0 = pin is correct +// 1 = ask for pin - client sends 0x8b8 +// 2 = create new pin - client sends 0x8ba +// 3 = pin must be changed - client 0x8be +// 4 = create new pin ?? - client sends 0x8ba +// 5 = client shows msgstr(1896) +// 6 = client shows msgstr(1897) Unable to use your KSSN number +// 7 = char select window shows a button - client sends 0x8c5 +// 8 = pincode was incorrect +// [4144] pincode_loginstate2 can replace pincode_loginstate, +// but kro using pincode_loginstate2 only for send wrong pin error or locked after 3 pins wrong +static void pincode_loginstate2(int fd, struct char_session_data *sd, enum pincode_login_response state, enum pincode_login_response2 flag) +{ +#if PACKETVER_MAIN_NUM >= 20180124 || PACKETVER_RE_NUM >= 20180124 || PACKETVER_ZERO_NUM >= 20180131 + nullpo_retv(sd); + + WFIFOHEAD(fd, 13); + WFIFOW(fd, 0) = 0xae9; + WFIFOL(fd, 2) = sd->pincode_seed = rnd() % 0xFFFF; + WFIFOL(fd, 6) = sd->account_id; + WFIFOW(fd, 10) = state; + WFIFOW(fd, 12) = flag; + WFIFOSET(fd, 13); +#endif +} + +static void pincode_notifyLoginPinUpdate(int account_id, char *pin) +{ nullpo_retv(pin); + Assert_retv(chr->login_fd != -1); - WFIFOHEAD(chr->login_fd,11); - WFIFOW(chr->login_fd,0) = 0x2738; - WFIFOL(chr->login_fd,2) = account_id; - safestrncpy(WFIFOP(chr->login_fd,6), pin, 5); - WFIFOSET(chr->login_fd,11); + WFIFOHEAD(chr->login_fd, 11); + WFIFOW(chr->login_fd, 0) = 0x2738; + WFIFOL(chr->login_fd, 2) = account_id; + safestrncpy(WFIFOP(chr->login_fd, 6), pin, 5); + WFIFOSET(chr->login_fd, 11); } -void pincode_notifyLoginPinError(int account_id) { - WFIFOHEAD(chr->login_fd,6); - WFIFOW(chr->login_fd,0) = 0x2739; - WFIFOL(chr->login_fd,2) = account_id; - WFIFOSET(chr->login_fd,6); +static void pincode_notifyLoginPinError(int account_id) +{ + WFIFOHEAD(chr->login_fd, 6); + WFIFOW(chr->login_fd, 0) = 0x2739; + WFIFOL(chr->login_fd, 2) = account_id; + WFIFOSET(chr->login_fd, 6); } -void pincode_decrypt(unsigned int userSeed, char* pin) { +static void pincode_decrypt(unsigned int userSeed, char *pin) +{ int i; - char tab[10] = {0,1,2,3,4,5,6,7,8,9}; + char tab[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; nullpo_retv(pin); + for (i = 1; i < 10; i++) { int pos; userSeed = pincode->baseSeed + userSeed * pincode->multiplier; pos = userSeed % (i + 1); - if( i != pos ){ + if (i != pos) { tab[i] ^= tab[pos]; tab[pos] ^= tab[i]; tab[i] ^= tab[pos]; } } - for( i = 0; i < 4; i++ ){ - pin[i] = tab[pin[i] - '0']; + for (i = 0; i < 4; i++) { + if (pin[i] < '0' || pin[i] > '9') + pin[i] = '0'; + else + pin[i] = tab[pin[i] - '0']; } sprintf(pin, "%d%d%d%d", pin[0], pin[1], pin[2], pin[3]); @@ -188,9 +337,11 @@ void pincode_decrypt(unsigned int userSeed, char* pin) { * * @retval false in case of error. */ -bool pincode_config_read(const char *filename, const struct config_t *config, bool imported) +static bool pincode_config_read(const char *filename, const struct config_t *config, bool imported) { const struct config_setting_t *setting = NULL; + const struct config_setting_t *temp = NULL; + nullpo_retr(false, filename); nullpo_retr(false, config); @@ -227,28 +378,94 @@ bool pincode_config_read(const char *filename, const struct config_t *config, bo } } + if (libconfig->setting_lookup_bool_real(setting, "check_blacklisted", &pincode->check_blacklist) == CONFIG_FALSE) { + if (!imported) { + ShowWarning("pincode 'check_blaclisted' not found, defaulting to false...\n"); + pincode->check_blacklist = false; + } + } + + if (pincode->check_blacklist) { + if ((temp = libconfig->setting_get_member(setting, "blacklist")) != NULL) { + VECTOR_DECL(char *) duplicate; + int i, j, size = libconfig->setting_length(temp); + VECTOR_INIT(duplicate); + VECTOR_ENSURE(duplicate, size, 1); + for (i = 0; i < size; i++) { + const char *pin = libconfig->setting_get_string_elem(temp, i); + + if (pin == NULL) + continue; + + if (strlen(pin) != 4) { + ShowError("Wrong size on element %d of blacklist. Desired size = 4, received = %d\n", i, (int)strlen(pin)); + continue; + } + + ARR_FIND(0, VECTOR_LENGTH(duplicate), j, strcmp(VECTOR_INDEX(duplicate, j), pin) == 0); + + if (j < VECTOR_LENGTH(duplicate)) { + ShowWarning("Duplicate pin on pincode blacklist. Item #%d\n", i); + continue; + } + + VECTOR_ENSURE(pincode->blacklist, 1, 1); + VECTOR_PUSH(pincode->blacklist, aStrdup(pin)); + VECTOR_PUSH(duplicate, aStrdup(pin)); + } + while (VECTOR_LENGTH(duplicate) > 0) { + aFree(VECTOR_POP(duplicate)); + } + VECTOR_CLEAR(duplicate); + } else if (!imported) { + ShowError("Pincode Blacklist Check is enabled but there's no blacklist setting! Disabling check.\n"); + pincode->check_blacklist = false; + } + } + return true; } -void pincode_defaults(void) { +static void do_pincode_init(void) +{ + VECTOR_INIT(pincode->blacklist); +} + +static void do_pincode_final(void) +{ + while (VECTOR_LENGTH(pincode->blacklist) > 0) { + aFree(VECTOR_POP(pincode->blacklist)); + } + VECTOR_CLEAR(pincode->blacklist); +} + +void pincode_defaults(void) +{ pincode = &pincode_s; - pincode->enabled = PINCODE_OK; + pincode->enabled = 0; pincode->changetime = 0; pincode->maxtry = 3; pincode->charselect = 0; + pincode->check_blacklist = false; pincode->multiplier = 0x3498; pincode->baseSeed = 0x881234; + pincode->init = do_pincode_init; + pincode->final = do_pincode_final; + pincode->handle = pincode_handle; pincode->decrypt = pincode_decrypt; pincode->error = pincode_notifyLoginPinError; pincode->update = pincode_notifyLoginPinUpdate; - pincode->sendstate = pincode_sendstate; + pincode->makestate = pincode_makestate; + pincode->editstate = pincode_editstate; + pincode->loginstate = pincode_loginstate; + pincode->loginstate2 = pincode_loginstate2; pincode->setnew = pincode_setnew; pincode->change = pincode_change; + pincode->isBlacklisted = pincode_isBlacklisted; pincode->compare = pincode_compare; pincode->check = pincode_check; pincode->config_read = pincode_config_read; - } diff --git a/src/char/pincode.h b/src/char/pincode.h index cffaa3054..699758179 100644 --- a/src/char/pincode.h +++ b/src/char/pincode.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -22,18 +22,40 @@ #define CHAR_PINCODE_H #include "common/hercules.h" +#include "common/db.h" /* Forward Declarations */ struct char_session_data; struct config_t; // common/conf.h -enum PincodeResponseCode { - PINCODE_OK = 0, - PINCODE_ASK = 1, - PINCODE_NOTSET = 2, - PINCODE_EXPIRED = 3, - PINCODE_UNUSED = 7, - PINCODE_WRONG = 8, +enum pincode_make_response { + PINCODE_MAKE_SUCCESS = 0, + PINCODE_MAKE_DUPLICATED = 1, + PINCODE_MAKE_RESTRICT_PW = 2, + PINCODE_MAKE_PERSONALNUM_PW = 3, + PINCODE_MAKE_FAILED = 4, +}; + +enum pincode_edit_response { + PINCODE_EDIT_SUCCESS = 0x0, + PINCODE_EDIT_FAILED = 0x1, + PINCODE_EDIT_RESTRICT_PW = 0x2, + PINCODE_EDIT_PERSONALNUM_PW = 0x3, +}; + +enum pincode_login_response { + PINCODE_LOGIN_OK = 0, + PINCODE_LOGIN_ASK = 1, + PINCODE_LOGIN_NOTSET = 2, + PINCODE_LOGIN_EXPIRED = 3, + PINCODE_LOGIN_RESTRICT_PW = 5, + PINCODE_LOGIN_UNUSED = 7, + PINCODE_LOGIN_WRONG = 8, +}; + +enum pincode_login_response2 { + PINCODE_LOGIN_FLAG_LOCKED = 0, + PINCODE_LOGIN_FLAG_WRONG = 2, }; /** @@ -45,6 +67,8 @@ struct pincode_interface { int changetime; int maxtry; int charselect; + bool check_blacklist; + VECTOR_DECL(char *) blacklist; unsigned int multiplier; unsigned int baseSeed; /* handler */ @@ -52,12 +76,18 @@ struct pincode_interface { void (*decrypt) (unsigned int userSeed, char* pin); void (*error) (int account_id); void (*update) (int account_id, char* pin); - void (*sendstate) (int fd, struct char_session_data* sd, uint16 state); + void (*makestate) (int fd, struct char_session_data *sd, enum pincode_make_response state); + void (*editstate) (int fd, struct char_session_data *sd, enum pincode_edit_response state); + void (*loginstate) (int fd, struct char_session_data *sd, enum pincode_login_response state); + void (*loginstate2) (int fd, struct char_session_data *sd, enum pincode_login_response state, enum pincode_login_response2 flag); void (*setnew) (int fd, struct char_session_data* sd); void (*change) (int fd, struct char_session_data* sd); + bool (*isBlacklisted) (const char *pin); int (*compare) (int fd, struct char_session_data* sd, char* pin); void (*check) (int fd, struct char_session_data* sd); bool (*config_read) (const char *filename, const struct config_t *config, bool imported); + void (*init) (void); + void (*final) (void); }; #ifdef HERCULES_CORE |