diff options
Diffstat (limited to 'src/char/char.c')
-rw-r--r-- | src/char/char.c | 203 |
1 files changed, 133 insertions, 70 deletions
diff --git a/src/char/char.c b/src/char/char.c index aac9ad20c..b3de22c00 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -425,8 +425,6 @@ static struct DBData char_create_charstatus(union DBKey key, va_list args) static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) { - int i = 0; - int count = 0; int diff = 0; char save_status[128]; //For displaying save information. [Skotlex] struct mmo_charstatus *cp; @@ -591,8 +589,9 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) //insert here. StrBuf->Clear(&buf); StrBuf->Printf(&buf, "INSERT INTO `%s`(`char_id`,`map`,`x`,`y`) VALUES ", memo_db); - for( i = 0, count = 0; i < MAX_MEMOPOINTS; ++i ) - { + + int count = 0; + for (int i = 0; i < MAX_MEMOPOINTS; ++i) { if( p->memo_point[i].map ) { if( count ) @@ -624,24 +623,29 @@ static 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_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; - if( p->skill[i].flag != SKILL_FLAG_PERMANENT && p->skill[i].flag != SKILL_FLAG_PERM_GRANTED && (p->skill[i].flag - SKILL_FLAG_REPLACED_LV_0) == 0 ) - continue; - if( count ) - StrBuf->AppendStr(&buf, ","); - StrBuf->Printf(&buf, "('%d','%d','%d','%d')", char_id, p->skill[i].id, - ( (p->skill[i].flag == SKILL_FLAG_PERMANENT || p->skill[i].flag == SKILL_FLAG_PERM_GRANTED) ? p->skill[i].lv : p->skill[i].flag - SKILL_FLAG_REPLACED_LV_0), - p->skill[i].flag == SKILL_FLAG_PERM_GRANTED ? p->skill[i].flag : 0);/* other flags do not need to be saved */ - ++count; - } + int count = 0; + for (int i = 0; i < MAX_SKILL_DB; ++i) { + if (p->skill[i].id == 0) + continue; + if (p->skill[i].flag == SKILL_FLAG_TEMPORARY) + continue; + if (p->skill[i].lv == 0 && (p->skill[i].flag == SKILL_FLAG_PERM_GRANTED || p->skill[i].flag == SKILL_FLAG_PERMANENT)) + continue; + if (p->skill[i].flag == SKILL_FLAG_REPLACED_LV_0) + continue; + + if (Assert_chk(p->skill[i].flag == SKILL_FLAG_PERMANENT || p->skill[i].flag == SKILL_FLAG_PERM_GRANTED || p->skill[i].flag > SKILL_FLAG_REPLACED_LV_0)) + continue; + if (count != 0) + StrBuf->AppendStr(&buf, ","); + int saved_lv = (p->skill[i].flag > SKILL_FLAG_REPLACED_LV_0) ? p->skill[i].flag - SKILL_FLAG_REPLACED_LV_0 : p->skill[i].lv; + int saved_flag = p->skill[i].flag == SKILL_FLAG_PERM_GRANTED ? p->skill[i].flag : 0; // other flags do not need to be saved + StrBuf->Printf(&buf, "('%d','%d','%d','%d')", char_id, p->skill[i].id, saved_lv, saved_flag); + + ++count; } - if( count ) - { - if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) ) - { + if (count != 0) { + if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { Sql_ShowDebug(inter->sql_handle); errors++; } @@ -651,7 +655,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) } diff = 0; - for(i = 0; i < MAX_FRIENDS; i++){ + for (int i = 0; i < MAX_FRIENDS; i++) { if(p->friends[i].char_id != cp->friends[i].char_id || p->friends[i].account_id != cp->friends[i].account_id){ diff = 1; @@ -669,8 +673,8 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) StrBuf->Clear(&buf); StrBuf->Printf(&buf, "INSERT INTO `%s` (`char_id`, `friend_account`, `friend_id`) VALUES ", friend_db); - for( i = 0, count = 0; i < MAX_FRIENDS; ++i ) - { + int count = 0; + for (int i = 0; i < MAX_FRIENDS; ++i) { if( p->friends[i].char_id > 0 ) { if( count ) @@ -695,7 +699,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) StrBuf->Clear(&buf); StrBuf->Printf(&buf, "REPLACE INTO `%s` (`char_id`, `hotkey`, `type`, `itemskill_id`, `skill_lvl`) VALUES ", hotkey_db); diff = 0; - for(i = 0; i < ARRAYLENGTH(p->hotkeys); i++){ + for (int i = 0; i < ARRAYLENGTH(p->hotkeys); i++) { if(memcmp(&p->hotkeys[i], &cp->hotkeys[i], sizeof(struct hotkey))) { if( diff ) @@ -1369,7 +1373,7 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa SqlStmt_ShowDebug(stmt); } - if( tmp_skill.flag != SKILL_FLAG_PERM_GRANTED ) + if (tmp_skill.flag != SKILL_FLAG_PERM_GRANTED) tmp_skill.flag = SKILL_FLAG_PERMANENT; for (i = 0; i < MAX_SKILL_DB && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i) { @@ -2531,19 +2535,39 @@ static void char_changesex(int account_id, int sex) } /** - * 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. + * Performs the necessary operations when changing a character's gender, + * 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 sex The character's new gender (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. - */ + * + **/ static void char_change_sex_sub(int sex, int acc, int char_id, int class, int guild_id) { - // job modification + struct SqlStmt *stmt = SQL->StmtMalloc(inter->sql_handle); + + /** If we can't save the data, there's nothing to do. **/ + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); + return; + } + + const char *query_inv = "UPDATE `%s` SET `equip`='0' WHERE `char_id`=?"; + + /** Don't change gender if resetting the view data fails to prevent character from being unable to login. **/ + if (SQL_ERROR == SQL->StmtPrepare(stmt, query_inv, inventory_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT32, &char_id, sizeof(char_id)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return; + } + + /** Correct the job class for gender specific jobs according to the passed gender. **/ if (class == JOB_BARD || class == JOB_DANCER) class = (sex == SEX_MALE ? JOB_BARD : JOB_DANCER); else if (class == JOB_CLOWN || class == JOB_GYPSY) @@ -2559,14 +2583,30 @@ static void char_change_sex_sub(int sex, int acc, int char_id, int class, int gu 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 PACKETVER >= 20141016 + char gender = (sex == SEX_MALE) ? 'M' : ((sex == SEX_FEMALE) ? 'F' : 'U'); +#else + char gender = 'U'; +#endif - 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] + const char *query_char = "UPDATE `%s` SET `class`=?, `weapon`='0', `shield`='0', `head_top`='0', " + "`head_mid`='0', `head_bottom`='0', `robe`='0', `sex`=? WHERE `char_id`=?"; + + /** Don't update guild data if changing gender fails to prevent data de-synchronisation. **/ + if (SQL_ERROR == SQL->StmtPrepare(stmt, query_char, char_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT32, &class, sizeof(class)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 1, SQLDT_ENUM, &gender, sizeof(gender)) + || SQL_ERROR == SQL->StmtBindParam(stmt, 2, SQLDT_INT32, &char_id, sizeof(char_id)) + || SQL_ERROR == SQL->StmtExecute(stmt)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return; + } + + SQL->StmtFree(stmt); + + /** Update guild member data if a guild ID was passed. **/ + if (guild_id != 0) inter_guild->sex_changed(guild_id, acc, char_id, sex); } @@ -2898,13 +2938,13 @@ static void char_global_accreg_to_login_add(const char *key, unsigned int index, if( val ) { char *sval = (char*)val; - len = strlen(sval)+1; + len = strlen(sval); - WFIFOB(chr->login_fd, nlen) = (unsigned char)len;/* won't be higher; the column size is 254 */ + WFIFOB(chr->login_fd, nlen) = (unsigned char)len; // Won't be higher; the column size is 255. nlen += 1; - safestrncpy(WFIFOP(chr->login_fd,nlen), sval, len); - nlen += len; + safestrncpy(WFIFOP(chr->login_fd, nlen), sval, len + 1); + nlen += len + 1; } } else { WFIFOB(chr->login_fd, nlen) = val ? 0 : 1; @@ -3500,45 +3540,68 @@ static void char_ask_name_ack(int fd, int acc, const char *name, int type, int r } /** - * Changes a character's sex. - * The information is updated on database, and the character is kicked if it - * currently is online. + * Changes a character's gender. + * 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. + * @param char_id The character ID + * @param sex The character's new gender (SEX_MALE or SEX_FEMALE). * @retval 0 in case of success. * @retval 1 in case of failure. - */ + * + **/ static int char_changecharsex(int char_id, int sex) { - int class = 0, guild_id = 0, account_id = 0; - char *data; + struct SqlStmt *stmt = SQL->StmtMalloc(inter->sql_handle); - // 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); + /** If we can't load the data, there's nothing to do. **/ + if (stmt == NULL) { + SqlStmt_ShowDebug(stmt); return 1; } - if (SQL->NumRows(inter->sql_handle) != 1 || SQL_ERROR == SQL->NextRow(inter->sql_handle)) { - SQL->FreeResult(inter->sql_handle); + + const char *query = "SELECT `account_id`, `class`, `guild_id` FROM `%s` WHERE `char_id`=?"; + int account_id = 0; + int class = 0; + int guild_id = 0; + + /** Abort changing gender if there was an error while loading the data. **/ + if (SQL_ERROR == SQL->StmtPrepare(stmt, query, char_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT32, &char_id, sizeof(char_id)) + || SQL_ERROR == SQL->StmtExecute(stmt) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT32, &account_id, sizeof(account_id), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT32, &class, sizeof(class), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_INT32, &guild_id, sizeof(guild_id), NULL, NULL)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); 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); + /** Abort changing gender if no character was found. **/ + if (SQL->StmtNumRows(stmt) < 1) { + ShowError("char_changecharsex: Requested non-existant character! (ID: %d)\n", char_id); + SQL->StmtFree(stmt); 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); + /** Abort changing gender if more than one character was found. **/ + if (SQL->StmtNumRows(stmt) > 1) { + ShowError("char_changecharsex: There are multiple characters with identical ID! (ID: %d)\n", char_id); + SQL->StmtFree(stmt); + return 1; + } + + /** Abort changing gender if fetching the data fails. **/ + if (SQL_ERROR == SQL->StmtNextRow(stmt)) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return 1; + } + + SQL->StmtFree(stmt); + char_change_sex_sub(sex, account_id, char_id, class, guild_id); + chr->disconnect_player(account_id); // Disconnect player if online on char-server. + chr->changesex(account_id, sex); // Notify all mapservers about this change. - // notify all mapservers about this change - chr->changesex(account_id, sex); return 0; } |