[Databuckets] Add Account Scoped Databuckets (#4603)

* [Databuckets] Add Account Scoped Databuckets

* Add variation

* Fix Lua after testing
This commit is contained in:
Chris Miles 2025-01-21 16:06:18 -06:00 committed by GitHub
parent d13c725a74
commit 37ced4b003
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 228 additions and 29 deletions

View File

@ -6301,7 +6301,24 @@ ALTER TABLE zone DROP COLUMN IF EXISTS npc_update_range;
ALTER TABLE zone DROP COLUMN IF EXISTS max_movement_update_range;
ALTER TABLE `zone` ADD COLUMN `client_update_range` int(11) NOT NULL DEFAULT 600 AFTER `npc_max_aggro_dist`;
)",
.content_schema_update = true
.content_schema_update = true,
},
ManifestEntry{
.version = 9292,
.description = "2025_01_21_data_buckets_account_id",
.check = "SHOW COLUMNS FROM `data_buckets` LIKE 'account_id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `data_buckets`
ADD COLUMN `account_id` bigint(11) NULL DEFAULT 0 AFTER `expires`,
DROP INDEX `keys`,
ADD UNIQUE INDEX `keys` (`key`, `character_id`, `npc_id`, `bot_id`, `account_id`) USING BTREE;
-- Add the INDEX for character_id and key
ALTER TABLE `data_buckets` ADD KEY `idx_account_id_key` (`account_id`, `key`);
)",
.content_schema_update = false
},
// -- template; copy/paste this when you need to create a new entry
// ManifestEntry{

View File

@ -23,6 +23,7 @@ public:
std::string key_;
std::string value;
uint32_t expires;
int64_t account_id;
int64_t character_id;
int64_t npc_id;
int64_t bot_id;
@ -36,6 +37,7 @@ public:
CEREAL_NVP(key_),
CEREAL_NVP(value),
CEREAL_NVP(expires),
CEREAL_NVP(account_id),
CEREAL_NVP(character_id),
CEREAL_NVP(npc_id),
CEREAL_NVP(bot_id)
@ -55,6 +57,7 @@ public:
"`key`",
"value",
"expires",
"account_id",
"character_id",
"npc_id",
"bot_id",
@ -68,6 +71,7 @@ public:
"`key`",
"value",
"expires",
"account_id",
"character_id",
"npc_id",
"bot_id",
@ -115,6 +119,7 @@ public:
e.key_ = "";
e.value = "";
e.expires = 0;
e.account_id = 0;
e.character_id = 0;
e.npc_id = 0;
e.bot_id = 0;
@ -158,9 +163,10 @@ public:
e.key_ = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : "";
e.expires = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.character_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.bot_id = row[6] ? strtoll(row[6], nullptr, 10) : 0;
e.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.npc_id = row[6] ? strtoll(row[6], nullptr, 10) : 0;
e.bot_id = row[7] ? strtoll(row[7], nullptr, 10) : 0;
return e;
}
@ -197,9 +203,10 @@ public:
v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'");
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
v.push_back(columns[3] + " = " + std::to_string(e.expires));
v.push_back(columns[4] + " = " + std::to_string(e.character_id));
v.push_back(columns[5] + " = " + std::to_string(e.npc_id));
v.push_back(columns[6] + " = " + std::to_string(e.bot_id));
v.push_back(columns[4] + " = " + std::to_string(e.account_id));
v.push_back(columns[5] + " = " + std::to_string(e.character_id));
v.push_back(columns[6] + " = " + std::to_string(e.npc_id));
v.push_back(columns[7] + " = " + std::to_string(e.bot_id));
auto results = db.QueryDatabase(
fmt::format(
@ -225,6 +232,7 @@ public:
v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires));
v.push_back(std::to_string(e.account_id));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.bot_id));
@ -261,6 +269,7 @@ public:
v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires));
v.push_back(std::to_string(e.account_id));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.bot_id));
@ -301,9 +310,10 @@ public:
e.key_ = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : "";
e.expires = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.character_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.bot_id = row[6] ? strtoll(row[6], nullptr, 10) : 0;
e.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.npc_id = row[6] ? strtoll(row[6], nullptr, 10) : 0;
e.bot_id = row[7] ? strtoll(row[7], nullptr, 10) : 0;
all_entries.push_back(e);
}
@ -332,9 +342,10 @@ public:
e.key_ = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : "";
e.expires = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.character_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.bot_id = row[6] ? strtoll(row[6], nullptr, 10) : 0;
e.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.npc_id = row[6] ? strtoll(row[6], nullptr, 10) : 0;
e.bot_id = row[7] ? strtoll(row[7], nullptr, 10) : 0;
all_entries.push_back(e);
}
@ -413,6 +424,7 @@ public:
v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires));
v.push_back(std::to_string(e.account_id));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.bot_id));
@ -442,6 +454,7 @@ public:
v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires));
v.push_back(std::to_string(e.account_id));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.bot_id));

View File

@ -402,6 +402,7 @@ Client::~Client() {
mMovementManager->RemoveClient(this);
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Account, AccountID());
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Client, CharacterID());
if (RuleB(Bots, Enabled)) {
@ -13172,10 +13173,57 @@ void Client::BroadcastPositionUpdate()
Group *g = GetGroup();
if (g) {
for (auto & m : g->members) {
for (auto &m: g->members) {
if (m && m->IsClient() && m != this) {
m->CastToClient()->QueuePacket(&outapp);
}
}
}
}
std::string Client::GetAccountBucket(std::string bucket_name)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
return DataBucket::GetData(k).value;
}
void Client::SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
k.expires = expiration;
k.value = bucket_value;
DataBucket::SetData(k);
}
void Client::DeleteAccountBucket(std::string bucket_name)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
DataBucket::DeleteData(k);
}
std::string Client::GetAccountBucketExpires(std::string bucket_name)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
return DataBucket::GetDataExpires(k);
}
std::string Client::GetAccountBucketRemaining(std::string bucket_name)
{
DataBucketKey k = {};
k.account_id = AccountID();
k.key = bucket_name;
return DataBucket::GetDataRemaining(k);
}

View File

@ -1829,6 +1829,13 @@ public:
void SendEvolveXPTransferWindow();
void SendEvolveTransferResults(const EQ::ItemInstance &inst_from, const EQ::ItemInstance &inst_to, const EQ::ItemInstance &inst_from_new, const EQ::ItemInstance &inst_to_new, const uint32 compatibility, const uint32 max_transfer_level);
// Account buckets
std::string GetAccountBucket(std::string bucket_name);
void SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration = "");
void DeleteAccountBucket(std::string bucket_name);
std::string GetAccountBucketExpires(std::string bucket_name);
std::string GetAccountBucketRemaining(std::string bucket_name);
protected:
friend class Mob;
void CalcEdibleBonuses(StatBonuses* newbon);

View File

@ -16,6 +16,7 @@ void DataBucket::SetData(const std::string &bucket_key, const std::string &bucke
.key = bucket_key,
.value = bucket_value,
.expires = expires_time,
.account_id = 0,
.character_id = 0,
.npc_id = 0,
.bot_id = 0
@ -37,6 +38,9 @@ void DataBucket::SetData(const DataBucketKey &k)
if (k.character_id > 0) {
b.character_id = k.character_id;
}
else if (k.account_id > 0) {
b.account_id = k.account_id;
}
else if (k.npc_id > 0) {
b.npc_id = k.npc_id;
}
@ -95,9 +99,10 @@ std::string DataBucket::GetData(const std::string &bucket_key)
DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, bool ignore_misses_cache)
{
LogDataBuckets(
"Getting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
"Getting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id
);
@ -151,6 +156,7 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
.key_ = k.key,
.value = "",
.expires = 0,
.account_id = k.account_id,
.character_id = k.character_id,
.npc_id = k.npc_id,
.bot_id = k.bot_id
@ -158,8 +164,9 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
);
LogDataBuckets(
"Key [{}] not found in database, adding to cache as a miss character_id [{}] npc_id [{}] bot_id [{}] cache size before [{}] after [{}]",
"Key [{}] not found in database, adding to cache as a miss account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] cache size before [{}] after [{}]",
k.key,
k.account_id,
k.character_id,
k.npc_id,
k.bot_id,
@ -216,8 +223,6 @@ bool DataBucket::DeleteData(const std::string &bucket_key)
// GetDataBuckets bulk loads all data buckets for a mob
bool DataBucket::GetDataBuckets(Mob *mob)
{
DataBucketLoadType::Type t{};
const uint32 id = mob->GetMobTypeIdentifier();
if (!id) {
@ -225,14 +230,13 @@ bool DataBucket::GetDataBuckets(Mob *mob)
}
if (mob->IsBot()) {
t = DataBucketLoadType::Bot;
BulkLoadEntitiesToCache(DataBucketLoadType::Bot, {id});
}
else if (mob->IsClient()) {
t = DataBucketLoadType::Client;
BulkLoadEntitiesToCache(DataBucketLoadType::Account, {id});
BulkLoadEntitiesToCache(DataBucketLoadType::Client, {id});
}
BulkLoadEntitiesToCache(t, {id});
return true;
}
@ -254,9 +258,10 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
);
LogDataBuckets(
"Deleting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]",
"Deleting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id,
size_before,
@ -277,9 +282,10 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
{
LogDataBuckets(
"Getting bucket expiration key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
"Getting bucket expiration key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id
);
@ -295,9 +301,10 @@ std::string DataBucket::GetDataExpires(const DataBucketKey &k)
std::string DataBucket::GetDataRemaining(const DataBucketKey &k)
{
LogDataBuckets(
"Getting bucket remaining key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
"Getting bucket remaining key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id
);
@ -320,6 +327,13 @@ std::string DataBucket::GetScopedDbFilters(const DataBucketKey &k)
query.emplace_back("character_id = 0");
}
if (k.account_id > 0) {
query.emplace_back(fmt::format("account_id = {}", k.account_id));
}
else {
query.emplace_back("account_id = 0");
}
if (k.npc_id > 0) {
query.emplace_back(fmt::format("npc_id = {}", k.npc_id));
}
@ -346,6 +360,7 @@ bool DataBucket::CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe,
return (
dbe.key_ == k.key &&
dbe.bot_id == k.bot_id &&
dbe.account_id == k.account_id &&
dbe.character_id == k.character_id &&
dbe.npc_id == k.npc_id
);
@ -364,6 +379,9 @@ void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector
if (t == DataBucketLoadType::Bot) {
has_cache = e.bot_id == ids[0];
}
else if (t == DataBucketLoadType::Account) {
has_cache = e.account_id == ids[0];
}
else if (t == DataBucketLoadType::Client) {
has_cache = e.character_id == ids[0];
}
@ -384,6 +402,9 @@ void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector
case DataBucketLoadType::Client:
column = "character_id";
break;
case DataBucketLoadType::Account:
column = "account_id";
break;
default:
LogError("Incorrect LoadType [{}]", static_cast<int>(t));
break;
@ -442,6 +463,7 @@ void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id)
[&](DataBucketsRepository::DataBuckets &e) {
return (
(type == DataBucketLoadType::Bot && e.bot_id == id) ||
(type == DataBucketLoadType::Account && e.account_id == id) ||
(type == DataBucketLoadType::Client && e.character_id == id)
);
}
@ -481,6 +503,7 @@ void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e)
g_data_bucket_cache.end(),
[&](DataBucketsRepository::DataBuckets &ce) {
return ce.id == 0 && ce.key_ == e.key_ &&
ce.account_id == e.account_id &&
ce.character_id == e.character_id &&
ce.npc_id == e.npc_id &&
ce.bot_id == e.bot_id;
@ -516,6 +539,8 @@ void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type)
return e.bot_id == id;
case DataBucketLoadType::Client:
return e.character_id == id;
case DataBucketLoadType::Account:
return e.account_id == id;
default:
return false;
}
@ -539,7 +564,7 @@ void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type)
// npcs (ids) can be in multiple zones so we can't cache locally to the zone
bool DataBucket::CanCache(const DataBucketKey &key)
{
if (key.character_id > 0 || key.bot_id > 0) {
if (key.character_id > 0 || key.account_id > 0 || key.bot_id > 0) {
return true;
}

View File

@ -12,20 +12,23 @@ struct DataBucketKey {
std::string key;
std::string value;
std::string expires;
int64_t character_id;
int64_t npc_id;
int64_t bot_id;
int64_t account_id = 0;
int64_t character_id = 0;
int64_t npc_id = 0;
int64_t bot_id = 0;
};
namespace DataBucketLoadType {
enum Type : uint8 {
Bot,
Account,
Client,
MaxType
};
static const std::string Name[Type::MaxType] = {
"Bot",
"Account",
"Client",
};
}

View File

@ -3464,6 +3464,42 @@ void Lua_Client::SetAAEXPPercentage(uint8 percentage)
self->SetAAEXPPercentage(percentage);
}
void Lua_Client::SetAccountBucket(std::string bucket_name, std::string bucket_value)
{
Lua_Safe_Call_Void();
self->SetAccountBucket(bucket_name, bucket_value);
}
void Lua_Client::SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration)
{
Lua_Safe_Call_Void();
self->SetAccountBucket(bucket_name, bucket_value, expiration);
}
void Lua_Client::DeleteAccountBucket(std::string bucket_name)
{
Lua_Safe_Call_Void();
self->DeleteAccountBucket(bucket_name);
}
std::string Lua_Client::GetAccountBucket(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetAccountBucket(bucket_name);
}
std::string Lua_Client::GetAccountBucketExpires(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetAccountBucketExpires(bucket_name);
}
std::string Lua_Client::GetAccountBucketRemaining(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetAccountBucketRemaining(bucket_name);
}
luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>())
@ -3552,6 +3588,7 @@ luabind::scope lua_register_client() {
.def("CreateTaskDynamicZone", &Lua_Client::CreateTaskDynamicZone)
.def("DecreaseByID", (bool(Lua_Client::*)(uint32,int))&Lua_Client::DecreaseByID)
.def("DescribeSpecialAbilities", (void(Lua_Client::*)(Lua_NPC))&Lua_Client::DescribeSpecialAbilities)
.def("DeleteAccountBucket", (void(Lua_Client::*)(std::string))&Lua_Client::DeleteAccountBucket)
.def("DeleteBucket", (void(Lua_Client::*)(std::string))&Lua_Client::DeleteBucket)
.def("DeleteItemInInventory", (void(Lua_Client::*)(int,int))&Lua_Client::DeleteItemInInventory)
.def("DeleteItemInInventory", (void(Lua_Client::*)(int,int,bool))&Lua_Client::DeleteItemInInventory)
@ -3630,6 +3667,9 @@ luabind::scope lua_register_client() {
.def("GetBotRequiredLevel", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotRequiredLevel)
.def("GetBotSpawnLimit", (int(Lua_Client::*)(void))&Lua_Client::GetBotSpawnLimit)
.def("GetBotSpawnLimit", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotSpawnLimit)
.def("GetAccountBucket", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountBucket)
.def("GetAccountBucketExpires", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountBucketExpires)
.def("GetAccountBucketRemaining", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountBucketRemaining)
.def("GetBucket", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucket)
.def("GetBucketExpires", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketExpires)
.def("GetBucketRemaining", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketRemaining)
@ -3922,6 +3962,8 @@ luabind::scope lua_register_client() {
.def("SetBotRequiredLevel", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotRequiredLevel)
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int))&Lua_Client::SetBotSpawnLimit)
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotSpawnLimit)
.def("SetAccountBucket", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountBucket)
.def("SetAccountBucket", (void(Lua_Client::*)(std::string,std::string,std::string))&Lua_Client::SetAccountBucket)
.def("SetBucket", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetBucket)
.def("SetBucket", (void(Lua_Client::*)(std::string,std::string,std::string))&Lua_Client::SetBucket)
.def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel)

View File

@ -512,6 +512,14 @@ public:
luabind::object GetInventorySlots(lua_State* L);
void SetAAEXPPercentage(uint8 percentage);
// account data buckets
void SetAccountBucket(std::string bucket_name, std::string bucket_value);
void SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration = "");
void DeleteAccountBucket(std::string bucket_name);
std::string GetAccountBucket(std::string bucket_name);
std::string GetAccountBucketExpires(std::string bucket_name);
std::string GetAccountBucketRemaining(std::string bucket_name);
void ApplySpell(int spell_id);
void ApplySpell(int spell_id, int duration);
void ApplySpell(int spell_id, int duration, int level);

View File

@ -3234,6 +3234,36 @@ void Perl_Client_SetAAEXPPercentage(Client* self, uint8 percentage)
self->SetAAEXPPercentage(percentage);
}
void Perl_Client_SetAccountBucket(Client* self, std::string bucket_name, std::string bucket_value)
{
self->SetAccountBucket(bucket_name, bucket_value);
}
void Perl_Client_SetAccountBucket(Client* self, std::string bucket_name, std::string bucket_value, std::string expiration = "")
{
self->SetAccountBucket(bucket_name, bucket_value, expiration);
}
void Perl_Client_DeleteAccountBucket(Client* self, std::string bucket_name)
{
self->DeleteAccountBucket(bucket_name);
}
std::string Perl_Client_GetAccountBucket(Client* self, std::string bucket_name)
{
return self->GetAccountBucket(bucket_name);
}
std::string Perl_Client_GetAccountBucketExpires(Client* self, std::string bucket_name)
{
return self->GetAccountBucketExpires(bucket_name);
}
std::string Perl_Client_GetAccountBucketRemaining(Client* self, std::string bucket_name)
{
return self->GetAccountBucketRemaining(bucket_name);
}
void perl_register_client()
{
perl::interpreter perl(PERL_GET_THX);
@ -3325,6 +3355,7 @@ void perl_register_client()
package.add("CreateTaskDynamicZone", &Perl_Client_CreateTaskDynamicZone);
package.add("DecreaseByID", &Perl_Client_DecreaseByID);
package.add("DescribeSpecialAbilities", &Perl_Client_DescribeSpecialAbilities);
package.add("DeleteAccountBucket", &Perl_Client_DeleteAccountBucket);
package.add("DeleteItemInInventory", (void(*)(Client*, int16))&Perl_Client_DeleteItemInInventory);
package.add("DeleteItemInInventory", (void(*)(Client*, int16, int16))&Perl_Client_DeleteItemInInventory);
package.add("DeleteItemInInventory", (void(*)(Client*, int16, int16, bool))&Perl_Client_DeleteItemInInventory);
@ -3362,6 +3393,9 @@ void perl_register_client()
package.add("GetAAPoints", &Perl_Client_GetAAPoints);
package.add("GetAFK", &Perl_Client_GetAFK);
package.add("GetAccountAge", &Perl_Client_GetAccountAge);
package.add("GetAccountBucket", &Perl_Client_GetAccountBucket);
package.add("GetAccountBucketExpires", &Perl_Client_GetAccountBucketExpires);
package.add("GetGetAccountBucketRemaining", &Perl_Client_GetAccountBucketRemaining);
package.add("GetAccountFlag", &Perl_Client_GetAccountFlag);
package.add("GetAccountFlags", &Perl_Client_GetAccountFlags);
package.add("GetAggroCount", &Perl_Client_GetAggroCount);
@ -3668,6 +3702,8 @@ void perl_register_client()
package.add("SetAATitle", (void(*)(Client*, std::string, bool))&Perl_Client_SetAATitle);
package.add("SetAFK", &Perl_Client_SetAFK);
package.add("SetAccountFlag", &Perl_Client_SetAccountFlag);
package.add("SetAccountBucket", (void(*)(Client*, std::string, std::string))&Perl_Client_SetAccountBucket);
package.add("SetAccountBucket", (void(*)(Client*, std::string, std::string, std::string))&Perl_Client_SetAccountBucket);
package.add("SetAlternateCurrencyValue", &Perl_Client_SetAlternateCurrencyValue);
package.add("SetAnon", &Perl_Client_SetAnon);
package.add("SetAutoLoginCharacterName", (bool(*)(Client*))&Perl_Client_SetAutoLoginCharacterName);