diff options
Diffstat (limited to 'src/char/char.c')
-rw-r--r-- | src/char/char.c | 613 |
1 files changed, 372 insertions, 241 deletions
diff --git a/src/char/char.c b/src/char/char.c index e43ebbfbc..7b1078e37 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -4,49 +4,49 @@ #define HERCULES_CORE -#include "../config/core.h" // CONSOLE_INPUT +#include "config/core.h" // CONSOLE_INPUT #include "char.h" +#include "char/HPMchar.h" +#include "char/geoip.h" +#include "char/int_auction.h" +#include "char/int_elemental.h" +#include "char/int_guild.h" +#include "char/int_homun.h" +#include "char/int_mail.h" +#include "char/int_mercenary.h" +#include "char/int_party.h" +#include "char/int_pet.h" +#include "char/int_quest.h" +#include "char/int_storage.h" +#include "char/inter.h" +#include "char/loginif.h" +#include "char/mapif.h" +#include "char/pincode.h" + +#include "common/HPM.h" +#include "common/cbasetypes.h" +#include "common/console.h" +#include "common/core.h" +#include "common/db.h" +#include "common/malloc.h" +#include "common/mapindex.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/strlib.h" +#include "common/sql.h" +#include "common/timer.h" +#include "common/utils.h" + #include <signal.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <sys/types.h> -#include <time.h> - -#include "HPMchar.h" -#include "geoip.h" -#include "int_auction.h" -#include "int_elemental.h" -#include "int_guild.h" -#include "int_homun.h" -#include "int_mail.h" -#include "int_mercenary.h" -#include "int_party.h" -#include "int_pet.h" -#include "int_quest.h" -#include "int_storage.h" -#include "inter.h" -#include "loginif.h" -#include "mapif.h" -#include "pincode.h" -#include "../common/HPM.h" -#include "../common/cbasetypes.h" -#include "../common/console.h" -#include "../common/core.h" -#include "../common/db.h" -#include "../common/malloc.h" -#include "../common/mapindex.h" -#include "../common/mmo.h" -#include "../common/showmsg.h" -#include "../common/socket.h" -#include "../common/strlib.h" -#include "../common/timer.h" -#include "../common/utils.h" - #ifndef WIN32 - #include <unistd.h> +# include <unistd.h> #endif // private declarations @@ -147,11 +147,6 @@ 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]; -// check for exit signal -// 0 is saving complete -// other is char_id -unsigned int save_flag = 0; - // Initial position (it's possible to set it in conf file) struct point start_point = { 0, 53, 111 }; @@ -239,7 +234,7 @@ void char_set_char_online(int map_id, int char_id, int account_id) { ShowNotice("chr->set_char_online: Character %d:%d marked in map server %d, but map server %d claims to have (%d:%d) online!\n", character->account_id, character->char_id, character->server, map_id, account_id, char_id); - mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); + mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); // 2: Already connected to server } //Update state data @@ -317,6 +312,7 @@ static int char_db_setoffline(DBKey key, DBData *data, va_list ap) { struct online_char_data* character = (struct online_char_data*)DB->data2ptr(data); int server_id = va_arg(ap, int); + nullpo_ret(character); if (server_id == -1) { character->char_id = -1; character->server = -1; @@ -336,13 +332,14 @@ static int char_db_kickoffline(DBKey key, DBData *data, va_list ap) { struct online_char_data* character = (struct online_char_data*)DB->data2ptr(data); int server_id = va_arg(ap, int); + nullpo_ret(character); if (server_id > -1 && character->server != server_id) return 0; //Kick out any connected characters, and set them offline as appropriate. - if (character->server > -1) - mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 1); + if (character->server > -1 && character->server < MAX_MAP_SERVERS) + mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 1); // 1: Server closed else if (character->waiting_disconnect == INVALID_TIMER) chr->set_char_offline(character->char_id, character->account_id); else @@ -404,7 +401,8 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) int errors = 0; //If there are any errors while saving, "cp" will not be updated at the end. StringBuf buf; - if (char_id!=p->char_id) return 0; + nullpo_ret(p); + if (char_id != p->char_id) return 0; cp = idb_ensure(chr->char_db_, char_id, chr->create_charstatus); @@ -453,31 +451,16 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) (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->show_equip != cp->show_equip) || (p->allow_party != cp->allow_party) || (p->font != cp->font) || - (p->uniqueitem_counter != cp->uniqueitem_counter ) || (p->sex != cp->sex) + (p->uniqueitem_counter != cp->uniqueitem_counter) ) { //Save status unsigned int opt = 0; - char sex; if( p->allow_party ) opt |= OPT_ALLOW_PARTY; if( p->show_equip ) opt |= OPT_SHOW_EQUIP; - switch (p->sex) - { - case 0: - sex = 'F'; - break; - case 1: - sex = 'M'; - break; - case 99: - default: - sex = 'U'; - break; - } - 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'," "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d'," @@ -485,8 +468,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) "`option`='%d',`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'," - " sex = '%c'" + "`delete_date`='%lu',`robe`='%d',`slotchange`='%d', `char_opt`='%u', `font`='%u', `uniqueitem_counter` ='%u'" " WHERE `account_id`='%d' AND `char_id` = '%d'", char_db, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, @@ -497,7 +479,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) 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, sex, + p->robe,p->slotchange,opt,p->font,p->uniqueitem_counter, p->account_id, p->char_id) ) { Sql_ShowDebug(inter->sql_handle); @@ -868,6 +850,7 @@ int char_inventory_to_sql(const struct item items[], int max, int id) { bool found; int errors = 0; + nullpo_ret(items); // The following code compares inventory with current database values // and performs modification/deletion/insertion only on relevant rows. @@ -997,6 +980,55 @@ int char_inventory_to_sql(const struct item items[], int max, int id) { return errors; } +/** + * Returns the correct gender ID for the given character and enum value. + * + * If the per-character sex is defined but not supported by the current packetver, the database entries are corrected. + * + * @param sd Character data, if available. + * @param p Character status. + * @param sex Character sex (database enum) + * + * @retval SEX_MALE if the per-character sex is male + * @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) +{ +#if PACKETVER >= 20141016 + (void)sd; (void)p; // Unused + switch (sex) { + case 'M': + return SEX_MALE; + case 'F': + return SEX_FEMALE; + case 'U': + default: + return 99; + } +#else + if (sex == 'M' || sex == 'F') { + if (!sd) { + // sd is not available, there isn't much we can do. Just return and print a warning. + ShowWarning("Character '%s' (CID: %d, AID: %d) has sex '%c', but PACKETVER does not support per-character sex. Defaulting to 'U'.\n", + p->name, p->char_id, p->account_id, sex); + return 99; + } + if ((sex == 'M' && sd->sex == SEX_FEMALE) + || (sex == 'F' && sd->sex == SEX_MALE)) { + ShowWarning("Changing sex of character '%s' (CID: %d, AID: %d) to 'U' due to incompatible PACKETVER.\n", p->name, p->char_id, p->account_id); + chr->changecharsex(p->char_id, sd->sex); + } else { + ShowInfo("Resetting sex of character '%s' (CID: %d, AID: %d) to 'U' due to incompatible PACKETVER.\n", p->name, p->char_id, p->account_id); + } + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `sex` = 'U' WHERE `char_id` = '%d'", char_db, p->char_id)) { + Sql_ShowDebug(inter->sql_handle); + } + } + return 99; +#endif +} + //===================================================================================================== // 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) @@ -1008,6 +1040,9 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) time_t unban_time = 0; char sex[2]; + nullpo_ret(sd); + nullpo_ret(buf); + stmt = SQL->StmtMalloc(inter->sql_handle); if( stmt == NULL ) { SqlStmt_ShowDebug(stmt); @@ -1075,21 +1110,12 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) } for( i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->StmtNextRow(stmt); i++ ) { + if (p.slot >= MAX_CHARS) + continue; p.last_point.map = mapindex->name2id(last_map); sd->found_char[p.slot] = p.char_id; sd->unban_time[p.slot] = unban_time; - switch( sex[0] ) { - case 'M': - p.sex = 1; - break; - case 'F': - p.sex = 0; - break; - case 'U': - default: - p.sex = 99; - break; - } + p.sex = chr->mmo_gender(sd, &p, sex[0]); j += chr->mmo_char_tobuf(WBUFP(buf, j), &p); } @@ -1116,12 +1142,14 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every struct s_friend tmp_friend; #ifdef HOTKEY_SAVING struct hotkey tmp_hotkey; - int hotkey_num; + int hotkey_num = 0; #endif unsigned int opt; int account_id; char sex[2]; + nullpo_ret(p); + memset(p, 0, sizeof(struct mmo_charstatus)); if (save_log) ShowInfo("Char load request (%d)\n", char_id); @@ -1200,31 +1228,20 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every || SQL_ERROR == SQL->StmtBindColumn(stmt, 53, SQLDT_UINT, &opt, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 54, SQLDT_UCHAR, &p->font, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 55, SQLDT_UINT, &p->uniqueitem_counter, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 56, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 56, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); return 0; } - if( SQL_ERROR == SQL->StmtNextRow(stmt) ) + if (SQL_SUCCESS != SQL->StmtNextRow(stmt)) { ShowError("Requested non-existant character id: %d!\n", char_id); SQL->StmtFree(stmt); return 0; } - switch( sex[0] ) { - case 'M': - p->sex = 1; - break; - case 'F': - p->sex = 0; - break; - case 'U': - default: - p->sex = 99; - break; - } + p->sex = chr->mmo_gender(NULL, p, sex[0]); account_id = p->account_id; @@ -1325,8 +1342,9 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every || 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); @@ -1348,8 +1366,9 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every || 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) - ) + ) { SqlStmt_ShowDebug(stmt); + } if( tmp_skill.flag != SKILL_FLAG_PERM_GRANTED ) tmp_skill.flag = SKILL_FLAG_PERMANENT; @@ -1370,8 +1389,10 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every || 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, 2, SQLDT_STRING, &tmp_friend.name, sizeof(tmp_friend.name), NULL, NULL) + ) { SqlStmt_ShowDebug(stmt); + } for( i = 0; i < MAX_FRIENDS && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) memcpy(&p->friends[i], &tmp_friend, sizeof(tmp_friend)); @@ -1414,8 +1435,10 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every || 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, 3, SQLDT_USHORT, &p->mod_death, 0, NULL, NULL) + ) { SqlStmt_ShowDebug(stmt); + } if( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) strcat(t_msg, " accdata"); @@ -1457,6 +1480,7 @@ bool char_char_slotchange(struct char_session_data *sd, int fd, unsigned short f struct mmo_charstatus char_dat; int from_id = 0; + nullpo_ret(sd); if( from >= MAX_CHARS || to >= MAX_CHARS || ( sd->char_slots && to > sd->char_slots ) || sd->found_char[from] <= 0 ) return false; @@ -1473,7 +1497,8 @@ bool char_char_slotchange(struct char_session_data *sd, int fd, unsigned short f /* update both at once */ if( SQL_SUCCESS != SQL->QueryStr(inter->sql_handle, "START TRANSACTION") || SQL_SUCCESS != SQL->Query(inter->sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d' LIMIT 1", char_db, from, sd->found_char[to]) - || SQL_SUCCESS != SQL->Query(inter->sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d' LIMIT 1", char_db, to, sd->found_char[from]) ) + || SQL_SUCCESS != SQL->Query(inter->sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d' LIMIT 1", char_db, to, sd->found_char[from]) + ) Sql_ShowDebug(inter->sql_handle); else result = true; @@ -1508,12 +1533,17 @@ 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]; + nullpo_retr(2, sd); + if( sd->new_name[0] == 0 ) // Not ready for rename return 2; if( !chr->mmo_char_fromsql(char_id, &char_dat, false) ) // Only the short data is needed. return 2; + if (sd->account_id != char_dat.account_id) // Try rename not own char + return 2; + if( char_dat.rename == 0 ) return 1; @@ -1556,8 +1586,11 @@ int char_check_char_name(char * name, char * esc_name) { int i; + nullpo_retr(-2, name); + nullpo_retr(-2, esc_name); + // check length of character name - if( name[0] == '\0' ) + if (name[0] == '\0') return -2; // empty character name /** * The client does not allow you to create names with less than 4 characters, however, @@ -1618,6 +1651,8 @@ int char_make_new_char_sql(struct char_session_data* sd, char* name_, int str, i char esc_name[NAME_LENGTH*2+1]; int char_id, flag, k, l; + nullpo_retr(-2, sd); + nullpo_retr(-2, name_); safestrncpy(name, name_, NAME_LENGTH); normalize_name(name,TRIM_CHARS); SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); @@ -1961,7 +1996,8 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { #endif //When the weapon is sent and your option is riding, the client crashes on login!? - WBUFW(buf,56) = (p->option&(0x20|0x80000|0x100000|0x200000|0x400000|0x800000|0x1000000|0x2000000|0x4000000|0x8000000)) ? 0 : p->weapon; + // FIXME[Haru]: is OPTION_HANBOK intended to be part of this list? And if it is, should the list also include other OPTION_ costumes? + WBUFL(buf,56) = p->option&(OPTION_RIDING|OPTION_DRAGON|OPTION_WUG|OPTION_WUGRIDER|OPTION_MADOGEAR|OPTION_HANBOK) ? 0 : p->weapon; WBUFW(buf,58) = p->base_level; WBUFW(buf,60) = min(p->skill_point, INT16_MAX); @@ -2027,6 +2063,7 @@ void char_mmo_char_send_ban_list(int fd, struct char_session_data *sd) { int i; time_t now = time(NULL); + nullpo_retv(sd); ARR_FIND(0, MAX_CHARS, i, sd->unban_time[i]); if( i != MAX_CHARS ) { int c; @@ -2063,6 +2100,7 @@ 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) { + nullpo_retv(sd); WFIFOHEAD(fd,29); WFIFOW(fd,0) = 0x82d; WFIFOW(fd,2) = 29; @@ -2080,6 +2118,7 @@ void char_mmo_char_send_slots_info(int fd, struct char_session_data* sd) { int char_mmo_char_send_characters(int fd, struct char_session_data* sd) { int j, offset = 0; + nullpo_ret(sd); #if PACKETVER >= 20100413 offset += 3; #endif @@ -2201,11 +2240,13 @@ static void char_auth_ok(int fd, struct char_session_data *sd) { struct online_char_data* character; + nullpo_retv(sd); + if( (character = (struct online_char_data*)idb_get(chr->online_char_db, sd->account_id)) != NULL ) { // check if character is not online already. [Skotlex] if (character->server > -1) { //Character already online. KICK KICK KICK - mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); + mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); // 2: Already connected to server if (character->waiting_disconnect == INVALID_TIMER) character->waiting_disconnect = timer->add(timer->gettick()+20000, chr->waiting_disconnect, character->account_id, 0); character->pincode_enable = -1; @@ -2356,7 +2397,8 @@ void char_parse_fromlogin_account_data(int fd) void char_parse_fromlogin_login_pong(int fd) { RFIFOSKIP(fd,2); - session[fd]->flag.ping = 0; + if (session[fd]) + session[fd]->flag.ping = 0; } void char_changesex(int account_id, int sex) @@ -2369,15 +2411,52 @@ void char_changesex(int account_id, int sex) mapif->sendall(buf, 7); } +/** + * Performs the necessary operations when changing a character's sex, such as + * correcting the job class and unequipping items, and propagating the + * information to the guild data. + * + * @param sex The new sex (SEX_MALE or SEX_FEMALE). + * @param acc The character's account ID. + * @param char_id The character ID. + * @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) +{ + // job modification + if (class_ == JOB_BARD || class_ == JOB_DANCER) + class_ = (sex == SEX_MALE ? JOB_BARD : JOB_DANCER); + else if (class_ == JOB_CLOWN || class_ == JOB_GYPSY) + class_ = (sex == SEX_MALE ? JOB_CLOWN : JOB_GYPSY); + else if (class_ == JOB_BABY_BARD || class_ == JOB_BABY_DANCER) + class_ = (sex == SEX_MALE ? JOB_BABY_BARD : JOB_BABY_DANCER); + else if (class_ == JOB_MINSTREL || class_ == JOB_WANDERER) + class_ = (sex == SEX_MALE ? JOB_MINSTREL : JOB_WANDERER); + else if (class_ == JOB_MINSTREL_T || class_ == JOB_WANDERER_T) + class_ = (sex == SEX_MALE ? JOB_MINSTREL_T : JOB_WANDERER_T); + else if (class_ == JOB_BABY_MINSTREL || class_ == JOB_BABY_WANDERER) + class_ = (sex == SEX_MALE ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER); + else if (class_ == JOB_KAGEROU || class_ == JOB_OBORO) + class_ = (sex == SEX_MALE ? JOB_KAGEROU : JOB_OBORO); + + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `equip`='0' WHERE `char_id`='%d'", inventory_db, char_id)) + Sql_ShowDebug(inter->sql_handle); + + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `class`='%d', `weapon`='0', `shield`='0', " + "`head_top`='0', `head_mid`='0', `head_bottom`='0' WHERE `char_id`='%d'", + char_db, class_, char_id)) + Sql_ShowDebug(inter->sql_handle); + if (guild_id) // If there is a guild, update the guild_member data [Skotlex] + inter_guild->sex_changed(guild_id, acc, char_id, sex); +} + int char_parse_fromlogin_changesex_reply(int fd) { - int char_id[MAX_CHARS]; - int class_[MAX_CHARS]; - int guild_id[MAX_CHARS]; - int num; + int char_id = 0, class_ = 0, guild_id = 0; int i; - char* data; - struct char_auth_node* node; + struct char_auth_node *node; + SqlStmt *stmt; int acc = RFIFOL(fd,2); int sex = RFIFOB(fd,6); @@ -2385,65 +2464,31 @@ int char_parse_fromlogin_changesex_reply(int fd) RFIFOSKIP(fd,7); // This should _never_ happen - if( acc <= 0 ) { + if (acc <= 0) { ShowError("Received invalid account id from login server! (aid: %d)\n", acc); return 1; } node = (struct char_auth_node*)idb_get(auth_db, acc); - if( node != NULL ) + if (node != NULL) node->sex = sex; // get characters - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `char_id`,`class`,`guild_id` FROM `%s` WHERE `account_id` = '%d'", char_db, acc) ) - Sql_ShowDebug(inter->sql_handle); - for( i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i ) - { - SQL->GetData(inter->sql_handle, 0, &data, NULL); char_id[i] = atoi(data); - SQL->GetData(inter->sql_handle, 1, &data, NULL); class_[i] = atoi(data); - SQL->GetData(inter->sql_handle, 2, &data, NULL); guild_id[i] = atoi(data); + 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) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); } - num = i; - for( i = 0; i < num; ++i ) - { - if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER || - class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY || - class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER || - class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER || - class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T || - class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER || - class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO ) - { - // job modification - if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER ) - class_[i] = (sex ? JOB_BARD : JOB_DANCER); - else if( class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY ) - class_[i] = (sex ? JOB_CLOWN : JOB_GYPSY); - else if( class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER ) - class_[i] = (sex ? JOB_BABY_BARD : JOB_BABY_DANCER); - else if( class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER ) - class_[i] = (sex ? JOB_MINSTREL : JOB_WANDERER); - else if( class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T ) - class_[i] = (sex ? JOB_MINSTREL_T : JOB_WANDERER_T); - else if( class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER ) - class_[i] = (sex ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER); - else if( class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO ) - class_[i] = (sex ? JOB_KAGEROU : JOB_OBORO); - } - - if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `equip`='0' WHERE `char_id`='%d'", inventory_db, char_id[i]) ) - Sql_ShowDebug(inter->sql_handle); - - if( SQL_ERROR == SQL->Query(inter->sql_handle, - "UPDATE `%s` SET `class`='%d', `weapon`='0', `shield`='0', `head_top`='0', `head_mid`='0', " - "`head_bottom`='0' WHERE `char_id`='%d'", - char_db, class_[i], char_id[i]) ) - Sql_ShowDebug(inter->sql_handle); - - if( guild_id[i] )// If there is a guild, update the guild_member data [Skotlex] - inter_guild->sex_changed(guild_id[i], acc, char_id[i], sex); + 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); } - SQL->FreeResult(inter->sql_handle); + SQL->StmtFree(stmt); // disconnect player if online on char-server chr->disconnect_player(acc); @@ -2488,7 +2533,7 @@ void char_parse_fromlogin_kick(int fd) {// account is already marked as online! if( character->server > -1 ) { //Kick it from the map server it is on. - mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); + mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); // 2: Already connected to server if (character->waiting_disconnect == INVALID_TIMER) character->waiting_disconnect = timer->add(timer->gettick()+AUTH_TIMEOUT, chr->waiting_disconnect, character->account_id, 0); } @@ -2870,7 +2915,7 @@ void char_update_fame_list(int type, int index, int fame) { mapif->sendall(buf, 8); } -//Loads a character's name and stores it in the buffer given (must be NAME_LENGTH in size) +//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) { @@ -2943,6 +2988,7 @@ void mapif_on_disconnect(int id) } void mapif_on_parse_accinfo(int account_id, int u_fd, int u_aid, int u_group, int map_fd) { + Assert_retv(chr->login_fd > 0); WFIFOHEAD(chr->login_fd,22); WFIFOW(chr->login_fd,0) = 0x2740; WFIFOL(chr->login_fd,2) = account_id; @@ -2964,7 +3010,7 @@ void char_parse_frommap_skillid2idx(int fd) int i; int j = RFIFOW(fd, 2) - 4; - memset(&skillid2idx, 0, sizeof(skillid2idx)); + memset(&skillid2idx, 0, sizeof(skillid2idx)); if( j ) j /= 4; for(i = 0; i < j; i++) { @@ -3134,7 +3180,7 @@ void char_parse_frommap_set_users(int fd, int id) if (character->server > -1 && character->server != id) { ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n", character->account_id, character->char_id, character->server, id, aid, cid); - mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); + mapif->disconnectplayer(chr->server[character->server].fd, character->account_id, character->char_id, 2); // 2: Already connected to server } character->server = id; character->char_id = cid; @@ -3348,6 +3394,8 @@ void char_ban(int account_id, int char_id, time_t *unban_time, short year, short struct tm *tmtime; SqlStmt* stmt = SQL->StmtMalloc(inter->sql_handle); + nullpo_retv(unban_time); + if (*unban_time == 0 || *unban_time < time(NULL)) timestamp = time(NULL); // new ban else @@ -3363,13 +3411,12 @@ void char_ban(int account_id, int char_id, time_t *unban_time, short year, short timestamp = mktime(tmtime); if( SQL_SUCCESS != SQL->StmtPrepare(stmt, - "UPDATE `%s` SET `unban_time` = ? WHERE `char_id` = ? LIMIT 1", - char_db) + "UPDATE `%s` SET `unban_time` = ? WHERE `char_id` = ? LIMIT 1", + char_db) || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_LONG, (void*)×tamp, sizeof(timestamp)) || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_INT, (void*)&char_id, sizeof(char_id)) || SQL_SUCCESS != SQL->StmtExecute(stmt) - - ) { + ) { SqlStmt_ShowDebug(stmt); } @@ -3388,12 +3435,14 @@ 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) ) { Sql_ShowDebug(inter->sql_handle); - *result = 1; + if (result) + *result = 1; } } void char_ask_name_ack(int fd, int acc, const char* name, int type, int result) { + nullpo_retv(name); WFIFOHEAD(fd,34); WFIFOW(fd, 0) = 0x2b0f; WFIFOL(fd, 2) = acc; @@ -3403,6 +3452,49 @@ void char_ask_name_ack(int fd, int acc, const char* name, int type, int result) WFIFOSET(fd,34); } +/** + * Changes a character's sex. + * The information is updated on database, and the character is kicked if it + * currently is online. + * + * @param char_id The character's ID. + * @param sex The new sex. + * @retval 0 in case of success. + * @retval 1 in case of failure. + */ +int char_changecharsex(int char_id, int sex) +{ + int class_ = 0, guild_id = 0, account_id = 0; + char *data; + + // get character data + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`,`class`,`guild_id` FROM `%s` WHERE `char_id` = '%d'", char_db, char_id)) { + Sql_ShowDebug(inter->sql_handle); + return 1; + } + if (SQL->NumRows(inter->sql_handle) != 1 || SQL_ERROR == SQL->NextRow(inter->sql_handle)) { + SQL->FreeResult(inter->sql_handle); + return 1; + } + SQL->GetData(inter->sql_handle, 0, &data, NULL); account_id = atoi(data); + SQL->GetData(inter->sql_handle, 1, &data, NULL); class_ = atoi(data); + SQL->GetData(inter->sql_handle, 2, &data, NULL); guild_id = atoi(data); + SQL->FreeResult(inter->sql_handle); + + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `sex` = '%c' WHERE `char_id` = '%d'", char_db, sex == SEX_MALE ? 'M' : 'F', char_id)) { + Sql_ShowDebug(inter->sql_handle); + return 1; + } + char_change_sex_sub(sex, account_id, char_id, class_, guild_id); + + // disconnect player if online on char-server + chr->disconnect_player(account_id); + + // notify all mapservers about this change + chr->changesex(account_id, sex); + return 0; +} + 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 @@ -3411,31 +3503,40 @@ void char_parse_frommap_change_account(int fd) int acc = RFIFOL(fd,2); // account_id of who ask (-1 if server itself made this request) const char* name = (char*)RFIFOP(fd,6); // name of the target character int type = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5 changesex, 6 charban, 7 charunban - short year = RFIFOW(fd,32); - short month = RFIFOW(fd,34); - short day = RFIFOW(fd,36); - short hour = RFIFOW(fd,38); - short minute = RFIFOW(fd,40); - short second = RFIFOW(fd,42); + short year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; + int sex = SEX_MALE; + if (type == 2 || type == 6) { + year = RFIFOW(fd,32); + month = RFIFOW(fd,34); + day = RFIFOW(fd,36); + hour = RFIFOW(fd,38); + minute = RFIFOW(fd,40); + second = RFIFOW(fd,42); + } else if (type == 8) { + sex = RFIFOB(fd, 32); + } RFIFOSKIP(fd,44); SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); - if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`,`char_id`,`unban_time` FROM `%s` WHERE `name` = '%s'", char_db, esc_name) ) + if(SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`,`char_id`,`unban_time` FROM `%s` WHERE `name` = '%s'", char_db, esc_name)) { Sql_ShowDebug(inter->sql_handle); - else if( SQL->NumRows(inter->sql_handle) == 0 ) { + } else if (SQL->NumRows(inter->sql_handle) == 0) { + SQL->FreeResult(inter->sql_handle); result = 1; // 1-player not found - } else if( SQL_SUCCESS != SQL->NextRow(inter->sql_handle) ) { + } else if (SQL_SUCCESS != SQL->NextRow(inter->sql_handle)) { Sql_ShowDebug(inter->sql_handle); + SQL->FreeResult(inter->sql_handle); result = 1; // 1-player not found } else { int account_id, char_id; - char* data; + char *data; time_t unban_time; SQL->GetData(inter->sql_handle, 0, &data, NULL); account_id = atoi(data); SQL->GetData(inter->sql_handle, 1, &data, NULL); char_id = atoi(data); SQL->GetData(inter->sql_handle, 2, &data, NULL); unban_time = atol(data); + SQL->FreeResult(inter->sql_handle); if( chr->login_fd <= 0 ) { result = 3; // 3-login-server offline @@ -3444,40 +3545,38 @@ void char_parse_frommap_change_account(int fd) result = 2; // 2-gm level too low #endif // 0 } else { - switch( type ) { - case 1: // block - loginif->block_account(account_id, 5); + switch (type) { + case CHAR_ASK_NAME_BLOCK: + loginif->block_account(account_id, 5); break; - case 2: // ban - loginif->ban_account(account_id, year, month, day, hour, minute, second); + case CHAR_ASK_NAME_BAN: + loginif->ban_account(account_id, year, month, day, hour, minute, second); break; - case 3: // unblock - loginif->block_account(account_id, 0); + case CHAR_ASK_NAME_UNBLOCK: + loginif->block_account(account_id, 0); break; - case 4: // unban - loginif->unban_account(account_id); + case CHAR_ASK_NAME_UNBAN: + loginif->unban_account(account_id); break; - case 5: // changesex - loginif->changesex(account_id); + case CHAR_ASK_NAME_CHANGESEX: + loginif->changesex(account_id); break; - case 6: //char ban + case CHAR_ASK_NAME_CHARBAN: /* handled by char server, so no redirection */ - { - chr->ban(account_id, char_id, &unban_time, year, month, day, hour, minute, second); - } + chr->ban(account_id, char_id, &unban_time, year, month, day, hour, minute, second); break; - case 7: //char unban + case CHAR_ASK_NAME_CHARUNBAN: chr->unban(char_id, &result); break; - + case CHAR_ASK_NAME_CHANGECHARSEX: + result = chr->changecharsex(char_id, sex); + break; } } } - SQL->FreeResult(inter->sql_handle); - // send answer if a player ask, not if the server ask - if( acc != -1 && type != 5 ) { // Don't send answer for changesex + if (acc != -1 && type != CHAR_ASK_NAME_CHANGESEX && type != CHAR_ASK_NAME_CHANGECHARSEX) { // Don't send answer for changesex chr->ask_name_ack(fd, acc, name, type, result); } } @@ -3499,6 +3598,10 @@ void char_parse_frommap_fame_list(int fd) default: size = 0; list = NULL; break; } + if (!list) { + RFIFOSKIP(fd, 11); + return; + } ARR_FIND(0, size, player_pos, list[player_pos].id == cid);// position of the player ARR_FIND(0, size, fame_pos, list[fame_pos].fame <= fame);// where the player should be @@ -3624,6 +3727,7 @@ void char_parse_frommap_ping(int fd) 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)); WFIFOW(fd,0) = 0x2afd; WFIFOW(fd,2) = 25 + sizeof(struct mmo_charstatus); @@ -3729,7 +3833,7 @@ void char_parse_frommap_request_stats_report(int fd) opt.silent = 1; opt.setTimeo = 1; - if( (sfd = make_connection(host2ip("stats.hercules.ws"),(uint16)25427,&opt) ) == -1 ) { + if( (sfd = make_connection(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 */ @@ -4156,25 +4260,26 @@ static void char_delete2_req(int fd, struct char_session_data* sd) time_t delete_date; char_id = RFIFOL(fd,2); + nullpo_retv(sd); ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id ); if( i == MAX_CHARS ) {// character not found - chr->delete2_ack(fd, char_id, 3, 0); + chr->delete2_ack(fd, char_id, 3, 0); // 3: A database error occurred return; } if( SQL_SUCCESS != SQL->Query(inter->sql_handle, "SELECT `delete_date` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) || SQL_SUCCESS != SQL->NextRow(inter->sql_handle) ) { Sql_ShowDebug(inter->sql_handle); - chr->delete2_ack(fd, char_id, 3, 0); + chr->delete2_ack(fd, char_id, 3, 0); // 3: A database error occurred return; } SQL->GetData(inter->sql_handle, 0, &data, NULL); delete_date = strtoul(data, NULL, 10); if( delete_date ) {// character already queued for deletion - chr->delete2_ack(fd, char_id, 0, 0); + chr->delete2_ack(fd, char_id, 0, 0); // 0: An unknown error occurred return; } @@ -4187,7 +4292,7 @@ static void char_delete2_req(int fd, struct char_session_data* sd) || SQL_SUCCESS != SQL->NextRow(inter->sql_handle) ) { Sql_ShowDebug(inter->sql_handle); - chr->delete2_ack(fd, char_id, 3, 0); + chr->delete2_ack(fd, char_id, 3, 0); // 3: A database error occurred return; } SQL->GetData(inter->sql_handle, 0, &data, NULL); party_id = atoi(data); @@ -4195,13 +4300,13 @@ static void char_delete2_req(int fd, struct char_session_data* sd) if( guild_id ) { - chr->delete2_ack(fd, char_id, 4, 0); + chr->delete2_ack(fd, char_id, 4, 0); // 4: To delete a character you must withdraw from the guild return; } if( party_id ) { - chr->delete2_ack(fd, char_id, 5, 0); + chr->delete2_ack(fd, char_id, 5, 0); // 5: To delete a character you must withdraw from the party return; } } @@ -4212,11 +4317,11 @@ static void char_delete2_req(int fd, struct char_session_data* sd) if( SQL_SUCCESS != SQL->Query(inter->sql_handle, "UPDATE `%s` SET `delete_date`='%lu' WHERE `char_id`='%d'", char_db, (unsigned long)delete_date, char_id) ) { Sql_ShowDebug(inter->sql_handle); - chr->delete2_ack(fd, char_id, 3, 0); + chr->delete2_ack(fd, char_id, 3, 0); // 3: A database error occurred return; } - chr->delete2_ack(fd, char_id, 1, delete_date); + chr->delete2_ack(fd, char_id, 1, delete_date); // 1: success } static void char_delete2_accept(int fd, struct char_session_data* sd) @@ -4227,6 +4332,7 @@ static void char_delete2_accept(int fd, struct char_session_data* sd) char* data; time_t delete_date; + nullpo_retv(sd); char_id = RFIFOL(fd,2); ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, char_id); @@ -4245,14 +4351,14 @@ static void char_delete2_accept(int fd, struct char_session_data* sd) ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id ); if( i == MAX_CHARS ) {// character not found - chr->delete2_accept_ack(fd, char_id, 3); + chr->delete2_accept_ack(fd, char_id, 3); // 3: A database error occurred return; } if( SQL_SUCCESS != SQL->Query(inter->sql_handle, "SELECT `base_level`,`delete_date` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) || SQL_SUCCESS != SQL->NextRow(inter->sql_handle) ) {// data error Sql_ShowDebug(inter->sql_handle); - chr->delete2_accept_ack(fd, char_id, 3); + chr->delete2_accept_ack(fd, char_id, 3); // 3: A database error occurred return; } @@ -4261,45 +4367,46 @@ static void char_delete2_accept(int fd, struct char_session_data* sd) if( !delete_date || delete_date>time(NULL) ) {// not queued or delay not yet passed - chr->delete2_accept_ack(fd, char_id, 4); + chr->delete2_accept_ack(fd, char_id, 4); // 4: Deleting not yet possible time return; } if( strcmp(sd->birthdate+2, birthdate) ) // +2 to cut off the century {// birth date is wrong - chr->delete2_accept_ack(fd, char_id, 5); + chr->delete2_accept_ack(fd, char_id, 5); // 5: Date of birth do not match return; } if( ( char_del_level > 0 && base_level >= (unsigned int)char_del_level ) || ( char_del_level < 0 && base_level <= (unsigned int)(-char_del_level) ) ) {// character level config restriction - chr->delete2_accept_ack(fd, char_id, 2); + chr->delete2_accept_ack(fd, char_id, 2); // 2: Due to system settings can not be deleted return; } // success if( chr->delete_char_sql(char_id) < 0 ) { - chr->delete2_accept_ack(fd, char_id, 3); + chr->delete2_accept_ack(fd, char_id, 3); // 3: A database error occurred return; } // refresh character list cache sd->found_char[i] = -1; - chr->delete2_accept_ack(fd, char_id, 1); + chr->delete2_accept_ack(fd, char_id, 1); // 1: success } static void char_delete2_cancel(int fd, struct char_session_data* sd) {// CH: <082b>.W <char id>.L int char_id, i; + nullpo_retv(sd); char_id = RFIFOL(fd,2); ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id ); if( i == MAX_CHARS ) {// character not found - chr->delete2_cancel_ack(fd, char_id, 2); + chr->delete2_cancel_ack(fd, char_id, 2); // 2: A database error occurred return; } @@ -4309,11 +4416,11 @@ static void char_delete2_cancel(int fd, struct char_session_data* sd) if( SQL_SUCCESS != SQL->Query(inter->sql_handle, "UPDATE `%s` SET `delete_date`='0' WHERE `char_id`='%d'", char_db, char_id) ) { Sql_ShowDebug(inter->sql_handle); - chr->delete2_cancel_ack(fd, char_id, 2); + chr->delete2_cancel_ack(fd, char_id, 2); // 2: A database error occurred return; } - chr->delete2_cancel_ack(fd, char_id, 1); + chr->delete2_cancel_ack(fd, char_id, 1); // 1: success } void char_send_account_id(int fd, int account_id) @@ -4391,6 +4498,7 @@ void char_parse_char_connect(int fd, struct char_session_data* sd, uint32 ipl) void char_send_map_info(int fd, int i, uint32 subnet_map_ip, struct mmo_charstatus *cd) { + nullpo_retv(cd); WFIFOHEAD(fd,28); WFIFOW(fd,0) = 0x71; WFIFOL(fd,2) = cd->char_id; @@ -4413,6 +4521,7 @@ int char_search_default_maps_mapserver(struct mmo_charstatus *cd) { int i; int j; + nullpo_retr(-1, cd); if ((i = chr->search_mapserver((j=mapindex->name2id(MAP_PRONTERA)),-1,-1)) >= 0) { cd->last_point.x = 273; cd->last_point.y = 354; @@ -4440,6 +4549,7 @@ 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) { struct mmo_charstatus char_dat; @@ -4481,7 +4591,7 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) //Not found?? May be forged packet. Sql_ShowDebug(inter->sql_handle); SQL->FreeResult(inter->sql_handle); - chr->auth_error(fd, 0); + chr->auth_error(fd, 0); // rejected from server return; } @@ -4490,7 +4600,7 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) /* client doesn't let it get to this point if you're banned, so its a forged packet */ if( sd->found_char[slot] == char_id && sd->unban_time[slot] > time(NULL) ) { - chr->auth_error(fd, 0); + chr->auth_error(fd, 0); // rejected from server return; } @@ -4499,12 +4609,13 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) if( !chr->mmo_char_fromsql(char_id, &char_dat, true) ) { /* failed? set it back offline */ chr->set_char_offline(char_id, sd->account_id); /* failed to load something. REJECT! */ - chr->auth_error(fd, 0); + chr->auth_error(fd, 0); // rejected from server return;/* jump off this boat */ } //Have to switch over to the DB instance otherwise data won't propagate [Kevin] cd = (struct mmo_charstatus *)idb_get(chr->char_db_, char_id); + nullpo_retv(cd); if( cd->sex == 99 ) cd->sex = sd->sex; @@ -4529,14 +4640,14 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) ARR_FIND( 0, ARRAYLENGTH(chr->server), j, chr->server[j].fd >= 0 && chr->server[j].map ); if (j == ARRAYLENGTH(chr->server)) { ShowInfo("Connection Closed. No map servers available.\n"); - chr->authfail_fd(fd, 1); + chr->authfail_fd(fd, 1); // 1 = Server closed return; } i = chr->search_default_maps_mapserver(cd); if (i < 0) { ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(cd->last_point.map)); - chr->authfail_fd(fd, 1); + chr->authfail_fd(fd, 1); // 1 = Server closed return; } } @@ -4548,8 +4659,7 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl) ShowError("chr->parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i); chr->server[i].fd = -1; memset(&chr->server[i], 0, sizeof(struct mmo_map_server)); - //Send server closed. - chr->authfail_fd(fd, 1); + chr->authfail_fd(fd, 1); // 1 = Server closed return; } @@ -4604,18 +4714,22 @@ 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) { int result; - if( !char_new ) //turn character creation on/off [Kevin] + if( !char_new ) { + //turn character creation on/off [Kevin] result = -2; - else + } else { #if PACKETVER >= 20120307 result = chr->make_new_char_sql(sd, (char*)RFIFOP(fd,2), 1, 1, 1, 1, 1, 1, RFIFOB(fd,26),RFIFOW(fd,27),RFIFOW(fd,29)); #else result = chr->make_new_char_sql(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOB(fd,27),RFIFOB(fd,28),RFIFOB(fd,29),RFIFOB(fd,30),RFIFOB(fd,31),RFIFOB(fd,32),RFIFOW(fd,33),RFIFOW(fd,35)); #endif + } + //'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3) if (result < 0) { chr->creation_failed(fd, result); } else { @@ -4651,6 +4765,7 @@ void char_delete_char_ok(int fd) 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) { char email[40]; @@ -4658,7 +4773,7 @@ void char_parse_char_delete_char(int fd, struct char_session_data* sd, unsigned int i; #if PACKETVER >= 20110309 - if( pincode->enabled ){ // hack check + if (pincode->enabled) { // hack check struct online_char_data* character; character = (struct online_char_data*)idb_get(chr->online_char_db, sd->account_id); if( character && character->pincode_enable == -1 ){ @@ -4718,6 +4833,7 @@ 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) { int i, cid =RFIFOL(fd,2); @@ -4742,6 +4858,7 @@ 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) { int i, aid = RFIFOL(fd,2), cid =RFIFOL(fd,6); @@ -4777,6 +4894,7 @@ 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) { int i; @@ -4841,22 +4959,24 @@ void char_login_map_server_ack(int fd, uint8 flag) WFIFOSET(fd,3); } -void char_parse_char_login_map_server(int fd) +void char_parse_char_login_map_server(int fd, uint32 ipl) { char* l_user = (char*)RFIFOP(fd,2); char* l_pass = (char*)RFIFOP(fd,26); int i; l_user[23] = '\0'; l_pass[23] = '\0'; + ARR_FIND( 0, ARRAYLENGTH(chr->server), i, chr->server[i].fd <= 0 ); - if( runflag != CHARSERVER_ST_RUNNING || + if (runflag != CHARSERVER_ST_RUNNING || i == ARRAYLENGTH(chr->server) || strcmp(l_user, chr->userid) != 0 || - strcmp(l_pass, chr->passwd) != 0 ) + strcmp(l_pass, chr->passwd) != 0 || + !chr->lan_subnetcheck(ipl)) { - chr->login_map_server_ack(fd, 3); + chr->login_map_server_ack(fd, 3); // Failure } else { - chr->login_map_server_ack(fd, 0); + chr->login_map_server_ack(fd, 0); // Success chr->server[i].fd = fd; chr->server[i].ip = ntohl(RFIFOL(fd,54)); @@ -4872,35 +4992,39 @@ void char_parse_char_login_map_server(int fd) 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) { - if( RFIFOL(fd,2) == sd->account_id ) - pincode->check( fd, sd ); + if (RFIFOL(fd,2) == sd->account_id) + pincode->check(fd, sd); - RFIFOSKIP(fd,10); + 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) { - if( RFIFOL(fd,2) == sd->account_id ) - pincode->sendstate( fd, sd, PINCODE_NOTSET ); + if (RFIFOL(fd,2) == sd->account_id) + pincode->sendstate(fd, sd, PINCODE_NOTSET); - RFIFOSKIP(fd,6); + 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) { - if( RFIFOL(fd,2) == sd->account_id ) - pincode->change( fd, sd ); + if (RFIFOL(fd,2) == sd->account_id) + pincode->change(fd, sd); - RFIFOSKIP(fd,14); + 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) { - if( RFIFOL(fd,2) == sd->account_id ) - pincode->setnew( fd, sd ); - RFIFOSKIP(fd,10); + 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) @@ -4994,7 +5118,6 @@ int char_parse_char(int fd) FIFOSD_CHECK(3); { chr->parse_char_select(fd, sd, ipl); - } break; @@ -5099,7 +5222,7 @@ int char_parse_char(int fd) if (RFIFOREST(fd) < 60) return 0; { - chr->parse_char_login_map_server(fd); + chr->parse_char_login_map_server(fd, ipl); } return 0; // avoid processing of follow-up packets here @@ -5155,6 +5278,7 @@ int mapif_sendall(unsigned char *buf, unsigned int len) { int i, c; + nullpo_ret(buf); c = 0; for(i = 0; i < ARRAYLENGTH(chr->server); i++) { int fd; @@ -5173,6 +5297,7 @@ 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; @@ -5189,6 +5314,7 @@ int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) 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 ); @@ -5241,6 +5367,7 @@ static int char_send_accounts_tologin_sub(DBKey key, DBData *data, va_list ap) struct online_char_data* character = DB->data2ptr(data); int* i = va_arg(ap, int*); + nullpo_ret(character); if(character->server > -1) { WFIFOL(chr->login_fd,8+(*i)*4) = character->account_id; @@ -5307,6 +5434,7 @@ static int char_waiting_disconnect(int tid, int64 tick, int id, intptr_t data) { static int char_online_data_cleanup_sub(DBKey key, DBData *data, va_list ap) { struct online_char_data *character= DB->data2ptr(data); + nullpo_ret(character); if (character->fd != -1) return 0; //Character still connected if (character->server == -2) //Unknown server.. set them offline @@ -5955,6 +6083,7 @@ void char_defaults(void) memset(chr->userid, 0, sizeof(chr->userid)); memset(chr->passwd, 0, sizeof(chr->passwd)); + memset(chr->server_name, 0, sizeof(chr->server_name)); chr->ip = 0; chr->port = 6121; @@ -5978,6 +6107,7 @@ void char_defaults(void) chr->mmo_char_tosql = char_mmo_char_tosql; chr->memitemdata_to_sql = char_memitemdata_to_sql; chr->inventory_to_sql = char_inventory_to_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; @@ -6045,6 +6175,7 @@ void char_defaults(void) chr->ban = char_ban; chr->unban = char_unban; chr->ask_name_ack = char_ask_name_ack; + chr->changecharsex = char_changecharsex; chr->parse_frommap_change_account = char_parse_frommap_change_account; chr->parse_frommap_fame_list = char_parse_frommap_fame_list; chr->parse_frommap_divorce_char = char_parse_frommap_divorce_char; |