diff --git a/src/char/char.c b/src/char/char.c index 93078f8ee72..487bec76343 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -287,11 +287,17 @@ void char_set_char_offline(int char_id, int account_id) } else { - struct mmo_charstatus* cp = (struct mmo_charstatus*)idb_get(chr->char_db_,char_id); + struct mmo_charstatus* cp = (struct mmo_charstatus*) idb_get(chr->char_db_,char_id); + struct storage_data *stor = (struct storage_data *) idb_get(inter_storage->account_storage, account_id); + inter_guild->CharOffline(char_id, cp?cp->guild_id:-1); + if (cp) idb_remove(chr->char_db_,char_id); + if (stor) /* Remove inter-storage data. */ + idb_remove(inter_storage->account_storage, account_id); + if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online`='0' WHERE `char_id`='%d' LIMIT 1", char_db, char_id) ) Sql_ShowDebug(inter->sql_handle); } @@ -442,14 +448,6 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p) errors++; } - //map storage data - if( memcmp(p->storage.items, cp->storage.items, sizeof(p->storage.items)) ) { - if (!chr->memitemdata_to_sql(p->storage.items, MAX_STORAGE, p->account_id, TABLE_STORAGE)) - strcat(save_status, " storage"); - else - errors++; - } - if ( (p->base_exp != cp->base_exp) || (p->base_level != cp->base_level) || (p->job_level != cp->job_level) || (p->job_exp != cp->job_exp) || @@ -1284,10 +1282,6 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every strcat(t_msg, " cart"); - //read storage - inter_storage->fromsql(p->account_id, &p->storage); - strcat(t_msg, " storage"); - //read skill //`skill` (`char_id`, `id`, `lv`) memset(&tmp_skill, 0, sizeof(tmp_skill)); diff --git a/src/char/int_storage.c b/src/char/int_storage.c index b78ad9f0e2e..eb1bd5e9fac 100644 --- a/src/char/int_storage.c +++ b/src/char/int_storage.c @@ -41,24 +41,145 @@ struct inter_storage_interface inter_storage_s; struct inter_storage_interface *inter_storage; /// Save storage data to sql -int inter_storage_tosql(int account_id, struct storage_data* p) +int inter_storage_tosql(int account_id, struct storage_data *cp, const struct storage_data *p) { + int i = 0, j = 0; + bool updated_p[MAX_STORAGE] = { false }; + int delete[MAX_STORAGE] = { 0 }; + int total_deletes = 0, total_updates = 0, total_inserts = 0; + int total_changes = 0; + StringBuf buf; + + nullpo_ret(cp); nullpo_ret(p); - chr->memitemdata_to_sql(p->items, MAX_STORAGE, account_id, TABLE_STORAGE); - return 0; + + StrBuf->Init(&buf); + + /** + * Compare and update items, if any. + */ + for (i = 0; i < VECTOR_LENGTH(cp->item); i++) { + struct item *cp_it = &VECTOR_INDEX(cp->item, i); + struct item *p_it = NULL; + + ARR_FIND(0, VECTOR_LENGTH(p->item), j, + (p_it = &VECTOR_INDEX(p->item, j)) != NULL + && cp_it->nameid == p_it->nameid + && cp_it->unique_id == p_it->unique_id + && memcmp(p_it->card, cp_it->card, sizeof(short) * MAX_SLOTS) == 0 + && memcmp(p_it->option, cp_it->option, 5 * MAX_ITEM_OPTIONS) == 0); + + if (j < VECTOR_LENGTH(p->item)) { + int k = 0; + if (memcmp(cp_it, p_it, sizeof(struct item)) != 0) { + if (total_updates == 0) { + StrBuf->Printf(&buf, "REPLACE INTO `%s` (`id`, `account_id`, `amount`, `equip`, `identify`, `refine`, `attribute`", storage_db); + for (k = 0; k < MAX_SLOTS; k++) + StrBuf->Printf(&buf, ", `card%d`", k); + for (k = 0; k < MAX_ITEM_OPTIONS; k++) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", k, k); + StrBuf->Printf(&buf, ", `expire_time`, `bound`, `unique_id`) VALUES"); + } + + StrBuf->Printf(&buf, "('%d', '%d', '%d', '%u', '%d', '%d', '%d'", + cp_it->id, account_id, p_it->amount, p_it->equip, p_it->identify, p_it->refine, p_it->attribute); + for (k = 0; k < MAX_SLOTS; k++) + StrBuf->Printf(&buf, "'%d'", p_it->card[k]); + for (k = 0; k < MAX_ITEM_OPTIONS; ++k) + StrBuf->Printf(&buf, "'%d', '%d'", p_it->option[k].index, p_it->option[k].value); + StrBuf->Printf(&buf, ", '%u', '%d', '%"PRIu64"')%s", p_it->expire_time, p_it->bound, p_it->unique_id, total_updates > 0 ? ", " : ""); + + total_updates++; + updated_p[j] = true; + } + } else { // Does not exist, so set for deletion. + delete[total_deletes++] = cp_it->id; + } + } + + if (total_updates > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) + Sql_ShowDebug(inter->sql_handle); + + /** + * Handle deletions, if any. + */ + if (total_deletes > 0) { + StrBuf->Clear(&buf); + StrBuf->Printf(&buf, "DELETE FROM `%s` WHERE `id` IN (", storage_db); + for (i = 0; i < total_deletes; i++) { + StrBuf->Printf(&buf, ", '%d'", delete[i]); + } + StrBuf->Printf(&buf, ");"); + + if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) + Sql_ShowDebug(inter->sql_handle); + } + + /** + * Check for new items. + */ + for (i = 0; i < VECTOR_LENGTH(p->item); i++) { + struct item *p_it = &VECTOR_INDEX(p->item, i); + + if (updated_p[i] == true) + continue; // Item already dealt with, lets continue. + + // Store the remaining items. + if (total_inserts == 0) { + StrBuf->Clear(&buf); + StrBuf->Printf(&buf, "INSERT INTO `%s`(`account_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", storage_db); + for (j = 0; j < MAX_SLOTS; ++j) + StrBuf->Printf(&buf, ", `card%d`", j); + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) + StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j); + StrBuf->AppendStr(&buf, ") VALUES "); + } + + StrBuf->Printf(&buf, "('%d', '%d', '%d', '%u', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'", + account_id, p_it->nameid, p_it->amount, p_it->equip, p_it->identify, p_it->refine, + p_it->attribute, p_it->expire_time, p_it->bound, p_it->unique_id); + for (j = 0; j < MAX_SLOTS; ++j) + StrBuf->Printf(&buf, ", '%d'", p_it->card[j]); + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) + StrBuf->Printf(&buf, ", '%d', '%d'", p_it->option[j].index, p_it->option[j].value); + StrBuf->Printf(&buf, ")%s", total_inserts > 0 ? ", " : ""); + + total_inserts++; + } + + if (total_inserts > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) + Sql_ShowDebug(inter->sql_handle); + + StrBuf->Destroy(&buf); + + /** + * Sync map-storage with inter-storage + */ + VECTOR_CLEAR(cp->item); + VECTOR_PUSHARRAY(cp->item, VECTOR_DATA(p->item), VECTOR_LENGTH(p->item)); + + *cp = *p; + + total_changes = total_inserts + total_updates + total_deletes; + + ShowInfo("storage save complete - id: %d (total: %d)\n", account_id, total_changes); + + return total_changes; } /// Load storage data to mem -int inter_storage_fromsql(int account_id, struct storage_data* p) +int inter_storage_fromsql(int account_id, struct storage_data *p) { StringBuf buf; char* data; int i; int j; + int num_rows = 0; nullpo_ret(p); - memset(p, 0, sizeof(struct storage_data)); //clean up memory - p->storage_amount = 0; + + if (VECTOR_LENGTH(p->item) > 0) + VECTOR_CLEAR(p->item); // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} StrBuf->Init(&buf); @@ -74,36 +195,46 @@ int inter_storage_fromsql(int account_id, struct storage_data* p) StrBuf->Destroy(&buf); - for (i = 0; i < MAX_STORAGE && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i) { - struct item *item = &p->items[i]; - SQL->GetData(inter->sql_handle, 0, &data, NULL); item->id = atoi(data); - SQL->GetData(inter->sql_handle, 1, &data, NULL); item->nameid = atoi(data); - SQL->GetData(inter->sql_handle, 2, &data, NULL); item->amount = atoi(data); - SQL->GetData(inter->sql_handle, 3, &data, NULL); item->equip = atoi(data); - SQL->GetData(inter->sql_handle, 4, &data, NULL); item->identify = atoi(data); - SQL->GetData(inter->sql_handle, 5, &data, NULL); item->refine = atoi(data); - SQL->GetData(inter->sql_handle, 6, &data, NULL); item->attribute = atoi(data); - SQL->GetData(inter->sql_handle, 7, &data, NULL); item->expire_time = (unsigned int)atoi(data); - SQL->GetData(inter->sql_handle, 8, &data, NULL); item->bound = atoi(data); - SQL->GetData(inter->sql_handle, 9, &data, NULL); item->unique_id = strtoull(data, NULL, 10); - /* Card Slots */ - for (j = 0; j < MAX_SLOTS; ++j) { - SQL->GetData(inter->sql_handle, 10 + j, &data, NULL); - item->card[j] = atoi(data); - } - /* Item Options */ - for (j = 0; j < MAX_ITEM_OPTIONS; ++j) { - SQL->GetData(inter->sql_handle, 10 + MAX_SLOTS + j * 2, &data, NULL); - item->option[j].index = atoi(data); - SQL->GetData(inter->sql_handle, 11 + MAX_SLOTS + j * 2, &data, NULL); - item->option[j].value = atoi(data); + if ((num_rows = (int)SQL->NumRows(inter->sql_handle)) != 0) { + + VECTOR_ENSURE(p->item, num_rows > MAX_STORAGE ? MAX_STORAGE : num_rows, 1); + + for (i = 0; i < MAX_STORAGE && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i) { + struct item item = { 0 }; + SQL->GetData(inter->sql_handle, 0, &data, NULL); item.id = atoi(data); + SQL->GetData(inter->sql_handle, 1, &data, NULL); item.nameid = atoi(data); + SQL->GetData(inter->sql_handle, 2, &data, NULL); item.amount = atoi(data); + SQL->GetData(inter->sql_handle, 3, &data, NULL); item.equip = atoi(data); + SQL->GetData(inter->sql_handle, 4, &data, NULL); item.identify = atoi(data); + SQL->GetData(inter->sql_handle, 5, &data, NULL); item.refine = atoi(data); + SQL->GetData(inter->sql_handle, 6, &data, NULL); item.attribute = atoi(data); + SQL->GetData(inter->sql_handle, 7, &data, NULL); item.expire_time = (unsigned int)atoi(data); + SQL->GetData(inter->sql_handle, 8, &data, NULL); item.bound = atoi(data); + SQL->GetData(inter->sql_handle, 9, &data, NULL); item.unique_id = strtoull(data, NULL, 10); + + /* Card Slots */ + for (j = 0; j < MAX_SLOTS; ++j) { + SQL->GetData(inter->sql_handle, 10 + j, &data, NULL); + item.card[j] = atoi(data); + } + + /* Item Options */ + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) { + SQL->GetData(inter->sql_handle, 10 + MAX_SLOTS + j * 2, &data, NULL); + item.option[j].index = atoi(data); + SQL->GetData(inter->sql_handle, 11 + MAX_SLOTS + j * 2, &data, NULL); + item.option[j].value = atoi(data); + } + + VECTOR_PUSH(p->item, item); } } - p->storage_amount = i; + SQL->FreeResult(inter->sql_handle); - ShowInfo("storage load complete from DB - id: %d (total: %d)\n", account_id, p->storage_amount); - return 1; + ShowInfo("storage load complete from DB - id: %d (total: %d)\n", account_id, VECTOR_LENGTH(p->item)); + + return VECTOR_LENGTH(p->item); } /// Save guild_storage data to sql @@ -174,15 +305,37 @@ int inter_storage_guild_storage_fromsql(int guild_id, struct guild_storage* p) return 0; } +/** + * Ensures storage data entity for a character. + * @see DBCreateData + */ +static struct DBData inter_storage_ensure_account_storage(union DBKey key, va_list args) +{ + struct storage_data *stor = NULL; + + CREATE(stor, struct storage_data, 1); + + stor->save = false; + stor->aggregate = 0; + + VECTOR_INIT(stor->item); + + return DB->ptr2data(stor); +} + //--------------------------------------------------------- // storage data initialize int inter_storage_sql_init(void) { + inter_storage->account_storage = idb_alloc(DB_OPT_RELEASE_DATA); + return 1; } // storage data finalize void inter_storage_sql_final(void) { + inter_storage->account_storage->destroy(inter_storage->account_storage, NULL); + return; } @@ -240,13 +393,126 @@ int mapif_save_guild_storage_ack(int fd, int account_id, int guild_id, int fail) return 0; } +//========================================================= +// Account Storage //--------------------------------------------------------- -// packet from map server +/** + * Parses account storage load request from map server. + * @packet 0x3010 [in] .L + * @param fd [in] file/socket descriptor + * @return 1 on success, 0 on failure. + */ +int mapif_parse_AccountStorageLoad(int fd) +{ + int account_id = RFIFOL(fd, 2); + + Assert_ret(fd > 0); + Assert_ret(account_id > 0); + + mapif->account_storage_load(fd, account_id); + + return 1; +} + +/** + * Loads the account storage and send to the map server. + * @packet 0x3805 [out] .L .P + * @param fd [in] file/socket descriptor. + * @param account_id [in] account id of the session. + * @return 1 on success, 0 on failure. + */ +int mapif_account_storage_load(int fd, int account_id) +{ + struct storage_data *stor = NULL; + int count = 0, i = 0, len = 0; + + Assert_ret(account_id > 0); + + stor = (struct storage_data *) idb_ensure(inter_storage->account_storage, account_id, inter_storage->ensure_account_storage); + + count = inter_storage->fromsql(account_id, stor); + + len = 8 + count * sizeof(struct item); + + WFIFOHEAD(fd, len); + WFIFOW(fd, 0) = 0x3805; + WFIFOW(fd, 2) = (uint16) len; + WFIFOL(fd, 4) = account_id; + for (i = 0; i < count; i++) + memcpy(WFIFOP(fd, 8 + i * sizeof(struct item)), &VECTOR_INDEX(stor->item, i), sizeof(struct item)); + WFIFOSET(fd, len); + + return 1; +} +/** + * Parses an account storage save request from the map server. + * @packet 0x3011 [in] .W .L .P + * @param fd [in] file/socket descriptor. + * @return 1 on success, 0 on failure. + */ +int mapif_parse_AccountStorageSave(int fd) +{ + int payload_size = RFIFOW(fd, 2) - 8, account_id = RFIFOL(fd, 4); + int i = 0, count = 0; + struct storage_data *cp_stor = NULL, p_stor = { 0 }; + + Assert_ret(fd > 0); + Assert_ret(account_id > 0); + + cp_stor = (struct storage_data *) idb_ensure(inter_storage->account_storage, account_id, inter_storage->ensure_account_storage); + + count = payload_size/sizeof(struct item); + + VECTOR_INIT(p_stor.item); + + if (count > 0) { + VECTOR_ENSURE(p_stor.item, count, 1); + + for (i = 0; i < count; i++) { + const struct item *it = RFIFOP(fd, 8 + i * sizeof(struct item)); + + VECTOR_PUSH(p_stor.item, *it); + } + + p_stor.aggregate = count; + + inter_storage->tosql(account_id, cp_stor, &p_stor); + + VECTOR_CLEAR(p_stor.item); + } + + mapif->sAccountStorageSaveAck(fd, account_id, true); + + return 1; +} + +/** + * Sends an acknowledgement for the save + * status of the account storage. + * @packet 0x3808 [out] .L .B + * @param fd [in] File/Socket Descriptor. + * @param account_id [in] Account ID of the storage in question. + * @param flag [in] Save flag, true for success and false for failure. + */ +void mapif_send_AccountStorageSaveAck(int fd, int account_id, bool flag) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x3808; + WFIFOL(fd, 2) = account_id; + WFIFOB(fd, 6) = flag ? 1 : 0; + WFIFOSET(fd, 7); +} + +//========================================================= +// Guild Storage +//--------------------------------------------------------- int mapif_parse_LoadGuildStorage(int fd) { RFIFOHEAD(fd); + mapif->load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6),1); + return 0; } @@ -274,6 +540,7 @@ int mapif_parse_SaveGuildStorage(int fd) SQL->FreeResult(inter->sql_handle); } mapif->save_guild_storage_ack(fd, RFIFOL(fd,4), guild_id, 1); + return 0; } @@ -487,6 +754,8 @@ int inter_storage_parse_frommap(int fd) { RFIFOHEAD(fd); switch(RFIFOW(fd,0)){ + case 0x3010: mapif->pAccountStorageLoad(fd); break; + case 0x3011: mapif->pAccountStorageSave(fd); break; case 0x3018: mapif->parse_LoadGuildStorage(fd); break; case 0x3019: mapif->parse_SaveGuildStorage(fd); break; #ifdef GP_BOUND_ITEMS @@ -502,6 +771,7 @@ void inter_storage_defaults(void) { inter_storage = &inter_storage_s; + inter_storage->ensure_account_storage = inter_storage_ensure_account_storage; inter_storage->tosql = inter_storage_tosql; inter_storage->fromsql = inter_storage_fromsql; inter_storage->guild_storage_tosql = inter_storage_guild_storage_tosql; diff --git a/src/char/int_storage.h b/src/char/int_storage.h index 8c6341e8580..57ed6f7ba29 100644 --- a/src/char/int_storage.h +++ b/src/char/int_storage.h @@ -21,6 +21,7 @@ #ifndef CHAR_INT_STORAGE_H #define CHAR_INT_STORAGE_H +#include "common/db.h" #include "common/hercules.h" struct storage_data; @@ -30,8 +31,13 @@ struct guild_storage; * inter_storage interface **/ struct inter_storage_interface { - int (*tosql) (int account_id, struct storage_data* p); - int (*fromsql) (int account_id, struct storage_data* p); + /* */ + struct DBMap *account_storage; + /* */ + /* */ + struct DBData (*ensure_account_storage) (union DBKey key, va_list args); + int (*tosql) (int account_id, struct storage_data *cp, const struct storage_data *p); + int (*fromsql) (int account_id, struct storage_data *p); int (*guild_storage_tosql) (int guild_id, const struct guild_storage *p); int (*guild_storage_fromsql) (int guild_id, struct guild_storage* p); int (*sql_init) (void); diff --git a/src/char/inter.c b/src/char/inter.c index d076bb6574e..cfbc0fd4db2 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -68,7 +68,7 @@ int party_share_level = 10; // recv. packet list int inter_recv_packet_length[] = { -1,-1, 7,-1, -1,13,36, (2 + 4 + 4 + 4 + NAME_LENGTH), 0, 0, 0, 0, 0, 0, 0, 0, // 3000- - 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- + 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- Account Storage [Smokexyz] -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- -1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- diff --git a/src/char/mapif.c b/src/char/mapif.c index 1dafb79c325..5fff96ba886 100644 --- a/src/char/mapif.c +++ b/src/char/mapif.c @@ -186,6 +186,10 @@ int mapif_load_guild_storage(int fd,int account_id,int guild_id, char flag); int mapif_save_guild_storage_ack(int fd, int account_id, int guild_id, int fail); int mapif_parse_LoadGuildStorage(int fd); int mapif_parse_SaveGuildStorage(int fd); +int mapif_account_storage_load(int fd, int account_id); +int mapif_parse_AccountStorageLoad(int fd); +int mapif_parse_AccountStorageSave(int fd); +void mapif_send_AccountStorageSaveAck(int fd, int account_id, bool save); int mapif_itembound_ack(int fd, int aid, int guild_id); int mapif_parse_ItemBoundRetrieve_sub(int fd); void mapif_parse_ItemBoundRetrieve(int fd); @@ -363,6 +367,10 @@ void mapif_defaults(void) { mapif->save_guild_storage_ack = mapif_save_guild_storage_ack; mapif->parse_LoadGuildStorage = mapif_parse_LoadGuildStorage; mapif->parse_SaveGuildStorage = mapif_parse_SaveGuildStorage; + mapif->pAccountStorageLoad = mapif_parse_AccountStorageLoad; + mapif->pAccountStorageSave = mapif_parse_AccountStorageSave; + mapif->sAccountStorageSaveAck = mapif_send_AccountStorageSaveAck; + mapif->account_storage_load = mapif_account_storage_load; mapif->itembound_ack = mapif_itembound_ack; mapif->parse_ItemBoundRetrieve_sub = mapif_parse_ItemBoundRetrieve_sub; mapif->parse_ItemBoundRetrieve = mapif_parse_ItemBoundRetrieve; diff --git a/src/char/mapif.h b/src/char/mapif.h index ebbddead00c..07fbed6c67a 100644 --- a/src/char/mapif.h +++ b/src/char/mapif.h @@ -180,6 +180,10 @@ struct mapif_interface { int (*save_guild_storage_ack) (int fd, int account_id, int guild_id, int fail); int (*parse_LoadGuildStorage) (int fd); int (*parse_SaveGuildStorage) (int fd); + int (*account_storage_load) (int fd, int account_id); + int (*pAccountStorageLoad) (int fd); + int (*pAccountStorageSave) (int fd); + void (*sAccountStorageSaveAck) (int fd, int account_id, bool save); int (*itembound_ack) (int fd, int aid, int guild_id); int (*parse_ItemBoundRetrieve_sub) (int fd); void (*parse_ItemBoundRetrieve) (int fd); diff --git a/src/common/mmo.h b/src/common/mmo.h index b3069b27c7f..0a71caaa2d4 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -23,6 +23,7 @@ #include "config/core.h" #include "common/cbasetypes.h" +#include "common/db.h" // VECTORS // server->client protocol version // 0 - pre-? @@ -63,7 +64,7 @@ // 20120307 - 2012-03-07aRagexeRE+ - 0x970 #ifndef PACKETVER - #define PACKETVER 20141022 + #define PACKETVER 20151029 #endif // PACKETVER //Uncomment the following line if your client is ragexeRE instead of ragexe (required because of conflicting packets in ragexe vs ragexeRE). @@ -479,8 +480,9 @@ struct status_change_data { }; struct storage_data { - int storage_amount; - struct item items[MAX_STORAGE]; + bool save; //< save flag. + uint32 aggregate; //< total item count. + VECTOR_DECL(struct item) item; //< item vector. }; struct guild_storage { @@ -616,7 +618,6 @@ struct mmo_charstatus { struct point last_point,save_point,memo_point[MAX_MEMOPOINTS]; struct item inventory[MAX_INVENTORY],cart[MAX_CART]; - struct storage_data storage; struct s_skill skill[MAX_SKILL]; struct s_friend friends[MAX_FRIENDS]; //New friend system [Skotlex] diff --git a/src/map/atcommand.c b/src/map/atcommand.c index bf816faa710..de6ffd69264 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -5160,17 +5160,17 @@ ACMD(storeall) ACMD(clearstorage) { - int i, j; + int i; if (sd->state.storage_flag == STORAGE_FLAG_NORMAL) { clif->message(fd, msg_fd(fd,250)); return false; } - j = sd->status.storage.storage_amount; - for (i = 0; i < j; ++i) { - storage->delitem(sd, i, sd->status.storage.items[i].amount); + for (i = 0; i < VECTOR_LENGTH(sd->storage.item); ++i) { + storage->delitem(sd, i, VECTOR_INDEX(sd->storage.item, i).amount); } + storage->close(sd); clif->message(fd, msg_fd(fd,1394)); // Your storage was cleaned. @@ -8027,8 +8027,8 @@ ACMD(itemlist) if( strcmpi(info->command, "storagelist") == 0 ) { location = "storage"; - items = sd->status.storage.items; - size = MAX_STORAGE; + items = VECTOR_DATA(sd->storage.item); + size = VECTOR_LENGTH(sd->storage.item); } else if( strcmpi(info->command, "cartlist") == 0 ) { location = "cart"; items = sd->status.cart; @@ -8049,7 +8049,7 @@ ACMD(itemlist) const struct item* it = &items[i]; struct item_data* itd; - if( it->nameid == 0 || (itd = itemdb->exists(it->nameid)) == NULL ) + if (it->nameid == 0 || (itd = itemdb->exists(it->nameid)) == NULL) continue; counter += it->amount; diff --git a/src/map/chrif.c b/src/map/chrif.c index bf613b02952..585c0c405b0 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -324,6 +324,8 @@ bool chrif_save(struct map_session_data *sd, int flag) { if( sd->save_quest ) intif->quest_save(sd); + intif->send_account_storage(sd); + return true; } diff --git a/src/map/clif.c b/src/map/clif.c index f1400a5d482..610e834c717 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -4187,12 +4187,13 @@ void clif_storageitemremoved(struct map_session_data* sd, int index, int amount) nullpo_retv(sd); - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xf6)); - WFIFOW(fd,0)=0xf6; // Storage item removed - WFIFOW(fd,2)=index+1; - WFIFOL(fd,4)=amount; - WFIFOSET(fd,packet_len(0xf6)); + fd = sd->fd; + + WFIFOHEAD(fd, packet_len(0xf6)); + WFIFOW(fd, 0) = 0xf6; // Storage item removed + WFIFOW(fd, 2) = index + 1; + WFIFOL(fd, 4) = amount; + WFIFOSET(fd, packet_len(0xf6)); } /// Closes storage (ZC_CLOSE_STORE). @@ -8429,9 +8430,11 @@ void clif_refresh_storagewindow(struct map_session_data *sd) nullpo_retv(sd); // Notify the client that the storage is open if (sd->state.storage_flag == STORAGE_FLAG_NORMAL) { - storage->sortitem(sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); - clif->storagelist(sd, sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); - clif->updatestorageamount(sd, sd->status.storage.storage_amount, MAX_STORAGE); + if (sd->storage.aggregate > 0) { + storage->sortitem(VECTOR_DATA(sd->storage.item), VECTOR_LENGTH(sd->storage.item)); + clif->storagelist(sd, VECTOR_DATA(sd->storage.item), VECTOR_LENGTH(sd->storage.item)); + } + clif->updatestorageamount(sd, sd->storage.aggregate, MAX_STORAGE); } // Notify the client that the gstorage is open otherwise it will // remain locked forever and nobody will be able to access it diff --git a/src/map/intif.c b/src/map/intif.c index 10a9ea8a9d3..f64d6c6b763 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -439,6 +439,131 @@ int intif_request_registry(struct map_session_data *sd, int flag) return 0; } +//================================================================= +// Account Storage +//----------------------------------------------------------------- + +/** + * Request the inter-server for a character's storage data. + * @packet 0x3010 [out] .L + * @param sd [in] pointer to session data. + */ +void intif_request_account_storage(const struct map_session_data *sd) +{ + nullpo_retv(sd); + + /* Check for character server availability */ + if (intif->CheckForCharServer()) + return; + + WFIFOHEAD(inter_fd, 6); + WFIFOW(inter_fd, 0) = 0x3010; + WFIFOL(inter_fd, 2) = sd->status.account_id; + WFIFOSET(inter_fd, 6); +} + +/** + * Parse the reception of account storage from the inter-server. + * @packet 0x3805 [in] .W .L .P + * @param fd [in] file/socket descriptor. + */ +void intif_parse_account_storage(int fd) +{ + int account_id = 0, payload_size = 0, storage_count = 0; + int i = 0; + struct map_session_data *sd = NULL; + + Assert_retv(fd > 0); + + payload_size = RFIFOW(fd, 2) - 8; + + if ((account_id = RFIFOL(fd, 4)) == 0 || (sd = map->id2sd(account_id)) == NULL) { + ShowError("intif_parse_account_storage: Session pointer was null for account id %d!\n", account_id); + return; + } + + if (sd->storage_received == true) { + ShowError("intif_parse_account_storage: Multiple calls from the inter-server received.\n"); + return; + } + + storage_count = (payload_size/sizeof(struct item)); + + VECTOR_ENSURE(sd->storage.item, storage_count, 1); + + sd->storage.aggregate = storage_count; //< Total items in storage. + + for (i = 0; i < storage_count; i++) { + const struct item *it = RFIFOP(fd, 8 + i * sizeof(struct item)); + VECTOR_PUSH(sd->storage.item, *it); + } + + sd->storage_received = true; //< Mark the storage as received. + sd->storage.save = false; //< Initialize the save flag as false. +} + +/** + * Send account storage information for saving. + * @packet 0x3011 [out] .W .L .P + * @param sd [in] pointer to session data. + */ +void intif_send_account_storage(const struct map_session_data *sd) +{ + int len = 0, i = 0, total = 0; + + if (intif->CheckForCharServer()) + return; + + if (sd->storage.save == false) + return; + + len = 8 + VECTOR_LENGTH(sd->storage.item) * sizeof(struct item); + + WFIFOHEAD(inter_fd, len); + WFIFOW(inter_fd, 0) = 0x3011; + WFIFOW(inter_fd, 2) = (uint16) len; + WFIFOL(inter_fd, 4) = sd->status.account_id; + + for (i = 0, total = 0; i < VECTOR_LENGTH(sd->storage.item) && total < MAX_STORAGE; i++) { + if (VECTOR_INDEX(sd->storage.item, i).nameid == 0) + continue; + + memcpy(WFIFOP(inter_fd, 8 + total * sizeof(struct item)), &VECTOR_INDEX(sd->storage.item, i), sizeof(struct item)); + total++; //< total # of items counter. + } + + WFIFOSET(inter_fd, len); +} + +/** + * Parse acknowledgement packet for the saving of an account's storage. + * @packet 0x3808 [in] .L .B + * @param fd [in] file/socket descriptor. + */ +void intif_parse_account_storage_save_ack(int fd) +{ + int account_id = RFIFOL(fd, 2); + uint8 saved = RFIFOB(fd, 6); + struct map_session_data *sd = NULL; + + Assert_retv(account_id > 0); + Assert_retv(fd > 0); + + if ((sd = map->id2sd(account_id)) == NULL) + return; //< character is most probably offline. + + if (saved == 0) { + ShowError("intif_parse_account_storage_save_ack: Storage has not been saved! (AID: %d)\n", account_id); + return; + } + + sd->storage.save = false; //< Storage has been saved. +} + +//================================================================= +// Guild Storage +//----------------------------------------------------------------- + int intif_request_guild_storage(int account_id,int guild_id) { if (intif->CheckForCharServer()) @@ -2306,8 +2431,10 @@ int intif_parse(int fd) case 0x3802: intif->pWisEnd(fd); break; case 0x3803: intif->pWisToGM(fd); break; case 0x3804: intif->pRegisters(fd); break; + case 0x3805: intif->pAccountStorage(fd); break; case 0x3806: intif->pChangeNameOk(fd); break; case 0x3807: intif->pMessageToFD(fd); break; + case 0x3808: intif->pAccountStorageSaveAck(fd); break; case 0x3818: intif->pLoadGuildStorage(fd); break; case 0x3819: intif->pSaveGuildStorage(fd); break; case 0x3820: intif->pPartyCreated(fd); break; @@ -2394,7 +2521,7 @@ int intif_parse(int fd) *-------------------------------------*/ void intif_defaults(void) { const int packet_len_table [INTIF_PACKET_LEN_TABLE_SIZE] = { - -1,-1,27,-1, -1, 0,37,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3800-0x380f + -1,-1,27,-1, -1,-1,37,-1, 7, 0, 0, 0, 0, 0, 0, 0, //0x3800-0x380f 0, 0, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, //0x3810 39,-1,15,15, 14,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830 @@ -2421,6 +2548,8 @@ void intif_defaults(void) { intif->wis_message_to_gm = intif_wis_message_to_gm; intif->saveregistry = intif_saveregistry; intif->request_registry = intif_request_registry; + intif->request_account_storage = intif_request_account_storage; + intif->send_account_storage = intif_send_account_storage; intif->request_guild_storage = intif_request_guild_storage; intif->send_guild_storage = intif_send_guild_storage; intif->create_party = intif_create_party; @@ -2497,6 +2626,8 @@ void intif_defaults(void) { intif->pRegisters = intif_parse_Registers; intif->pChangeNameOk = intif_parse_ChangeNameOk; intif->pMessageToFD = intif_parse_MessageToFD; + intif->pAccountStorage = intif_parse_account_storage; + intif->pAccountStorageSaveAck = intif_parse_account_storage_save_ack; intif->pLoadGuildStorage = intif_parse_LoadGuildStorage; intif->pSaveGuildStorage = intif_parse_SaveGuildStorage; intif->pPartyCreated = intif_parse_PartyCreated; diff --git a/src/map/intif.h b/src/map/intif.h index b20acf02972..3d6a524401d 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -66,6 +66,8 @@ struct intif_interface { int (*wis_message_to_gm) (char *Wisp_name, int permission, char *mes); int (*saveregistry) (struct map_session_data *sd); int (*request_registry) (struct map_session_data *sd, int flag); + void (*request_account_storage) (const struct map_session_data *sd); + void (*send_account_storage) (const struct map_session_data *sd); int (*request_guild_storage) (int account_id, int guild_id); int (*send_guild_storage) (int account_id, struct guild_storage *gstor); int (*create_party) (struct party_member *member, const char *name, int item, int item2); @@ -139,8 +141,10 @@ struct intif_interface { int (*pWisToGM_sub) (struct map_session_data* sd,va_list va); void (*pWisToGM) (int fd); void (*pRegisters) (int fd); + void (*pAccountStorage) (int fd); void (*pChangeNameOk) (int fd); void (*pMessageToFD) (int fd); + void (*pAccountStorageSaveAck) (int fd); void (*pLoadGuildStorage) (int fd); void (*pSaveGuildStorage) (int fd); void (*pPartyCreated) (int fd); diff --git a/src/map/pc.c b/src/map/pc.c index f0d050feb84..0ef8a186b2a 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1251,6 +1251,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->bg_queue.type = 0; VECTOR_INIT(sd->script_queues); + VECTOR_INIT(sd->storage.item); //< initialize storage item vector. sd->state.dialog = 0; @@ -1483,6 +1484,9 @@ int pc_reg_received(struct map_session_data *sd) status_calc_pc(sd,SCO_FIRST|SCO_FORCE); chrif->scdata_request(sd->status.account_id, sd->status.char_id); + // Storage Request + intif->request_account_storage(sd); + intif->Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox intif->request_questlog(sd); @@ -10218,21 +10222,23 @@ int pc_checkitem(struct map_session_data *sd) sd->status.cart[i].unique_id = itemdb->unique_id(sd); } - for( i = 0; i < MAX_STORAGE; i++ ) { - id = sd->status.storage.items[i].nameid; + for (i = 0; i < VECTOR_LENGTH(sd->storage.item); i++) { + struct item *it = &VECTOR_INDEX(sd->storage.item, i); - if (!id) + if (it->nameid == 0) continue; - if( id && !itemdb_available(id) ) { - ShowWarning("Removed invalid/disabled item id %d from storage (amount=%d, char_id=%d).\n", id, sd->status.storage.items[i].amount, sd->status.char_id); - storage->delitem(sd, i, sd->status.storage.items[i].amount); + if (!itemdb_available(id)) { + ShowWarning("Removed invalid/disabled item id %d from storage (amount=%d, char_id=%d).\n", id, it->amount, sd->status.char_id); + + storage->delitem(sd, i, it->amount); + storage->close(sd); continue; } - if ( !sd->status.storage.items[i].unique_id && !itemdb->isstackable(id) ) - sd->status.storage.items[i].unique_id = itemdb->unique_id(sd); + if (it->unique_id == 0 && itemdb->isstackable(id) == 0) + it->unique_id = itemdb->unique_id(sd); } if (sd->guild) { diff --git a/src/map/pc.h b/src/map/pc.h index 482e30c41a1..f11d528d283 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -251,7 +251,9 @@ struct map_session_data { unsigned int extra_temp_permissions; /* permissions from @addperm */ struct mmo_charstatus status; - struct item_data* inventory_data[MAX_INVENTORY]; // direct pointers to itemdb entries (faster than doing item_id lookups) + struct item_data *inventory_data[MAX_INVENTORY]; // direct pointers to itemdb entries (faster than doing item_id lookups) + struct storage_data storage; ///< Account Storage + bool storage_received; short equip_index[EQI_MAX]; unsigned int weight,max_weight; int cart_weight,cart_num,cart_weight_max; diff --git a/src/map/storage.c b/src/map/storage.c index aacc7c053c2..2960bb68fba 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -114,9 +114,13 @@ int storage_storageopen(struct map_session_data *sd) } sd->state.storage_flag = STORAGE_FLAG_NORMAL; - storage->sortitem(sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); - clif->storagelist(sd, sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); - clif->updatestorageamount(sd, sd->status.storage.storage_amount, MAX_STORAGE); + + if (sd->storage.aggregate > 0) { + storage->sortitem(VECTOR_DATA(sd->storage.item), VECTOR_LENGTH(sd->storage.item)); + clif->storagelist(sd, VECTOR_DATA(sd->storage.item), VECTOR_LENGTH(sd->storage.item)); + } + + clif->updatestorageamount(sd, sd->storage.aggregate, MAX_STORAGE); return 0; } @@ -148,61 +152,76 @@ int compare_item(struct item *a, struct item *b) *------------------------------------------*/ int storage_additem(struct map_session_data* sd, struct item* item_data, int amount) { - struct storage_data* stor; - struct item_data *data; + struct item_data *data = NULL; + struct item *it = NULL; int i; nullpo_retr(1, sd); nullpo_retr(1, item_data); - stor = &sd->status.storage; - if( item_data->nameid <= 0 || amount <= 0 ) - return 1; + + Assert_retr(1, item_data->nameid > 0); + Assert_retr(1, amount > 0); data = itemdb->search(item_data->nameid); - if( data->stack.storage && amount > data->stack.amount ) - {// item stack limitation + if (data->stack.storage && amount > data->stack.amount) // item stack limitation return 1; - } if (!itemdb_canstore(item_data, pc_get_group_level(sd))) { //Check if item is storable. [Skotlex] - clif->message (sd->fd, msg_sd(sd,264)); // This item cannot be stored. + clif->message (sd->fd, msg_sd(sd, 264)); // This item cannot be stored. return 1; } - if( item_data->bound > IBT_ACCOUNT && !pc_can_give_bound_items(sd) ) { - clif->message(sd->fd, msg_sd(sd,294)); // This bound item cannot be stored there. + if (item_data->bound > IBT_ACCOUNT && !pc_can_give_bound_items(sd)) { + clif->message(sd->fd, msg_sd(sd, 294)); // This bound item cannot be stored there. return 1; } - if( itemdb->isstackable2(data) ) - {//Stackable - for( i = 0; i < MAX_STORAGE; i++ ) - { - if( compare_item(&stor->items[i], item_data) ) - {// existing items found, stack them - if( amount > MAX_AMOUNT - stor->items[i].amount || ( data->stack.storage && amount > data->stack.amount - stor->items[i].amount ) ) + if (itemdb->isstackable2(data)) {//Stackable + for (i = 0; i < VECTOR_LENGTH(sd->storage.item); i++) { + it = &VECTOR_INDEX(sd->storage.item, i); + + if (it->nameid == 0) + continue; + + if (compare_item(it, item_data)) { // existing items found, stack them + if (amount > MAX_AMOUNT - it->amount || (data->stack.storage && amount > data->stack.amount - it->amount)) return 1; - stor->items[i].amount += amount; - clif->storageitemadded(sd,&stor->items[i],i,amount); + it->amount += amount; + + clif->storageitemadded(sd, it, i, amount); + return 0; } } } - // find free slot - ARR_FIND( 0, MAX_STORAGE, i, stor->items[i].nameid == 0 ); - if( i >= MAX_STORAGE ) + // Check if storage exceeds limit. + if (sd->storage.aggregate >= MAX_STORAGE) return 1; - // add item to slot - memcpy(&stor->items[i],item_data,sizeof(stor->items[0])); - stor->storage_amount++; - stor->items[i].amount = amount; - clif->storageitemadded(sd,&stor->items[i],i,amount); - clif->updatestorageamount(sd, stor->storage_amount, MAX_STORAGE); + ARR_FIND(0, VECTOR_LENGTH(sd->storage.item), i, VECTOR_INDEX(sd->storage.item, i).nameid == 0); + + if (i == VECTOR_LENGTH(sd->storage.item)) { + VECTOR_ENSURE(sd->storage.item, 1, 1); + VECTOR_PUSH(sd->storage.item, *item_data); + + it = &VECTOR_INDEX(sd->storage.item, i); + } else { + it = &VECTOR_INDEX(sd->storage.item, i); + } + + *it = *item_data; + + sd->storage.aggregate++; + + clif->storageitemadded(sd, it, i, amount); + + clif->updatestorageamount(sd, sd->storage.aggregate, MAX_STORAGE); + + sd->storage.save = true; //< set a save flag. return 0; } @@ -212,21 +231,29 @@ int storage_additem(struct map_session_data* sd, struct item* item_data, int amo *------------------------------------------*/ int storage_delitem(struct map_session_data* sd, int n, int amount) { + struct item *it = NULL; + nullpo_retr(1, sd); - Assert_retr(1, n >= 0 && n < MAX_STORAGE); - if( sd->status.storage.items[n].nameid == 0 || sd->status.storage.items[n].amount < amount ) - return 1; - sd->status.storage.items[n].amount -= amount; - if( sd->status.storage.items[n].amount == 0 ) - { - memset(&sd->status.storage.items[n],0,sizeof(sd->status.storage.items[0])); - sd->status.storage.storage_amount--; - if( sd->state.storage_flag == STORAGE_FLAG_NORMAL ) - clif->updatestorageamount(sd, sd->status.storage.storage_amount, MAX_STORAGE); + Assert_retr(1, n >= 0 && n < VECTOR_LENGTH(sd->storage.item)); + + nullpo_retr(1, (it = &VECTOR_INDEX(sd->storage.item, n))); + + Assert_retr(1, amount <= it->amount); + + Assert_retr(1, it->nameid > 0); + + it->amount -= amount; + + if (it->amount == 0) { + memset(it, 0, sizeof(struct item)); + clif->updatestorageamount(sd, --sd->storage.aggregate, MAX_STORAGE); } - if( sd->state.storage_flag == STORAGE_FLAG_NORMAL ) - clif->storageitemremoved(sd,n,amount); + + sd->storage.save = true; + + clif->storageitemremoved(sd, n, amount); + return 0; } @@ -237,23 +264,23 @@ int storage_delitem(struct map_session_data* sd, int n, int amount) * 0 : fail * 1 : success *------------------------------------------*/ -int storage_storageadd(struct map_session_data* sd, int index, int amount) +int storage_add_from_inventory(struct map_session_data* sd, int index, int amount) { nullpo_ret(sd); - if( sd->status.storage.storage_amount > MAX_STORAGE ) + if (sd->storage.aggregate > MAX_STORAGE) return 0; // storage full - if( index < 0 || index >= MAX_INVENTORY ) + if (index < 0 || index >= MAX_INVENTORY) return 0; - if( sd->status.inventory[index].nameid <= 0 ) + if (sd->status.inventory[index].nameid <= 0) return 0; // No item on that spot - if( amount < 1 || amount > sd->status.inventory[index].amount ) + if (amount < 1 || amount > sd->status.inventory[index].amount) return 0; - if( storage->additem(sd,&sd->status.inventory[index],amount) == 0 ) + if (storage->additem(sd, &sd->status.inventory[index], amount) == 0) pc->delitem(sd, index, amount, 0, DELITEM_TOSTORAGE, LOG_TYPE_STORAGE); else clif->dropitem(sd, index, 0); @@ -268,24 +295,28 @@ int storage_storageadd(struct map_session_data* sd, int index, int amount) * 0 : fail * 1 : success *------------------------------------------*/ -int storage_storageget(struct map_session_data* sd, int index, int amount) +int storage_add_to_inventory(struct map_session_data* sd, int index, int amount) { int flag; + struct item *it = NULL; nullpo_ret(sd); - if( index < 0 || index >= MAX_STORAGE ) + + if (index < 0 || index >= VECTOR_LENGTH(sd->storage.item)) return 0; - if( sd->status.storage.items[index].nameid <= 0 ) + it = &VECTOR_INDEX(sd->storage.item, index); + + if (it->nameid <= 0) return 0; //Nothing there - if( amount < 1 || amount > sd->status.storage.items[index].amount ) + if (amount < 1 || amount > it->amount) return 0; - if( (flag = pc->additem(sd,&sd->status.storage.items[index],amount,LOG_TYPE_STORAGE)) == 0 ) - storage->delitem(sd,index,amount); + if ((flag = pc->additem(sd, it, amount, LOG_TYPE_STORAGE)) == 0) + storage->delitem(sd, index, amount); else - clif->additem(sd,0,0,flag); + clif->additem(sd, 0, 0, flag); return 1; } @@ -301,19 +332,19 @@ int storage_storageaddfromcart(struct map_session_data* sd, int index, int amoun { nullpo_ret(sd); - if( sd->status.storage.storage_amount > MAX_STORAGE ) + if (sd->storage.aggregate > MAX_STORAGE) return 0; // storage full / storage closed - if( index < 0 || index >= MAX_CART ) + if (index < 0 || index >= MAX_CART) return 0; if( sd->status.cart[index].nameid <= 0 ) return 0; //No item there. - if( amount < 1 || amount > sd->status.cart[index].amount ) + if (amount < 1 || amount > sd->status.cart[index].amount) return 0; - if( storage->additem(sd,&sd->status.cart[index],amount) == 0 ) + if (storage->additem(sd,&sd->status.cart[index],amount) == 0) pc->cart_delitem(sd,index,amount,0,LOG_TYPE_STORAGE); return 1; @@ -329,22 +360,26 @@ int storage_storageaddfromcart(struct map_session_data* sd, int index, int amoun int storage_storagegettocart(struct map_session_data* sd, int index, int amount) { int flag = 0; + struct item *it = NULL; + nullpo_ret(sd); - if( index < 0 || index >= MAX_STORAGE ) + if (index < 0 || index >= VECTOR_LENGTH(sd->storage.item)) return 0; - if( sd->status.storage.items[index].nameid <= 0 ) + it = &VECTOR_INDEX(sd->storage.item, index); + + if (it->nameid <= 0) return 0; //Nothing there. - if( amount < 1 || amount > sd->status.storage.items[index].amount ) + if (amount < 1 || amount > it->amount) return 0; - if( (flag = pc->cart_additem(sd,&sd->status.storage.items[index],amount,LOG_TYPE_STORAGE)) == 0 ) - storage->delitem(sd,index,amount); + if ((flag = pc->cart_additem(sd, it, amount, LOG_TYPE_STORAGE)) == 0) + storage->delitem(sd, index, amount); else { clif->dropitem(sd, index,0); - clif->cart_additem_ack(sd,flag == 1?0x0:0x1); + clif->cart_additem_ack(sd, flag == 1?0x0:0x1); } return 1; @@ -356,12 +391,26 @@ int storage_storagegettocart(struct map_session_data* sd, int index, int amount) *------------------------------------------*/ void storage_storageclose(struct map_session_data* sd) { + int i = 0; + nullpo_retv(sd); + Assert_retv(sd->state.storage_flag == STORAGE_FLAG_NORMAL); + clif->storageclose(sd); - if( map->save_settings&4 ) - chrif->save(sd,0); //Invokes the storage saving as well. + if (map->save_settings & 4) + chrif->save(sd, 0); //Invokes the storage saving as well. + + /* Erase deleted account storage items from memory + * and resize the vector. */ + while (i < VECTOR_LENGTH(sd->storage.item)) { + if (VECTOR_INDEX(sd->storage.item, i).nameid == 0) { + VECTOR_ERASE(sd->storage.item, i); + } else { + i++; + } + } sd->state.storage_flag = STORAGE_FLAG_CLOSED; } @@ -791,8 +840,8 @@ void storage_defaults(void) /* */ storage->delitem = storage_delitem; storage->open = storage_storageopen; - storage->add = storage_storageadd; - storage->get = storage_storageget; + storage->add = storage_add_from_inventory; + storage->get = storage_add_to_inventory; storage->additem = storage_additem; storage->addfromcart = storage_storageaddfromcart; storage->gettocart = storage_storagegettocart; diff --git a/src/map/unit.c b/src/map/unit.c index 79abb8c6afe..bc5800b4286 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -2757,6 +2757,8 @@ int unit_free(struct block_list *bl, clr_type clrtype) sd->instance = NULL; } VECTOR_CLEAR(sd->script_queues); + VECTOR_CLEAR(sd->storage.item); + sd->storage_received = false; if( sd->quest_log != NULL ) { aFree(sd->quest_log); sd->quest_log = NULL; diff --git a/src/plugins/HPMHooking/HPMHooking_char.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_char.Hooks.inc index d2853fcdcba..5068a21bb00 100644 --- a/src/plugins/HPMHooking/HPMHooking_char.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_char.Hooks.inc @@ -8032,15 +8032,15 @@ int HP_inter_quest_parse_frommap(int fd) { return retVal___; } /* inter_storage_interface */ -int HP_inter_storage_tosql(int account_id, struct storage_data *p) { +int HP_inter_storage_tosql(int account_id, struct storage_data *cp, struct storage_data *p) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_inter_storage_tosql_pre > 0) { - int (*preHookFunc) (int *account_id, struct storage_data **p); + int (*preHookFunc) (int *account_id, struct storage_data **cp, struct storage_data **p); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_inter_storage_tosql_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_inter_storage_tosql_pre[hIndex].func; - retVal___ = preHookFunc(&account_id, &p); + retVal___ = preHookFunc(&account_id, &cp, &p); } if (*HPMforce_return) { *HPMforce_return = false; @@ -8048,13 +8048,13 @@ int HP_inter_storage_tosql(int account_id, struct storage_data *p) { } } { - retVal___ = HPMHooks.source.inter_storage.tosql(account_id, p); + retVal___ = HPMHooks.source.inter_storage.tosql(account_id, cp, p); } if (HPMHooks.count.HP_inter_storage_tosql_post > 0) { - int (*postHookFunc) (int retVal___, int account_id, struct storage_data *p); + int (*postHookFunc) (int retVal___, int account_id, struct storage_data *cp, struct storage_data *p); for (hIndex = 0; hIndex < HPMHooks.count.HP_inter_storage_tosql_post; hIndex++) { postHookFunc = HPMHooks.list.HP_inter_storage_tosql_post[hIndex].func; - retVal___ = postHookFunc(retVal___, account_id, p); + retVal___ = postHookFunc(retVal___, account_id, cp, p); } } return retVal___;