Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of Hercules Ultimate Storage System (HUSS) #3330

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1ffe6ad
Implementation of the Hercules Ultimate Storage System
jasonch35 Nov 4, 2024
0a930b5
Add SQL files for main db and upgrades
jasonch35 Nov 4, 2024
d403047
Add data on message tables for the updated atcommands
jasonch35 Nov 4, 2024
520001b
Storage configuration
jasonch35 Nov 4, 2024
95d6f76
Update NPC scripts to comply with new command
jasonch35 Nov 4, 2024
98ad026
Update script and atcommand documentation
jasonch35 Nov 4, 2024
0cb567d
Update HPM Hooks
jasonch35 Nov 4, 2024
4aea3a4
Show warning if storage capacity is over MAX_STORAGE
jasonch35 Nov 5, 2024
c8947dc
Use cart function when storing from cart
jasonch35 Nov 21, 2024
c5e746a
Indexed storage_id column from storage sql table
jasonch35 Dec 1, 2024
41528de
Char-server to ignore storage sizes and hardcoded to MAX_STORAGE instead
jasonch35 Dec 1, 2024
6e485b3
Moved storage_settings struct from mmo to storage header
jasonch35 Dec 1, 2024
1dccefc
Added access modes argument for storage->open
jasonch35 Dec 1, 2024
8baf55b
Refactored atcommands logic
jasonch35 Dec 1, 2024
973f6f6
Missed name parameter in clif.c
jasonch35 Dec 1, 2024
dcdd63c
Fixed invalid storage(0) assertion
jasonch35 Dec 1, 2024
49997aa
Indentation/space fix
jasonch35 Dec 1, 2024
645b77a
PCCHECK_STORAGE check fix
jasonch35 Dec 1, 2024
d6adf1b
Initialization logic fix
jasonch35 Dec 1, 2024
007a858
Removed unnecessary cleaning of VECTOR
jasonch35 Dec 1, 2024
1c34316
Removed full storage checks for functions that calls storage->additem()
jasonch35 Dec 1, 2024
a919fd8
Set constant at the end of every iteration at config reading
jasonch35 Dec 1, 2024
53baf8b
Update HPM Hooks
jasonch35 Dec 1, 2024
e882183
Messagetable update
jasonch35 Dec 1, 2024
dc85a3e
Proper code styling for asterisks
jasonch35 Dec 1, 2024
3ec152b
Clif update storage amount window to capacity
jasonch35 Dec 1, 2024
8df6252
Corrected documentation
jasonch35 Dec 1, 2024
9f08c77
Storage config reading
jasonch35 Dec 11, 2024
8eaf684
Removed redundant assignment
jasonch35 Jan 2, 2025
761ea97
Clear item list for every storage at unit freeing
jasonch35 Jan 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 41 additions & 30 deletions src/char/int_storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,26 @@ static struct inter_storage_interface inter_storage_s;
struct inter_storage_interface *inter_storage;

/// Save storage data to sql
static int inter_storage_tosql(int account_id, const struct storage_data *p)
static int inter_storage_tosql(int account_id, int storage_id, const struct storage_data *p)
{
int i = 0, j = 0;
bool matched_p[MAX_STORAGE] = { false };
int delete[MAX_STORAGE] = { 0 };
int total_deletes = 0, total_updates = 0, total_inserts = 0;
int total_changes = 0;
struct storage_data cp = { 0 };
StringBuf buf;

nullpo_ret(p);

struct storage_data cp = { 0 };
VECTOR_INIT(cp.item);
inter_storage->fromsql(account_id, &cp);

int cp_size = inter_storage->fromsql(account_id, storage_id, &cp, 0);

bool* matched_p = aCalloc(VECTOR_LENGTH(p->item), sizeof(bool));
MishimaHaruna marked this conversation as resolved.
Show resolved Hide resolved

StringBuf buf;
StrBuf->Init(&buf);

int total_deletes = 0, total_updates = 0, total_inserts = 0;
int i = 0, j = 0;
if (VECTOR_LENGTH(cp.item) > 0) {

int *delete = aCalloc(cp_size, sizeof(int));
MishimaHaruna marked this conversation as resolved.
Show resolved Hide resolved

/**
* Compare and update items, if any.
*/
Expand All @@ -67,27 +69,27 @@ static int inter_storage_tosql(int account_id, const struct storage_data *p)
struct item *p_it = NULL;

ARR_FIND(0, VECTOR_LENGTH(p->item), j,
matched_p[j] != true
&& (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(int) * MAX_SLOTS) == 0
&& memcmp(p_it->option, cp_it->option, 5 * MAX_ITEM_OPTIONS) == 0);
matched_p[j] != true
&& (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(int) * 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`, `nameid`, `amount`, `equip`, `identify`, `refine`, `grade`, `attribute`", storage_db);
StrBuf->Printf(&buf, "REPLACE INTO `%s` (`id`, `account_id`, `storage_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `grade`, `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->AppendStr(&buf, ", `expire_time`, `bound`, `unique_id`) VALUES");
}

StrBuf->Printf(&buf, "%s('%d', '%d', '%d', '%d', '%u', '%d', '%d', '%d', '%d'",
total_updates > 0 ? ", " : "", cp_it->id, account_id, p_it->nameid, p_it->amount, p_it->equip, p_it->identify, p_it->refine, p_it->grade, p_it->attribute);
StrBuf->Printf(&buf, "%s('%d', '%d', '%d', '%d', '%d', '%u', '%d', '%d', '%d', '%d'",
total_updates > 0 ? ", " : "", cp_it->id, account_id, storage_id, p_it->nameid, p_it->amount, p_it->equip, p_it->identify, p_it->refine, p_it->grade, 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)
Expand Down Expand Up @@ -119,6 +121,8 @@ static int inter_storage_tosql(int account_id, const struct storage_data *p)
if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)))
Sql_ShowDebug(inter->sql_handle);
}

aFree(delete);
}

/**
Expand All @@ -133,16 +137,16 @@ static int inter_storage_tosql(int account_id, const struct storage_data *p)
// Store the remaining items.
if (total_inserts == 0) {
StrBuf->Clear(&buf);
StrBuf->Printf(&buf, "INSERT INTO `%s` (`account_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `grade`, `attribute`, `expire_time`, `bound`, `unique_id`", storage_db);
StrBuf->Printf(&buf, "INSERT INTO `%s` (`account_id`, `storage_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `grade`, `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, "%s('%d', '%d', '%d', '%u', '%d', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'",
total_inserts > 0 ? ", " : "", account_id, p_it->nameid, p_it->amount, p_it->equip, p_it->identify, p_it->refine, p_it->grade,
StrBuf->Printf(&buf, "%s('%d', '%d', '%d', '%d', '%u', '%d', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'",
total_inserts > 0 ? ", " : "", account_id, storage_id, p_it->nameid, p_it->amount, p_it->equip, p_it->identify, p_it->refine, p_it->grade,
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]);
Expand All @@ -159,13 +163,15 @@ static int inter_storage_tosql(int account_id, const struct storage_data *p)
StrBuf->Destroy(&buf);
VECTOR_CLEAR(cp.item);

total_changes = total_inserts + total_updates + total_deletes;
ShowInfo("storage save complete - id: %d (total: %d)\n", account_id, total_changes);
return total_changes;
aFree(matched_p);

ShowInfo("Storage #%d save complete - id: %d (replace: %d, insert: %d, delete: %d)\n", storage_id, account_id, total_updates, total_inserts, total_deletes);

return total_inserts + total_updates + total_deletes;
}

/// Load storage data to mem
static int inter_storage_fromsql(int account_id, struct storage_data *p)
static int inter_storage_fromsql(int account_id, int storage_id, struct storage_data* p, int storage_size)
{
StringBuf buf;
char* data;
Expand All @@ -174,6 +180,7 @@ static int inter_storage_fromsql(int account_id, struct storage_data *p)
int num_rows = 0;

nullpo_ret(p);
Assert_ret(storage_size >= 0);

if (VECTOR_LENGTH(p->item) > 0)
VECTOR_CLEAR(p->item);
Expand All @@ -185,7 +192,7 @@ static int inter_storage_fromsql(int account_id, struct storage_data *p)
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->Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id);
StrBuf->Printf(&buf, " FROM `%s` WHERE `account_id`='%d' AND `storage_id`='%d' ORDER BY `nameid`", storage_db, account_id, storage_id);

if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)))
Sql_ShowDebug(inter->sql_handle);
Expand All @@ -194,10 +201,14 @@ static int inter_storage_fromsql(int account_id, struct storage_data *p)

if ((num_rows = (int)SQL->NumRows(inter->sql_handle)) != 0) {

VECTOR_ENSURE(p->item, num_rows > MAX_STORAGE ? MAX_STORAGE : num_rows, 1);
VECTOR_ENSURE(p->item, storage_size > 0 ? min(num_rows, storage_size) : num_rows, 1);

for (i = 0; i < MAX_STORAGE && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i) {
for (i = 0; SQL_SUCCESS == SQL->NextRow(inter->sql_handle); ++i) {
struct item item = { 0 };

if (storage_size > 0 && i > storage_size)
MishimaHaruna marked this conversation as resolved.
Show resolved Hide resolved
break;

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);
Expand Down Expand Up @@ -230,7 +241,7 @@ static int inter_storage_fromsql(int account_id, struct storage_data *p)

SQL->FreeResult(inter->sql_handle);

ShowInfo("storage load complete from DB - id: %d (total: %d)\n", account_id, VECTOR_LENGTH(p->item));
ShowInfo("Storage #%d load complete from DB - id: %d (total: %d)\n", storage_id, account_id, VECTOR_LENGTH(p->item));

return VECTOR_LENGTH(p->item);
}
Expand Down
4 changes: 2 additions & 2 deletions src/char/int_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ struct guild_storage;
* inter_storage interface
**/
struct inter_storage_interface {
int (*tosql) (int account_id, const struct storage_data *p);
int (*fromsql) (int account_id, struct storage_data *p);
int (*tosql) (int account_id, int storage_id, const struct storage_data *p);
int (*fromsql) (int account_id, int storage_id, struct storage_data *p, int storage_size);
bool (*guild_storage_tosql) (int guild_id, const struct guild_storage *gstor);
int (*guild_storage_fromsql) (int guild_id, struct guild_storage *gstor);
int (*sql_init) (void);
Expand Down
48 changes: 30 additions & 18 deletions src/char/mapif.c
Original file line number Diff line number Diff line change
Expand Up @@ -1831,29 +1831,32 @@ static int mapif_save_guild_storage_ack(int fd, int account_id, int guild_id, in

/**
* Loads the account storage and send to the map server.
* @packet 0x3805 [out] <account_id>.L <struct item[]>.P
* @packet 0x3805 [out] <packet_len>.W <account_id>.L <storage_id>.W <struct item[]>.P
* @param fd [in] file/socket descriptor.
* @param account_id [in] account id of the session.
* @param storage_id [in] storage id to load
* @param storage_size [in] size of storage
* @return 1 on success, 0 on failure.
*/
static int mapif_account_storage_load(int fd, int account_id)
static int mapif_account_storage_load(int fd, int account_id, int storage_id, int storage_size)
{
struct storage_data stor = { 0 };
int count = 0, i = 0, len = 0;

Assert_ret(account_id > 0);

VECTOR_INIT(stor.item);
count = inter_storage->fromsql(account_id, &stor);
count = inter_storage->fromsql(account_id, storage_id, &stor, storage_size);

len = 8 + count * sizeof(struct item);
len = 10 + count * sizeof(struct item);

WFIFOHEAD(fd, len);
WFIFOW(fd, 0) = 0x3805;
WFIFOW(fd, 2) = (uint16) len;
WFIFOL(fd, 4) = account_id;
WFIFOW(fd, 8) = storage_id;
for (i = 0; i < count; i++)
memcpy(WFIFOP(fd, 8 + i * sizeof(struct item)), &VECTOR_INDEX(stor.item, i), sizeof(struct item));
memcpy(WFIFOP(fd, 10 + i * sizeof(struct item)), &VECTOR_INDEX(stor.item, i), sizeof(struct item));
WFIFOSET(fd, len);

VECTOR_CLEAR(stor.item);
Expand All @@ -1863,18 +1866,22 @@ static int mapif_account_storage_load(int fd, int account_id)

/**
* Parses account storage load request from map server.
* @packet 0x3010 [in] <account_id>.L
* @packet 0x3010 [in] <account_id>.L storage_id>.W <storage_size>.W
* @param fd [in] file/socket descriptor
* @return 1 on success, 0 on failure.
*/
static int mapif_parse_AccountStorageLoad(int fd)
{
int account_id = RFIFOL(fd, 2);
int storage_id = RFIFOW(fd, 6);
int storage_size = RFIFOW(fd, 8);

Assert_ret(fd > 0);
Assert_ret(account_id > 0);
Assert_ret(storage_id > 0);
Assert_ret(storage_size >= 0);

mapif->account_storage_load(fd, account_id);
mapif->account_storage_load(fd, account_id, storage_id, storage_size);

return 1;
}
Expand All @@ -1883,7 +1890,7 @@ static int mapif_parse_AccountStorageLoad(int fd)
* Parses an account storage save request from the map server.
*
* @code{.unparsed}
* @packet 0x3011 [in] <packet_len>.W <account_id>.L <struct item[]>.P
* @packet 0x3011 [in] <packet_len>.W <account_id>.L <storage_id>.W <struct item[]>.P
* @endcode
*
* @attention If the size of packet 0x3011 changes,
Expand All @@ -1899,53 +1906,58 @@ static int mapif_parse_AccountStorageLoad(int fd)
**/
static int mapif_parse_AccountStorageSave(int fd)
{
int payload_size = RFIFOW(fd, 2) - 8, account_id = RFIFOL(fd, 4);
int payload_size = RFIFOW(fd, 2) - 10;
int account_id = RFIFOL(fd, 4);
int storage_id = RFIFOW(fd, 8);

int i = 0, count = 0;
struct storage_data p_stor = { 0 };

Assert_ret(fd > 0);
Assert_ret(account_id > 0);

count = payload_size/sizeof(struct item);
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));
const struct item *it = RFIFOP(fd, 10 + i * sizeof(struct item));

VECTOR_PUSH(p_stor.item, *it);
}

p_stor.aggregate = count;
}

inter_storage->tosql(account_id, &p_stor);
inter_storage->tosql(account_id, storage_id, &p_stor);

VECTOR_CLEAR(p_stor.item);

mapif->sAccountStorageSaveAck(fd, account_id, true);
mapif->sAccountStorageSaveAck(fd, account_id, storage_id, true);

return 1;
}

/**
* Sends an acknowledgement for the save
* status of the account storage.
* @packet 0x3808 [out] <account_id>.L <save_flag>.B
* @packet 0x3808 [out] <account_id>.L <storage_id>.W <save_flag>.B
* @param fd [in] File/Socket Descriptor.
* @param account_id [in] Account ID of the storage in question.
* @param storage_id [in] acknowledgement of storage id.
* @param flag [in] Save flag, true for success and false for failure.
*/
static void mapif_send_AccountStorageSaveAck(int fd, int account_id, bool flag)
static void mapif_send_AccountStorageSaveAck(int fd, int account_id, int storage_id, bool flag)
{
WFIFOHEAD(fd, 7);
WFIFOHEAD(fd, 9);
WFIFOW(fd, 0) = 0x3808;
WFIFOL(fd, 2) = account_id;
WFIFOB(fd, 6) = flag ? 1 : 0;
WFIFOSET(fd, 7);
WFIFOW(fd, 6) = storage_id;
WFIFOB(fd, 8) = flag ? 1 : 0;
WFIFOSET(fd, 9);
}

static int mapif_parse_LoadGuildStorage(int fd)
Expand Down
4 changes: 2 additions & 2 deletions src/char/mapif.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,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 (*account_storage_load) (int fd, int account_id, int storage_id, int storage_size);
int (*pAccountStorageLoad) (int fd);
int (*pAccountStorageSave) (int fd);
void (*sAccountStorageSaveAck) (int fd, int account_id, bool save);
void (*sAccountStorageSaveAck) (int fd, int account_id, int storage_id, bool save);
int (*itembound_ack) (int fd, int aid, int guild_id);
void (*parse_ItemBoundRetrieve) (int fd);
void (*parse_accinfo) (int fd);
Expand Down
8 changes: 8 additions & 0 deletions src/common/mmo.h
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,15 @@ struct status_change_data {
int total_tick; ///< Total duration.
};

/* Hercules Ultimate Storage System [Smokexyz/Hercules] */
struct storage_settings {
int uid; ///< Storage Identifier.
char name[NAME_LENGTH]; ///< Storage Name
int capacity; ///< Item Capacity.
};
MishimaHaruna marked this conversation as resolved.
Show resolved Hide resolved

struct storage_data {
int uid; ///< Storage Identifier.
bool save; ///< save flag.
bool received; ///< received flag.
int aggregate; ///< total item count.
Expand Down
2 changes: 1 addition & 1 deletion src/common/packets_inter_len.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ packetLen(0x300c, 0)
packetLen(0x300d, 0)
packetLen(0x300e, 0)
packetLen(0x300f, 0)
packetLen(0x3010, 6)
packetLen(0x3010, 10)
packetLen(0x3011, -1)
packetLen(0x3012, 6)
packetLen(0x3013, -1)
Expand Down
2 changes: 1 addition & 1 deletion src/common/packets_intif_len.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ packetLen(0x3804, -1)
packetLen(0x3805, -1)
packetLen(0x3806, 37)
packetLen(0x3807, -1)
packetLen(0x3808, 7)
packetLen(0x3808, 9)
packetLen(0x3809, 0)
packetLen(0x380a, 0)
packetLen(0x380b, 0)
Expand Down
Loading