diff options
Diffstat (limited to 'src/char/char.c')
-rw-r--r-- | src/char/char.c | 1802 |
1 files changed, 934 insertions, 868 deletions
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; |