summaryrefslogtreecommitdiff
path: root/src/char
diff options
context:
space:
mode:
Diffstat (limited to 'src/char')
-rw-r--r--src/char/HPMchar.c1
-rw-r--r--src/char/Makefile.in2
-rw-r--r--src/char/char.c250
-rw-r--r--src/char/char.h7
-rw-r--r--src/char/int_guild.c24
-rw-r--r--src/char/int_guild.h2
-rw-r--r--src/char/int_pet.c2
-rw-r--r--src/char/int_pet.h2
-rw-r--r--src/char/int_rodex.c140
-rw-r--r--src/char/int_rodex.h6
-rw-r--r--src/char/inter.c6
-rw-r--r--src/char/mapif.c84
-rw-r--r--src/char/mapif.h7
-rw-r--r--src/char/packets_hc_struct.h45
14 files changed, 450 insertions, 128 deletions
diff --git a/src/char/HPMchar.c b/src/char/HPMchar.c
index db2c3702e..f3cf2cff4 100644
--- a/src/char/HPMchar.c
+++ b/src/char/HPMchar.c
@@ -57,6 +57,7 @@
#include "common/mapindex.h"
#include "common/mmo.h"
#include "common/nullpo.h"
+#include "common/packets.h"
#include "common/random.h"
#include "common/showmsg.h"
#include "common/socket.h"
diff --git a/src/char/Makefile.in b/src/char/Makefile.in
index 95c8df813..f159a443f 100644
--- a/src/char/Makefile.in
+++ b/src/char/Makefile.in
@@ -46,7 +46,7 @@ CHAR_C = char.c HPMchar.c loginif.c mapif.c geoip.c inter.c int_achievement.c in
CHAR_OBJ = $(addprefix obj_sql/, $(patsubst %.c,%.o,$(CHAR_C)))
CHAR_H = char.h HPMchar.h loginif.h mapif.h geoip.h inter.h int_achievement.h int_auction.h int_clan.h int_elemental.h \
int_guild.h int_homun.h int_mail.h int_mercenary.h int_party.h int_pet.h \
- int_quest.h int_rodex.h int_storage.h pincode.h
+ int_quest.h int_rodex.h int_storage.h pincode.h packets_hc_struct.h
CHAR_PH =
HAVE_MYSQL=@HAVE_MYSQL@
diff --git a/src/char/char.c b/src/char/char.c
index 54f6ca7d1..44225ecb5 100644
--- a/src/char/char.c
+++ b/src/char/char.c
@@ -21,7 +21,7 @@
#define HERCULES_CORE
#include "config/core.h" // CONSOLE_INPUT
-#include "char.h"
+#include "char/char.h"
#include "char/HPMchar.h"
#include "char/geoip.h"
@@ -41,6 +41,7 @@
#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"
@@ -53,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"
@@ -139,6 +141,7 @@ char char_name_letters[1024] = ""; // list of letters/symbols allowed (or not) i
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
static int max_connect_user = -1;
static int gm_allow_group = -1;
@@ -476,15 +479,25 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p)
(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->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->attendance_timer != cp->attendance_timer) || (p->title_id != cp->title_id) || (p->inventorySize != cp->inventorySize) ||
+ (p->allow_call != cp->allow_call)
) {
//Save status
unsigned int opt = 0;
- if( p->allow_party )
+ 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 )
+ if (p->show_equip)
opt |= OPT_SHOW_EQUIP;
+ if (p->allow_call)
+ opt |= OPT_ALLOW_CALL;
if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d',"
"`base_exp`='%"PRIu64"', `job_exp`='%"PRIu64"', `zeny`='%d',"
@@ -495,7 +508,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p)
"`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',`clan_id`='%d',`last_login`='%"PRId64"',`attendance_count`='%d',`attendance_timer`='%"PRId64"',"
- "`title_id`='%d'"
+ "`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,
@@ -508,7 +521,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p)
(unsigned long)p->delete_date, // FIXME: platform-dependent size
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->title_id, p->inventorySize,
p->account_id, p->char_id) )
{
Sql_ShowDebug(inter->sql_handle);
@@ -1049,7 +1062,7 @@ static int char_mmo_gender(const struct char_session_data *sd, const struct mmo_
//=====================================================================================================
// Loads the basic character rooster for the given account. Returns total buffer used.
-static 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;
@@ -1058,6 +1071,9 @@ static 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);
@@ -1079,13 +1095,13 @@ static 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`,`title_id`"
+ "`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, 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, 3, SQLDT_INT, &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)
@@ -1123,25 +1139,36 @@ static int char_mmo_chars_fromsql(struct char_session_data *sd, uint8 *buf)
|| 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;
}
@@ -1169,6 +1196,7 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa
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);
@@ -1188,7 +1216,7 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa
"`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`,`clan_id`,`last_login`, `attendance_count`, `attendance_timer`,"
- "`title_id`"
+ "`title_id`, `inventory_size`"
" FROM `%s` WHERE `char_id`=? LIMIT 1", char_db)
|| SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id)
|| SQL_ERROR == SQL->StmtExecute(stmt)
@@ -1196,7 +1224,7 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa
|| 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, 4, SQLDT_INT, &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)
@@ -1256,6 +1284,7 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa
|| 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);
@@ -1287,6 +1316,13 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa
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
@@ -1414,10 +1450,12 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa
SQL->StmtFree(stmt);
/* load options into proper vars */
- if( opt & OPT_ALLOW_PARTY )
+ if (opt & OPT_ALLOW_PARTY)
p->allow_party = true;
- if( opt & OPT_SHOW_EQUIP )
+ if (opt & OPT_SHOW_EQUIP)
p->show_equip = true;
+ if (opt & OPT_ALLOW_CALL)
+ p->allow_call = true;
cp = idb_ensure(chr->char_db_, char_id, chr->create_charstatus);
memcpy(cp, p, sizeof(struct mmo_charstatus));
@@ -1514,6 +1552,14 @@ static 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
@@ -1539,9 +1585,20 @@ static 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);
}
@@ -1653,7 +1710,7 @@ static int char_check_char_name(const char *name, const char *esc_name)
* -5: 'Symbols in Character Names are forbidden'
* char_id: Success
**/
-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)
+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, int starting_class, uint8 sex)
{
char name[NAME_LENGTH];
char esc_name[NAME_LENGTH*2+1];
@@ -1700,22 +1757,22 @@ static int char_make_new_char_sql(struct char_session_data *sd, const char *name
#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`, `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')",
+ "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`, `sex`, `inventory_size`) 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', '%d')",
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, sex)) {
+ mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y, sex, FIXED_INVENTORY_SIZE)) {
Sql_ShowDebug(inter->sql_handle);
return -2; //No, stop the procedure!
}
#else
//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`, `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', '%s', '%d', '%d', '%s', '%d', '%d')",
+ "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`, `inventory_size`) VALUES ("
+ "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d', '%d')",
char_db, sd->account_id , slot, esc_name, starting_class, start_zeny, 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) )
+ mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y, FIXED_INVENTORY_SIZE) )
{
Sql_ShowDebug(inter->sql_handle);
return -2; //No, stop the procedure!
@@ -1967,7 +2024,7 @@ static 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)
+#define MAX_CHAR_BUF (PACKET_LEN_0x006d - 2)
static int char_mmo_char_tobuf(uint8 *buffer, struct mmo_charstatus *p)
{
unsigned short offset = 0;
@@ -2072,18 +2129,36 @@ static 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 */
-static 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) {
+ chr->send_HC_ACK_CHARINFO_PER_PAGE_tail(fd, sd);
+ }
+#endif
+}
+
+static void char_send_HC_ACK_CHARINFO_PER_PAGE_tail(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));
+ 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
}
@@ -2132,17 +2207,20 @@ static void char_mmo_char_send_ban_list(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
@@ -2166,7 +2244,7 @@ static 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);
@@ -2315,19 +2393,29 @@ static void char_ping_login_server(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));
+ switch (RFIFOB(fd,2)) {
+ case 0:
+ ShowStatus("Connected to login-server (connection #%d).\n", fd);
+ loginif->on_ready();
+ break;
+ case 1: // Invalid username/password
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/map-server.conf and /conf/char/char-server.conf\n");
sockt->eof(fd);
return 1;
- } else {
- ShowStatus("Connected to login-server (connection #%d).\n", fd);
- loginif->on_ready();
+ case 2: // IP not allowed
+ ShowError("Can not connect to login-server.\n");
+ ShowError("Please make sure your IP is allowed in conf/network.conf\n");
+ sockt->eof(fd);
+ return 1;
+ default:
+ ShowError("Invalid response from the login-server. Error code: %d\n", (int)RFIFOB(fd,2));
+ sockt->eof(fd);
+ return 1;
}
- RFIFOSKIP(fd,3);
+ RFIFOSKIP(fd, 3);
return 0;
}
@@ -2409,12 +2497,8 @@ static 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
@@ -4151,10 +4235,10 @@ static void char_delete2_accept_actual_ack(int fd, int char_id, uint32 result)
/// Any (0x718): An unknown error has occurred.
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);
@@ -4345,9 +4429,9 @@ static void char_delete2_cancel(int fd, struct char_session_data *sd)
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);
}
static void char_parse_char_connect(int fd, struct char_session_data *sd, uint32 ipl)
@@ -4382,6 +4466,7 @@ static void char_parse_char_connect(int fd, struct char_session_data *sd, uint32
if( core->runflag != CHARSERVER_ST_RUNNING ) {
chr->auth_error(fd, 0);
+ sockt->eof(fd);
return;
}
@@ -4396,11 +4481,13 @@ static void char_parse_char_connect(int fd, struct char_session_data *sd, uint32
/* 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);
@@ -4412,6 +4499,7 @@ static void char_parse_char_connect(int fd, struct char_session_data *sd, uint32
loginif->auth(fd, sd, ipl);
} else { // if no login-server, we must refuse connection
chr->auth_error(fd, 0);
+ sockt->eof(fd);
}
}
}
@@ -4558,8 +4646,19 @@ static void char_parse_char_select(int fd, struct char_session_data *sd, uint32
// 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);
@@ -4621,7 +4720,8 @@ static void char_creation_failed(int fd, int result)
/* 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'
@@ -4900,10 +5000,10 @@ static void char_parse_char_delete2_cancel(int fd, struct char_session_data *sd)
// 3 - error
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);
}
static void char_parse_char_login_map_server(int fd, uint32 ipl)
@@ -4921,6 +5021,7 @@ static 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
@@ -4930,6 +5031,7 @@ static 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);
}
@@ -4975,7 +5077,7 @@ static void char_parse_char_pincode_first_pin(int fd, struct char_session_data *
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);
}
@@ -4995,8 +5097,8 @@ static void char_parse_char_move_character(int fd, struct char_session_data *sd)
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
@@ -5299,6 +5401,7 @@ static int char_check_connect_login_server(int tid, int64 tick, int id, intptr_t
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();
@@ -5848,6 +5951,7 @@ static bool char_config_read_player_name(const char *filename, const struct conf
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;
}
@@ -6290,6 +6394,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);
@@ -6390,7 +6495,8 @@ void char_defaults(void)
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->send_HC_ACK_CHARINFO_PER_PAGE_tail = char_send_HC_ACK_CHARINFO_PER_PAGE_tail;
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;
diff --git a/src/char/char.h b/src/char/char.h
index 81cab1eaf..5de3e2a80 100644
--- a/src/char/char.h
+++ b/src/char/char.h
@@ -142,18 +142,19 @@ struct char_interface {
int (*getitemdata_from_sql) (struct item *items, int max, int guid, enum inventory_table_type table);
int (*memitemdata_to_sql) (const struct item items[], int id, enum inventory_table_type table);
int (*mmo_gender) (const struct char_session_data *sd, const struct mmo_charstatus *p, char sex);
- int (*mmo_chars_fromsql) (struct char_session_data* sd, uint8* buf);
+ int (*mmo_chars_fromsql) (struct char_session_data* sd, uint8* buf, int *count);
int (*mmo_char_fromsql) (int char_id, struct mmo_charstatus* p, bool load_everything);
int (*mmo_char_sql_init) (void);
bool (*char_slotchange) (struct char_session_data *sd, int fd, unsigned short from, unsigned short to);
int (*rename_char_sql) (struct char_session_data *sd, int char_id);
bool (*name_exists) (const char *name, const char *esc_name);
int (*check_char_name) (const char *name, const char *esc_name);
- int (*make_new_char_sql) (struct char_session_data *sd, const char *name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style, short starting_job, uint8 sex);
+ int (*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, int starting_job, uint8 sex);
int (*divorce_char_sql) (int partner_id1, int partner_id2);
int (*count_users) (void);
int (*mmo_char_tobuf) (uint8* buffer, struct mmo_charstatus* p);
- void (*mmo_char_send099d) (int fd, struct char_session_data *sd);
+ void (*send_HC_ACK_CHARINFO_PER_PAGE) (int fd, struct char_session_data *sd);
+ void (*send_HC_ACK_CHARINFO_PER_PAGE_tail) (int fd, struct char_session_data *sd);
void (*mmo_char_send_ban_list) (int fd, struct char_session_data *sd);
void (*mmo_char_send_slots_info) (int fd, struct char_session_data* sd);
int (*mmo_char_send_characters) (int fd, struct char_session_data* sd);
diff --git a/src/char/int_guild.c b/src/char/int_guild.c
index 56e1c1ba3..8e05c76e2 100644
--- a/src/char/int_guild.c
+++ b/src/char/int_guild.c
@@ -319,8 +319,8 @@ static int inter_guild_tosql(struct guild *g, int flag)
SQL->EscapeStringLen(inter->sql_handle, esc_name, e->name, strnlen(e->name, NAME_LENGTH));
SQL->EscapeStringLen(inter->sql_handle, esc_mes, e->mes, strnlen(e->mes, sizeof(e->mes)));
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "REPLACE INTO `%s` (`guild_id`,`account_id`,`name`,`mes`) "
- "VALUES ('%d','%d','%s','%s')", guild_expulsion_db, g->guild_id, e->account_id, esc_name, esc_mes) )
+ if( SQL_ERROR == SQL->Query(inter->sql_handle, "REPLACE INTO `%s` (`guild_id`,`account_id`, `char_id`, `name`,`mes`) "
+ "VALUES ('%d','%d','%d','%s','%s')", guild_expulsion_db, g->guild_id, e->account_id, e->char_id, esc_name, esc_mes) )
Sql_ShowDebug(inter->sql_handle);
}
}
@@ -444,8 +444,14 @@ static struct guild *inter_guild_fromsql(int guild_id)
m->position = MAX_GUILDPOSITION - 1;
SQL->GetData(inter->sql_handle, 11, &data, &len); memcpy(m->name, data, min(len, NAME_LENGTH));
SQL->GetData(inter->sql_handle, 12, &data, NULL);
- if (data != NULL)
+ if (data != NULL) {
m->last_login = atoi(data);
+ // 2036-12-31
+ if (m->last_login > 2114283600) {
+ ShowError("Last login time bigger than allowd value in %d:%s: %u\n", guild_id, g->name, m->last_login);
+ m->last_login = 0;
+ }
+ }
m->modified = GS_MEMBER_UNMODIFIED;
}
@@ -488,7 +494,7 @@ static struct guild *inter_guild_fromsql(int guild_id)
}
//printf("- Read guild_expulsion %d from sql \n",guild_id);
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`,`name`,`mes` FROM `%s` WHERE `guild_id`='%d'", guild_expulsion_db, guild_id) )
+ if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `account_id`,`char_id`,`name`,`mes` FROM `%s` WHERE `guild_id`='%d'", guild_expulsion_db, guild_id) )
{
Sql_ShowDebug(inter->sql_handle);
aFree(g);
@@ -499,8 +505,9 @@ static struct guild *inter_guild_fromsql(int guild_id)
struct guild_expulsion *e = &g->expulsion[i];
SQL->GetData(inter->sql_handle, 0, &data, NULL); e->account_id = atoi(data);
- SQL->GetData(inter->sql_handle, 1, &data, &len); memcpy(e->name, data, min(len, NAME_LENGTH));
- SQL->GetData(inter->sql_handle, 2, &data, &len); memcpy(e->mes, data, min(len, sizeof(e->mes)));
+ SQL->GetData(inter->sql_handle, 1, &data, NULL); e->char_id = atoi(data);
+ SQL->GetData(inter->sql_handle, 2, &data, &len); memcpy(e->name, data, min(len, NAME_LENGTH));
+ SQL->GetData(inter->sql_handle, 3, &data, &len); memcpy(e->mes, data, min(len, sizeof(e->mes)));
}
//printf("- Read guild_skill %d from sql \n",guild_id);
@@ -1038,6 +1045,7 @@ static bool inter_guild_leave(int guild_id, int account_id, int char_id, int fla
}
// Save the expulsion entry
g->expulsion[j].account_id = account_id;
+ g->expulsion[j].char_id = char_id;
safestrncpy(g->expulsion[j].name, g->member[i].name, NAME_LENGTH);
safestrncpy(g->expulsion[j].mes, mes, 40);
}
@@ -1060,7 +1068,7 @@ static bool inter_guild_leave(int guild_id, int account_id, int char_id, int fla
}
// Change member info
-static bool inter_guild_update_member_info_short(int guild_id, int account_id, int char_id, int online, int lv, int16 class)
+static bool inter_guild_update_member_info_short(int guild_id, int account_id, int char_id, int online, int lv, int class)
{
// Could speed up by manipulating only guild_member
struct guild *g;
@@ -1602,7 +1610,7 @@ static int inter_guild_parse_frommap(int fd)
case 0x3032: mapif->parse_GuildAddMember(fd, RFIFOL(fd,4), RFIFOP(fd,8)); break;
case 0x3033: mapif->parse_GuildMasterChange(fd, RFIFOL(fd,4), RFIFOP(fd,8), RFIFOW(fd,2)-8); break;
case 0x3034: mapif->parse_GuildLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOB(fd,14), RFIFOP(fd,15)); break;
- case 0x3035: mapif->parse_GuildChangeMemberInfoShort(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOW(fd,15),RFIFOW(fd,17)); break;
+ case 0x3035: mapif->parse_GuildChangeMemberInfoShort(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOL(fd,15),RFIFOL(fd,19)); break;
case 0x3036: mapif->parse_BreakGuild(fd,RFIFOL(fd,2)); break;
case 0x3037: mapif->parse_GuildMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,12), RFIFOW(fd,2)-12); break;
case 0x3039: mapif->parse_GuildBasicInfoChange(fd, RFIFOL(fd,4), RFIFOW(fd,8), RFIFOP(fd,10), RFIFOW(fd,2)-10); break;
diff --git a/src/char/int_guild.h b/src/char/int_guild.h
index 33873edcd..4ed0f526e 100644
--- a/src/char/int_guild.h
+++ b/src/char/int_guild.h
@@ -72,7 +72,7 @@ struct inter_guild_interface {
struct guild *(*create) (const char *name, const struct guild_member *master);
bool (*add_member) (int guild_id, const struct guild_member *member, int map_fd);
bool (*leave) (int guild_id, int account_id, int char_id, int flag, const char *mes, int map_fd);
- bool (*update_member_info_short) (int guild_id, int account_id, int char_id, int online, int lv, int16 class);
+ bool (*update_member_info_short) (int guild_id, int account_id, int char_id, int online, int lv, int class);
bool (*update_member_info) (int guild_id, int account_id, int char_id, int type, const char *data, int len);
bool (*disband) (int guild_id);
bool (*update_basic_info) (int guild_id, int type, const void *data, int len);
diff --git a/src/char/int_pet.c b/src/char/int_pet.c
index 8f87becff..d31e7545c 100644
--- a/src/char/int_pet.c
+++ b/src/char/int_pet.c
@@ -160,7 +160,7 @@ static int inter_pet_delete(int pet_id)
return 0;
}
//------------------------------------------------------
-static struct s_pet *inter_pet_create(int account_id, int char_id, short pet_class, short pet_lv, int pet_egg_id,
+static struct s_pet *inter_pet_create(int account_id, int char_id, int pet_class, int pet_lv, int pet_egg_id,
int pet_equip, short intimate, short hungry, char rename_flag, char incubate, const char *pet_name)
{
nullpo_ret(pet_name);
diff --git a/src/char/int_pet.h b/src/char/int_pet.h
index 104771735..b5852d441 100644
--- a/src/char/int_pet.h
+++ b/src/char/int_pet.h
@@ -37,7 +37,7 @@ struct inter_pet_interface {
int (*delete_) (int pet_id);
int (*parse_frommap) (int fd);
- struct s_pet *(*create) (int account_id, int char_id, short pet_class, short pet_lv, int pet_egg_id,
+ struct s_pet *(*create) (int account_id, int char_id, int pet_class, int pet_lv, int pet_egg_id,
int pet_equip, short intimate, short hungry, char rename_flag, char incubate, const char *pet_name);
struct s_pet *(*load) (int account_id, int char_id, int pet_id);
};
diff --git a/src/char/int_rodex.c b/src/char/int_rodex.c
index 18c277574..ffa5f5452 100644
--- a/src/char/int_rodex.c
+++ b/src/char/int_rodex.c
@@ -268,7 +268,7 @@ static bool inter_rodex_hasnew(int char_id, int account_id)
}
/// Checks player name and retrieves some data
-static bool inter_rodex_checkname(const char *name, int *target_char_id, short *target_class, int *target_level)
+static bool inter_rodex_checkname(const char *name, int *target_char_id, int *target_class, int *target_level)
{
char esc_name[NAME_LENGTH * 2 + 1];
bool found = false;
@@ -286,7 +286,7 @@ static bool inter_rodex_checkname(const char *name, int *target_char_id, short *
if (SQL_SUCCESS == SQL->NextRow(inter->sql_handle)) {
char *data;
SQL->GetData(inter->sql_handle, 0, &data, NULL); *target_char_id = atoi(data);
- SQL->GetData(inter->sql_handle, 1, &data, NULL); *target_class = (short)atoi(data);
+ SQL->GetData(inter->sql_handle, 1, &data, NULL); *target_class = atoi(data);
SQL->GetData(inter->sql_handle, 2, &data, NULL); *target_level = atoi(data);
found = true;
}
@@ -346,11 +346,128 @@ static int64 inter_rodex_savemessage(struct rodex_message *msg)
return msg->id;
}
+static int64 inter_rodex_getzeny(int64 mail_id)
+{
+ Assert_retr(-1, mail_id > 0);
+
+ if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `zeny`, `type` FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) {
+ Sql_ShowDebug(inter->sql_handle);
+ } else {
+ if (SQL_SUCCESS == SQL->NextRow(inter->sql_handle)) {
+ char *data;
+ SQL->GetData(inter->sql_handle, 0, &data, NULL);
+ int64 zeny = atoi(data);
+ SQL->GetData(inter->sql_handle, 1, &data, NULL);
+ uint8 type = atoi(data);
+ SQL->FreeResult(inter->sql_handle);
+ if ((type & MAIL_TYPE_ZENY) == 0)
+ return -1;
+ return zeny;
+ }
+ }
+ SQL->FreeResult(inter->sql_handle);
+
+ return -1;
+}
+
+static int inter_rodex_getitems(int64 mail_id, struct rodex_item *items)
+{
+ Assert_retr(-1, mail_id > 0);
+ nullpo_retr(-1, items);
+
+ if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `type` FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) {
+ Sql_ShowDebug(inter->sql_handle);
+ return -1;
+ } else {
+ if (SQL_SUCCESS == SQL->NextRow(inter->sql_handle)) {
+ char *data;
+ SQL->GetData(inter->sql_handle, 0, &data, NULL);
+ uint8 type = atoi(data);
+ SQL->FreeResult(inter->sql_handle);
+ if ((type & MAIL_TYPE_ITEM) == 0)
+ return -1;
+ } else {
+ SQL->FreeResult(inter->sql_handle);
+ return -1;
+ }
+ }
+
+
+ int itemsCount = 0;
+
+ struct SqlStmt *stmt_items = SQL->StmtMalloc(inter->sql_handle);
+
+ if (stmt_items == NULL) {
+ return -1;
+ }
+
+ StringBuf buf;
+ StrBuf->Init(&buf);
+
+ StrBuf->AppendStr(&buf, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`");
+ for (int i = 0; i < MAX_SLOTS; i++) {
+ StrBuf->Printf(&buf, ", `card%d`", i);
+ }
+ for (int i = 0; i < MAX_ITEM_OPTIONS; i++) {
+ StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", i, i);
+ }
+ StrBuf->Printf(&buf, "FROM `%s` WHERE mail_id = ? ORDER BY `mail_id` ASC", rodex_item_db);
+
+ struct item it = { 0 };
+
+ if (SQL_ERROR == SQL->StmtPrepareStr(stmt_items, StrBuf->Value(&buf))
+ || SQL_ERROR == SQL->StmtBindParam(stmt_items, 0, SQLDT_INT64, &mail_id, sizeof mail_id)
+ ) {
+ SqlStmt_ShowDebug(stmt_items);
+ }
+
+ if (SQL_ERROR == SQL->StmtExecute(stmt_items)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 0, SQLDT_INT, &it.nameid, sizeof it.nameid, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 1, SQLDT_SHORT, &it.amount, sizeof it.amount, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 2, SQLDT_UINT, &it.equip, sizeof it.equip, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 3, SQLDT_CHAR, &it.identify, sizeof it.identify, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 4, SQLDT_CHAR, &it.refine, sizeof it.refine, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 5, SQLDT_CHAR, &it.attribute, sizeof it.attribute, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 6, SQLDT_UINT, &it.expire_time, sizeof it.expire_time, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 7, SQLDT_UCHAR, &it.bound, sizeof it.bound, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 8, SQLDT_UINT64, &it.unique_id, sizeof it.unique_id, NULL, NULL)
+ ) {
+ SqlStmt_ShowDebug(stmt_items);
+ }
+ for (int i = 0; i < MAX_SLOTS; i++) {
+ if (SQL_ERROR == SQL->StmtBindColumn(stmt_items, 9 + i, SQLDT_INT, &it.card[i], sizeof it.card[i], NULL, NULL))
+ SqlStmt_ShowDebug(stmt_items);
+ }
+ for (int i = 0; i < MAX_ITEM_OPTIONS; i++) {
+ if (SQL_ERROR == SQL->StmtBindColumn(stmt_items, 9 + MAX_SLOTS + i * 2, SQLDT_INT16, &it.option[i].index, sizeof it.option[i].index, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 10 + MAX_SLOTS + i * 2, SQLDT_INT16, &it.option[i].value, sizeof it.option[i].value, NULL, NULL)
+ ) {
+ SqlStmt_ShowDebug(stmt_items);
+ }
+ }
+
+ for (int i = 0; i < RODEX_MAX_ITEM && SQL_SUCCESS == SQL->StmtNextRow(stmt_items); ++i) {
+ items[i].item = it;
+ items[i].idx = itemsCount;
+ itemsCount++;
+ }
+
+ SQL->StmtFreeResult(stmt_items);
+
+ StrBuf->Destroy(&buf);
+ SQL->StmtFree(stmt_items);
+
+ return itemsCount;
+}
+
/*==========================================
* Update/Delete mail
*------------------------------------------*/
-static bool inter_rodex_updatemail(int64 mail_id, int8 flag)
+static bool inter_rodex_updatemail(int fd, int account_id, int char_id, int64 mail_id, uint8 opentype, int8 flag)
{
+ Assert_retr(false, fd >= 0);
+ Assert_retr(false, account_id > 0);
+ Assert_retr(false, char_id > 0);
Assert_retr(false, mail_id > 0);
Assert_retr(false, flag >= 0 && flag <= 4);
@@ -361,17 +478,26 @@ static bool inter_rodex_updatemail(int64 mail_id, int8 flag)
break;
case 1: // Get Zeny
- if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `zeny` = 0, `type` = `type` & (~2) WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id))
+ {
+ const int64 zeny = inter_rodex->getzeny(mail_id);
+ if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `zeny` = 0, `type` = `type` & (~2) WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) {
Sql_ShowDebug(inter->sql_handle);
+ break;
+ }
+ mapif->rodex_getzenyack(fd, char_id, mail_id, opentype, zeny);
break;
-
+ }
case 2: // Get Items
+ {
+ struct rodex_item items[RODEX_MAX_ITEM];
+ const int count = inter_rodex->getitems(mail_id, &items[0]);
if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_item_db, mail_id))
Sql_ShowDebug(inter->sql_handle);
if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `zeny` = 0, `type` = `type` & (~4) WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id))
Sql_ShowDebug(inter->sql_handle);
+ mapif->rodex_getitemsack(fd, char_id, mail_id, opentype, count, &items[0]);
break;
-
+ }
case 3: // Delete Mail
if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id))
Sql_ShowDebug(inter->sql_handle);
@@ -429,4 +555,6 @@ void inter_rodex_defaults(void)
inter_rodex->hasnew = inter_rodex_hasnew;
inter_rodex->checkname = inter_rodex_checkname;
inter_rodex->updatemail = inter_rodex_updatemail;
+ inter_rodex->getzeny = inter_rodex_getzeny;
+ inter_rodex->getitems = inter_rodex_getitems;
}
diff --git a/src/char/int_rodex.h b/src/char/int_rodex.h
index 43e2d891c..a6a172ceb 100644
--- a/src/char/int_rodex.h
+++ b/src/char/int_rodex.h
@@ -34,9 +34,11 @@ struct inter_rodex_interface {
int (*parse_frommap) (int fd);
int (*fromsql) (int char_id, int account_id, int8 opentype, int64 mail_id, struct rodex_maillist *mails);
bool (*hasnew) (int char_id, int account_id);
- bool (*checkname) (const char *name, int *target_char_id, short *target_class, int *target_level);
+ bool (*checkname) (const char *name, int *target_char_id, int *target_class, int *target_level);
int64 (*savemessage) (struct rodex_message* msg);
- bool (*updatemail) (int64 mail_id, int8 flag);
+ bool (*updatemail) (int fd, int account_id, int char_id, int64 mail_id, uint8 opentype, int8 flag);
+ int64 (*getzeny) (int64 mail_id);
+ int (*getitems) (int64 mail_id, struct rodex_item *items);
};
#ifdef HERCULES_CORE
diff --git a/src/char/inter.c b/src/char/inter.c
index 418c9b0a1..64c840c16 100644
--- a/src/char/inter.c
+++ b/src/char/inter.c
@@ -73,13 +73,13 @@ static int inter_recv_packet_length[] = {
-1,-1, 7,-1, -1,13,36, (2 + 4 + 4 + 4 + NAME_LENGTH), 0, 0, 0, 0, 0, 0, 0, 0, // 3000-
6,-1, 6,-1, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- Account Storage, Achievements [Smokexyz]
-1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party
- -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030-
+ -1, 6,-1,-1, 55,23, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030-
-1, 9, 0, 0, 10,10, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- Clan System(3044-3045)
-1,-1,10,10, 0,-1,12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus], Item Bound [Mhalicot]
6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish]
-1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil]
- 52,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080-
- -1,10,-1, 6, 0, 20,10,11, -1,6 + NAME_LENGTH, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator], RoDEX packets
+ 56,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080-
+ -1,10,-1, 6, 0, 20,10,20, -1,6 + NAME_LENGTH, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator], RoDEX packets
};
static struct DBMap *wis_db = NULL; // int wis_id -> struct WisData*
diff --git a/src/char/mapif.c b/src/char/mapif.c
index dc5735550..8f213ecb5 100644
--- a/src/char/mapif.c
+++ b/src/char/mapif.c
@@ -569,7 +569,7 @@ static int mapif_guild_withdraw(int guild_id, int account_id, int char_id, int f
// Send short member's info
static int mapif_guild_memberinfoshort(struct guild *g, int idx)
{
- unsigned char buf[23];
+ unsigned char buf[25];
nullpo_ret(g);
Assert_ret(idx >= 0 && idx < MAX_GUILD);
WBUFW(buf, 0) = 0x3835;
@@ -578,9 +578,9 @@ static int mapif_guild_memberinfoshort(struct guild *g, int idx)
WBUFL(buf, 10) = g->member[idx].char_id;
WBUFB(buf, 14) = (unsigned char)g->member[idx].online;
WBUFW(buf, 15) = g->member[idx].lv;
- WBUFW(buf, 17) = g->member[idx].class;
- WBUFL(buf, 19) = g->member[idx].last_login;
- mapif->sendall(buf, 23);
+ WBUFL(buf, 17) = g->member[idx].class;
+ WBUFL(buf, 21) = g->member[idx].last_login;
+ mapif->sendall(buf, 25);
return 0;
}
@@ -797,7 +797,7 @@ static int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char
}
// Change member info
-static int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, int char_id, int online, int lv, int16 class)
+static int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, int char_id, int online, int lv, int class)
{
inter_guild->update_member_info_short(guild_id, account_id, char_id, online, lv, class);
return 0;
@@ -1423,18 +1423,18 @@ static int mapif_parse_PartyLeaderChange(int fd, int party_id, int account_id, i
static int mapif_pet_created(int fd, int account_id, struct s_pet *p)
{
- WFIFOHEAD(fd, 12);
+ WFIFOHEAD(fd, 14);
WFIFOW(fd, 0) = 0x3880;
WFIFOL(fd, 2) = account_id;
if (p != NULL){
- WFIFOW(fd, 6) = p->class_;
- WFIFOL(fd, 8) = p->pet_id;
+ WFIFOL(fd, 6) = p->class_;
+ WFIFOL(fd, 10) = p->pet_id;
ShowInfo("int_pet: created pet %d - %s\n", p->pet_id, p->name);
} else {
- WFIFOB(fd, 6) = 0;
- WFIFOL(fd, 8) = 0;
+ WFIFOL(fd, 6) = 0;
+ WFIFOL(fd, 10) = 0;
}
- WFIFOSET(fd, 12);
+ WFIFOSET(fd, 14);
return 0;
}
@@ -1521,15 +1521,15 @@ static int mapif_parse_CreatePet(int fd)
account_id = RFIFOL(fd, 2);
pet = inter_pet->create(account_id,
RFIFOL(fd, 6),
- RFIFOW(fd, 10),
- RFIFOW(fd, 12),
+ RFIFOL(fd, 10),
RFIFOL(fd, 14),
RFIFOL(fd, 18),
- RFIFOW(fd, 22),
- RFIFOW(fd, 24),
- RFIFOB(fd, 26),
- RFIFOB(fd, 27),
- RFIFOP(fd, 28));
+ RFIFOL(fd, 22),
+ RFIFOW(fd, 26),
+ RFIFOW(fd, 28),
+ RFIFOB(fd, 30),
+ RFIFOB(fd, 31),
+ RFIFOP(fd, 32));
if (pet != NULL)
mapif->pet_created(fd, account_id, pet);
@@ -1744,10 +1744,13 @@ static void mapif_rodex_sendhasnew(int fd, int char_id, bool has_new)
*------------------------------------------*/
static void mapif_parse_rodex_updatemail(int fd)
{
- int64 mail_id = RFIFOL(fd, 2);
- int8 flag = RFIFOB(fd, 10);
+ int account_id = RFIFOL(fd, 2);
+ int char_id = RFIFOL(fd, 6);
+ int64 mail_id = RFIFOQ(fd, 10);
+ uint8 opentype = RFIFOB(fd, 18);
+ int8 flag = RFIFOB(fd, 19);
- inter_rodex->updatemail(mail_id, flag);
+ inter_rodex->updatemail(fd, account_id, char_id, mail_id, opentype, flag);
}
/*==========================================
@@ -1789,7 +1792,7 @@ static void mapif_parse_rodex_checkname(int fd)
int reqchar_id = RFIFOL(fd, 2);
char name[NAME_LENGTH];
int target_char_id, target_level;
- short target_class;
+ int target_class;
safestrncpy(name, RFIFOP(fd, 6), NAME_LENGTH);
@@ -1799,20 +1802,20 @@ static void mapif_parse_rodex_checkname(int fd)
mapif->rodex_checkname(fd, reqchar_id, 0, 0, 0, name);
}
-static void mapif_rodex_checkname(int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char *name)
+static void mapif_rodex_checkname(int fd, int reqchar_id, int target_char_id, int target_class, int target_level, char *name)
{
nullpo_retv(name);
Assert_retv(reqchar_id > 0);
Assert_retv(target_char_id >= 0);
- WFIFOHEAD(fd, 16 + NAME_LENGTH);
+ WFIFOHEAD(fd, 18 + NAME_LENGTH);
WFIFOW(fd, 0) = 0x3898;
WFIFOL(fd, 2) = reqchar_id;
WFIFOL(fd, 6) = target_char_id;
- WFIFOW(fd, 10) = target_class;
- WFIFOL(fd, 12) = target_level;
- safestrncpy(WFIFOP(fd, 16), name, NAME_LENGTH);
- WFIFOSET(fd, 16 + NAME_LENGTH);
+ WFIFOL(fd, 10) = target_class;
+ WFIFOL(fd, 14) = target_level;
+ safestrncpy(WFIFOP(fd, 18), name, NAME_LENGTH);
+ WFIFOSET(fd, 18 + NAME_LENGTH);
}
static int mapif_load_guild_storage(int fd, int account_id, int guild_id, char flag)
@@ -2461,6 +2464,29 @@ static void mapif_achievement_save(int char_id, struct char_achievements *p)
inter_achievement->tosql(char_id, cp, p);
}
+static void mapif_rodex_getzenyack(int fd, int char_id, int64 mail_id, uint8 opentype, int64 zeny)
+{
+ WFIFOHEAD(fd, 23);
+ WFIFOW(fd, 0) = 0x3899;
+ WFIFOL(fd, 2) = char_id;
+ WFIFOQ(fd, 6) = zeny;
+ WFIFOQ(fd, 14) = mail_id;
+ WFIFOB(fd, 22) = opentype;
+ WFIFOSET(fd, 23);
+}
+
+static void mapif_rodex_getitemsack(int fd, int char_id, int64 mail_id, uint8 opentype, int count, const struct rodex_item *items)
+{
+ WFIFOHEAD(fd, 15 + sizeof(struct rodex_item) * RODEX_MAX_ITEM);
+ WFIFOW(fd, 0) = 0x389a;
+ WFIFOL(fd, 2) = char_id;
+ WFIFOQ(fd, 6) = mail_id;
+ WFIFOB(fd, 14) = opentype;
+ WFIFOB(fd, 15) = count;
+ memcpy(WFIFOP(fd, 16), items, sizeof(struct rodex_item) * RODEX_MAX_ITEM);
+ WFIFOSET(fd, 16 + sizeof(struct rodex_item) * RODEX_MAX_ITEM);
+}
+
void mapif_defaults(void)
{
mapif = &mapif_s;
@@ -2605,6 +2631,8 @@ void mapif_defaults(void)
mapif->rodex_send = mapif_rodex_send;
mapif->parse_rodex_checkname = mapif_parse_rodex_checkname;
mapif->rodex_checkname = mapif_rodex_checkname;
+ mapif->rodex_getzenyack = mapif_rodex_getzenyack;
+ mapif->rodex_getitemsack = mapif_rodex_getitemsack;
mapif->load_guild_storage = mapif_load_guild_storage;
mapif->save_guild_storage_ack = mapif_save_guild_storage_ack;
mapif->parse_LoadGuildStorage = mapif_parse_LoadGuildStorage;
diff --git a/src/char/mapif.h b/src/char/mapif.h
index bfdefe4ea..71a41f94c 100644
--- a/src/char/mapif.h
+++ b/src/char/mapif.h
@@ -24,6 +24,7 @@
#include "common/mmo.h"
struct WisData;
+struct rodex_item;
/**
* mapif interface
@@ -84,7 +85,7 @@ struct mapif_interface {
int (*parse_GuildInfo) (int fd, int guild_id);
int (*parse_GuildAddMember) (int fd, int guild_id, const struct guild_member *m);
int (*parse_GuildLeave) (int fd, int guild_id, int account_id, int char_id, int flag, const char *mes);
- int (*parse_GuildChangeMemberInfoShort) (int fd, int guild_id, int account_id, int char_id, int online, int lv, int16 class);
+ int (*parse_GuildChangeMemberInfoShort) (int fd, int guild_id, int account_id, int char_id, int online, int lv, int class);
int (*parse_BreakGuild) (int fd, int guild_id);
int (*parse_GuildMessage) (int fd, int guild_id, int account_id, const char *mes, int len);
int (*parse_GuildBasicInfoChange) (int fd, int guild_id, int type, const void *data, int len);
@@ -167,7 +168,9 @@ struct mapif_interface {
void (*parse_rodex_send) (int fd);
void (*rodex_send) (int fd, int sender_id, int receiver_id, int receiver_accountid, bool result);
void (*parse_rodex_checkname) (int fd);
- void (*rodex_checkname) (int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char *name);
+ void (*rodex_checkname) (int fd, int reqchar_id, int target_char_id, int target_class, int target_level, char *name);
+ void (*rodex_getzenyack) (int fd, int char_id, int64 mail_id, uint8 opentype, int64 zeny);
+ void (*rodex_getitemsack) (int fd, int char_id, int64 mail_id, uint8 opentype, int count, const struct rodex_item *items);
int (*load_guild_storage) (int fd, int account_id, int guild_id, char flag);
int (*save_guild_storage_ack) (int fd, int account_id, int guild_id, int fail);
int (*parse_LoadGuildStorage) (int fd);
diff --git a/src/char/packets_hc_struct.h b/src/char/packets_hc_struct.h
new file mode 100644
index 000000000..196493cac
--- /dev/null
+++ b/src/char/packets_hc_struct.h
@@ -0,0 +1,45 @@
+/**
+ * This file is part of Hercules.
+ * http://herc.ws - http://github.com/HerculesWS/Hercules
+ *
+ * Copyright (C) 2016-2018 Hercules Dev Team
+ *
+ * Hercules is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef CHAR_PACKETS_HC_STRUCT_H
+#define CHAR_PACKETS_HC_STRUCT_H
+
+#include "common/hercules.h"
+#include "common/mmo.h"
+#include "common/packetsstatic_len.h"
+
+/* Packets Structs */
+#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
+#pragma pack(push, 1)
+#endif // not NetBSD < 6 / Solaris
+
+#if PACKETVER_MAIN_NUM >= 20130522 || PACKETVER_RE_NUM >= 20130327 || defined(PACKETVER_ZERO)
+struct PACKET_HC_ACK_CHARINFO_PER_PAGE {
+ int16 packetId;
+ int16 packetLen;
+ // chars list[]
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(HC_ACK_CHARINFO_PER_PAGE, 0x099d);
+#endif
+
+#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
+#pragma pack(pop)
+#endif // not NetBSD < 6 / Solaris
+
+#endif // CHAR_PACKETS_HC_STRUCT_H