mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 18:51:29 +00:00
[Databuckets] Improved Reliability and Performance of Databuckets (#4562)
* [Databuckets] Don't broadcast client-scoped updates * Remove temp feature flag * Remove distributed caching, only cache for character scoped data, simplify * Update bot.cpp * Cleanup * Update data_bucket.cpp * Cleanup * Cleanup * Remove BulkLoadEntities from LoadNPCTypes * Update data_bucket.cpp * Cleanup * More cleanup * More cleanup * BulkLoadEntities to BulkLoadEntitiesToCache * Add CanCache in DeleteData to gate an unnecessary call
This commit is contained in:
parent
5c6e7a8b09
commit
66a7dd0143
@ -319,8 +319,6 @@
|
|||||||
// player events
|
// player events
|
||||||
#define ServerOP_PlayerEvent 0x5100
|
#define ServerOP_PlayerEvent 0x5100
|
||||||
|
|
||||||
#define ServerOP_DataBucketCacheUpdate 0x5200
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
CZUpdateType_Character,
|
CZUpdateType_Character,
|
||||||
CZUpdateType_Group,
|
CZUpdateType_Group,
|
||||||
|
|||||||
@ -1564,11 +1564,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
DynamicZone::HandleZoneMessage(pack);
|
DynamicZone::HandleZoneMessage(pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ServerOP_DataBucketCacheUpdate: {
|
|
||||||
zoneserver_list.SendPacket(pack);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ServerOP_GuildTributeUpdate: {
|
case ServerOP_GuildTributeUpdate: {
|
||||||
auto data = (GuildTributeUpdate *)pack->pBuffer;
|
auto data = (GuildTributeUpdate *)pack->pBuffer;
|
||||||
auto guild = guild_mgr.GetGuildByGuildID(data->guild_id);
|
auto guild = guild_mgr.GetGuildByGuildID(data->guild_id);
|
||||||
|
|||||||
@ -3089,6 +3089,7 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
|||||||
m_targetable = true;
|
m_targetable = true;
|
||||||
entity_list.AddBot(this, true, true);
|
entity_list.AddBot(this, true, true);
|
||||||
|
|
||||||
|
ClearDataBucketCache();
|
||||||
DataBucket::GetDataBuckets(this);
|
DataBucket::GetDataBuckets(this);
|
||||||
LoadBotSpellSettings();
|
LoadBotSpellSettings();
|
||||||
if (!AI_AddBotSpells(GetBotSpellID())) {
|
if (!AI_AddBotSpells(GetBotSpellID())) {
|
||||||
|
|||||||
@ -524,7 +524,6 @@ int Client::HandlePacket(const EQApplicationPacket *app)
|
|||||||
// Finish client connecting state
|
// Finish client connecting state
|
||||||
void Client::CompleteConnect()
|
void Client::CompleteConnect()
|
||||||
{
|
{
|
||||||
|
|
||||||
UpdateWho();
|
UpdateWho();
|
||||||
client_state = CLIENT_CONNECTED;
|
client_state = CLIENT_CONNECTED;
|
||||||
SendAllPackets();
|
SendAllPackets();
|
||||||
@ -1413,6 +1412,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
|||||||
drakkin_details = m_pp.drakkin_details;
|
drakkin_details = m_pp.drakkin_details;
|
||||||
|
|
||||||
// Load Data Buckets
|
// Load Data Buckets
|
||||||
|
ClearDataBucketCache();
|
||||||
DataBucket::GetDataBuckets(this);
|
DataBucket::GetDataBuckets(this);
|
||||||
|
|
||||||
// Max Level for Character:PerCharacterQglobalMaxLevel and Character:PerCharacterBucketMaxLevel
|
// Max Level for Character:PerCharacterQglobalMaxLevel and Character:PerCharacterBucketMaxLevel
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
extern WorldServer worldserver;
|
extern WorldServer worldserver;
|
||||||
|
|
||||||
std::vector<DataBucketCacheEntry> g_data_bucket_cache = {};
|
std::vector<DataBucketsRepository::DataBuckets> g_data_bucket_cache = {};
|
||||||
|
|
||||||
void DataBucket::SetData(const std::string &bucket_key, const std::string &bucket_value, std::string expires_time)
|
void DataBucket::SetData(const std::string &bucket_key, const std::string &bucket_value, std::string expires_time)
|
||||||
{
|
{
|
||||||
@ -58,14 +58,14 @@ void DataBucket::SetData(const DataBucketKey &k)
|
|||||||
b.value = k.value;
|
b.value = k.value;
|
||||||
|
|
||||||
if (bucket_id) {
|
if (bucket_id) {
|
||||||
// loop cache and update cache value and timestamp
|
|
||||||
for (auto &ce: g_data_bucket_cache) {
|
// update the cache if it exists
|
||||||
if (CheckBucketMatch(ce.e, k)) {
|
if (CanCache(k)) {
|
||||||
ce.e = b;
|
for (auto &e: g_data_bucket_cache) {
|
||||||
ce.updated_time = GetCurrentTimeUNIX();
|
if (CheckBucketMatch(e, k)) {
|
||||||
ce.update_action = DataBucketCacheUpdateAction::Upsert;
|
e = b;
|
||||||
SendDataBucketCacheUpdate(ce);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,28 +74,18 @@ void DataBucket::SetData(const DataBucketKey &k)
|
|||||||
else {
|
else {
|
||||||
b.key_ = k.key;
|
b.key_ = k.key;
|
||||||
b = DataBucketsRepository::InsertOne(database, b);
|
b = DataBucketsRepository::InsertOne(database, b);
|
||||||
if (!ExistsInCache(b)) {
|
|
||||||
// add data bucket and timestamp to cache
|
|
||||||
auto ce = DataBucketCacheEntry{
|
|
||||||
.e = b,
|
|
||||||
.updated_time = DataBucket::GetCurrentTimeUNIX(),
|
|
||||||
.update_action = DataBucketCacheUpdateAction::Upsert
|
|
||||||
};
|
|
||||||
|
|
||||||
g_data_bucket_cache.emplace_back(ce);
|
|
||||||
|
|
||||||
SendDataBucketCacheUpdate(ce);
|
|
||||||
|
|
||||||
|
// add to cache if it doesn't exist
|
||||||
|
if (CanCache(k) && !ExistsInCache(b)) {
|
||||||
DeleteFromMissesCache(b);
|
DeleteFromMissesCache(b);
|
||||||
|
g_data_bucket_cache.emplace_back(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DataBucket::GetData(const std::string &bucket_key)
|
std::string DataBucket::GetData(const std::string &bucket_key)
|
||||||
{
|
{
|
||||||
DataBucketKey k = {};
|
return GetData(DataBucketKey{.key = bucket_key}).value;
|
||||||
k.key = bucket_key;
|
|
||||||
return GetData(k).value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetData fetches bucket data from the database or cache if it exists
|
// GetData fetches bucket data from the database or cache if it exists
|
||||||
@ -112,22 +102,27 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
|
|||||||
k.npc_id
|
k.npc_id
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const auto &ce: g_data_bucket_cache) {
|
bool can_cache = CanCache(k);
|
||||||
if (CheckBucketMatch(ce.e, k)) {
|
|
||||||
if (ce.e.expires > 0 && ce.e.expires < std::time(nullptr)) {
|
|
||||||
LogDataBuckets("Attempted to read expired key [{}] removing from cache", ce.e.key_);
|
|
||||||
DeleteData(k);
|
|
||||||
return DataBucketsRepository::NewEntity();
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a bucket miss, return empty entity
|
// check the cache first if we can cache
|
||||||
// we still cache bucket misses, so we don't have to hit the database
|
if (can_cache) {
|
||||||
if (ce.e.id == 0) {
|
for (const auto &e: g_data_bucket_cache) {
|
||||||
return DataBucketsRepository::NewEntity();
|
if (CheckBucketMatch(e, k)) {
|
||||||
}
|
if (e.expires > 0 && e.expires < std::time(nullptr)) {
|
||||||
|
LogDataBuckets("Attempted to read expired key [{}] removing from cache", e.key_);
|
||||||
|
DeleteData(k);
|
||||||
|
return DataBucketsRepository::NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
LogDataBuckets("Returning key [{}] value [{}] from cache", ce.e.key_, ce.e.value);
|
// this is a bucket miss, return empty entity
|
||||||
return ce.e;
|
// we still cache bucket misses, so we don't have to hit the database
|
||||||
|
if (e.id == 0) {
|
||||||
|
return DataBucketsRepository::NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
LogDataBuckets("Returning key [{}] value [{}] from cache", e.key_, e.value);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,23 +139,21 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
|
|||||||
|
|
||||||
// if we're ignoring the misses cache, don't add to the cache
|
// if we're ignoring the misses cache, don't add to the cache
|
||||||
// the only place this is ignored is during the initial read of SetData
|
// the only place this is ignored is during the initial read of SetData
|
||||||
if (!ignore_misses_cache) {
|
bool add_to_misses_cache = !ignore_misses_cache && can_cache;
|
||||||
|
if (add_to_misses_cache) {
|
||||||
size_t size_before = g_data_bucket_cache.size();
|
size_t size_before = g_data_bucket_cache.size();
|
||||||
|
|
||||||
// cache bucket misses, so we don't have to hit the database
|
// cache bucket misses, so we don't have to hit the database
|
||||||
// when scripts try to read a bucket that doesn't exist
|
// when scripts try to read a bucket that doesn't exist
|
||||||
g_data_bucket_cache.emplace_back(
|
g_data_bucket_cache.emplace_back(
|
||||||
DataBucketCacheEntry{
|
DataBucketsRepository::DataBuckets{
|
||||||
.e = DataBucketsRepository::DataBuckets{
|
.id = 0,
|
||||||
.id = 0,
|
.key_ = k.key,
|
||||||
.key_ = k.key,
|
.value = "",
|
||||||
.value = "",
|
.expires = 0,
|
||||||
.expires = 0,
|
.character_id = k.character_id,
|
||||||
.character_id = k.character_id,
|
.npc_id = k.npc_id,
|
||||||
.npc_id = k.npc_id,
|
.bot_id = k.bot_id
|
||||||
.bot_id = k.bot_id
|
|
||||||
},
|
|
||||||
.updated_time = DataBucket::GetCurrentTimeUNIX()
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -178,60 +171,54 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto bucket = r.front();
|
||||||
|
|
||||||
// if the entry has expired, delete it
|
// if the entry has expired, delete it
|
||||||
if (r[0].expires > 0 && r[0].expires < (long long) std::time(nullptr)) {
|
if (bucket.expires > 0 && bucket.expires < (long long) std::time(nullptr)) {
|
||||||
DeleteData(k);
|
DeleteData(k);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_cache = false;
|
// add to cache if it doesn't exist
|
||||||
for (auto &ce: g_data_bucket_cache) {
|
if (can_cache) {
|
||||||
if (ce.e.id == r[0].id) {
|
bool has_cache = false;
|
||||||
has_cache = true;
|
|
||||||
break;
|
for (auto &e: g_data_bucket_cache) {
|
||||||
|
if (e.id == bucket.id) {
|
||||||
|
has_cache = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_cache) {
|
||||||
|
g_data_bucket_cache.emplace_back(bucket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!has_cache) {
|
return bucket;
|
||||||
// add data bucket and timestamp to cache
|
|
||||||
g_data_bucket_cache.emplace_back(
|
|
||||||
DataBucketCacheEntry{
|
|
||||||
.e = r[0],
|
|
||||||
.updated_time = DataBucket::GetCurrentTimeUNIX()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return r[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DataBucket::GetDataExpires(const std::string &bucket_key)
|
std::string DataBucket::GetDataExpires(const std::string &bucket_key)
|
||||||
{
|
{
|
||||||
DataBucketKey k = {};
|
return GetDataExpires(DataBucketKey{.key = bucket_key});
|
||||||
k.key = bucket_key;
|
|
||||||
|
|
||||||
return GetDataExpires(k);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DataBucket::GetDataRemaining(const std::string &bucket_key)
|
std::string DataBucket::GetDataRemaining(const std::string &bucket_key)
|
||||||
{
|
{
|
||||||
DataBucketKey k = {};
|
return GetDataRemaining(DataBucketKey{.key = bucket_key});
|
||||||
k.key = bucket_key;
|
|
||||||
return GetDataRemaining(k);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DataBucket::DeleteData(const std::string &bucket_key)
|
bool DataBucket::DeleteData(const std::string &bucket_key)
|
||||||
{
|
{
|
||||||
DataBucketKey k = {};
|
return DeleteData(DataBucketKey{.key = bucket_key});
|
||||||
k.key = bucket_key;
|
|
||||||
return DeleteData(k);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDataBuckets bulk loads all data buckets for a mob
|
// GetDataBuckets bulk loads all data buckets for a mob
|
||||||
bool DataBucket::GetDataBuckets(Mob *mob)
|
bool DataBucket::GetDataBuckets(Mob *mob)
|
||||||
{
|
{
|
||||||
DataBucketLoadType::Type t;
|
DataBucketLoadType::Type t{};
|
||||||
const uint32 id = mob->GetMobTypeIdentifier();
|
|
||||||
|
const uint32 id = mob->GetMobTypeIdentifier();
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return false;
|
return false;
|
||||||
@ -243,46 +230,39 @@ bool DataBucket::GetDataBuckets(Mob *mob)
|
|||||||
else if (mob->IsClient()) {
|
else if (mob->IsClient()) {
|
||||||
t = DataBucketLoadType::Client;
|
t = DataBucketLoadType::Client;
|
||||||
}
|
}
|
||||||
else if (mob->IsNPC()) {
|
|
||||||
t = DataBucketLoadType::NPC;
|
|
||||||
}
|
|
||||||
|
|
||||||
BulkLoadEntities(t, {id});
|
BulkLoadEntitiesToCache(t, {id});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DataBucket::DeleteData(const DataBucketKey &k)
|
bool DataBucket::DeleteData(const DataBucketKey &k)
|
||||||
{
|
{
|
||||||
size_t size_before = g_data_bucket_cache.size();
|
if (CanCache(k)) {
|
||||||
|
size_t size_before = g_data_bucket_cache.size();
|
||||||
|
|
||||||
// delete from cache where contents match
|
// delete from cache where contents match
|
||||||
g_data_bucket_cache.erase(
|
g_data_bucket_cache.erase(
|
||||||
std::remove_if(
|
std::remove_if(
|
||||||
g_data_bucket_cache.begin(),
|
g_data_bucket_cache.begin(),
|
||||||
g_data_bucket_cache.end(),
|
g_data_bucket_cache.end(),
|
||||||
[&](DataBucketCacheEntry &ce) {
|
[&](DataBucketsRepository::DataBuckets &e) {
|
||||||
bool match = CheckBucketMatch(ce.e, k);
|
return CheckBucketMatch(e, k);
|
||||||
if (match) {
|
|
||||||
ce.update_action = DataBucketCacheUpdateAction::Delete;
|
|
||||||
SendDataBucketCacheUpdate(ce);
|
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
g_data_bucket_cache.end()
|
||||||
|
);
|
||||||
|
|
||||||
return match;
|
LogDataBuckets(
|
||||||
}
|
"Deleting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]",
|
||||||
),
|
k.key,
|
||||||
g_data_bucket_cache.end()
|
k.bot_id,
|
||||||
);
|
k.character_id,
|
||||||
|
k.npc_id,
|
||||||
LogDataBuckets(
|
size_before,
|
||||||
"Deleting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]",
|
g_data_bucket_cache.size()
|
||||||
k.key,
|
);
|
||||||
k.bot_id,
|
}
|
||||||
k.character_id,
|
|
||||||
k.npc_id,
|
|
||||||
size_before,
|
|
||||||
g_data_bucket_cache.size()
|
|
||||||
);
|
|
||||||
|
|
||||||
return DataBucketsRepository::DeleteWhere(
|
return DataBucketsRepository::DeleteWhere(
|
||||||
database,
|
database,
|
||||||
@ -371,23 +351,21 @@ bool DataBucket::CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32> ids)
|
void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector<uint32> ids)
|
||||||
{
|
{
|
||||||
if (ids.empty()) {
|
if (ids.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ids.size() == 1) {
|
if (ids.size() == 1) {
|
||||||
bool has_cache = false;
|
bool has_cache = false;
|
||||||
for (const auto &ce: g_data_bucket_cache) {
|
|
||||||
|
for (const auto &e: g_data_bucket_cache) {
|
||||||
if (t == DataBucketLoadType::Bot) {
|
if (t == DataBucketLoadType::Bot) {
|
||||||
has_cache = ce.e.bot_id == ids[0];
|
has_cache = e.bot_id == ids[0];
|
||||||
}
|
}
|
||||||
else if (t == DataBucketLoadType::Client) {
|
else if (t == DataBucketLoadType::Client) {
|
||||||
has_cache = ce.e.character_id == ids[0];
|
has_cache = e.character_id == ids[0];
|
||||||
}
|
|
||||||
else if (t == DataBucketLoadType::NPC) {
|
|
||||||
has_cache = ce.e.npc_id == ids[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,9 +384,6 @@ void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32
|
|||||||
case DataBucketLoadType::Client:
|
case DataBucketLoadType::Client:
|
||||||
column = "character_id";
|
column = "character_id";
|
||||||
break;
|
break;
|
||||||
case DataBucketLoadType::NPC:
|
|
||||||
column = "npc_id";
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
LogError("Incorrect LoadType [{}]", static_cast<int>(t));
|
LogError("Incorrect LoadType [{}]", static_cast<int>(t));
|
||||||
break;
|
break;
|
||||||
@ -442,12 +417,7 @@ void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32
|
|||||||
if (!ExistsInCache(e)) {
|
if (!ExistsInCache(e)) {
|
||||||
LogDataBucketsDetail("bucket id [{}] bucket key [{}] bucket value [{}]", e.id, e.key_, e.value);
|
LogDataBucketsDetail("bucket id [{}] bucket key [{}] bucket value [{}]", e.id, e.key_, e.value);
|
||||||
|
|
||||||
g_data_bucket_cache.emplace_back(
|
g_data_bucket_cache.emplace_back(e);
|
||||||
DataBucketCacheEntry{
|
|
||||||
.e = e,
|
|
||||||
.updated_time = GetCurrentTimeUNIX()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,7 +431,7 @@ void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type t, uint32 id)
|
void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id)
|
||||||
{
|
{
|
||||||
size_t size_before = g_data_bucket_cache.size();
|
size_t size_before = g_data_bucket_cache.size();
|
||||||
|
|
||||||
@ -469,11 +439,10 @@ void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type t, uint32 id)
|
|||||||
std::remove_if(
|
std::remove_if(
|
||||||
g_data_bucket_cache.begin(),
|
g_data_bucket_cache.begin(),
|
||||||
g_data_bucket_cache.end(),
|
g_data_bucket_cache.end(),
|
||||||
[&](DataBucketCacheEntry &ce) {
|
[&](DataBucketsRepository::DataBuckets &e) {
|
||||||
return (
|
return (
|
||||||
(t == DataBucketLoadType::Bot && ce.e.bot_id == id) ||
|
(type == DataBucketLoadType::Bot && e.bot_id == id) ||
|
||||||
(t == DataBucketLoadType::Client && ce.e.character_id == id) ||
|
(type == DataBucketLoadType::Client && e.character_id == id)
|
||||||
(t == DataBucketLoadType::NPC && ce.e.npc_id == id)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -482,24 +451,17 @@ void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type t, uint32 id)
|
|||||||
|
|
||||||
LogDataBuckets(
|
LogDataBuckets(
|
||||||
"LoadType [{}] id [{}] cache size before [{}] after [{}]",
|
"LoadType [{}] id [{}] cache size before [{}] after [{}]",
|
||||||
DataBucketLoadType::Name[t],
|
DataBucketLoadType::Name[type],
|
||||||
id,
|
id,
|
||||||
size_before,
|
size_before,
|
||||||
g_data_bucket_cache.size()
|
g_data_bucket_cache.size()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t DataBucket::GetCurrentTimeUNIX()
|
bool DataBucket::ExistsInCache(const DataBucketsRepository::DataBuckets &entry)
|
||||||
{
|
{
|
||||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
for (const auto &e: g_data_bucket_cache) {
|
||||||
std::chrono::system_clock::now().time_since_epoch()
|
if (e.id == entry.id) {
|
||||||
).count();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataBucket::ExistsInCache(const DataBucketsRepository::DataBuckets &e)
|
|
||||||
{
|
|
||||||
for (const auto &ce: g_data_bucket_cache) {
|
|
||||||
if (ce.e.id == e.id) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -507,134 +469,6 @@ bool DataBucket::ExistsInCache(const DataBucketsRepository::DataBuckets &e)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DataBucket::SendDataBucketCacheUpdate(const DataBucketCacheEntry &e)
|
|
||||||
{
|
|
||||||
if (!e.e.id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
EQ::Net::DynamicPacket p;
|
|
||||||
p.PutSerialize(0, e);
|
|
||||||
|
|
||||||
auto pack_size = sizeof(ServerDataBucketCacheUpdate_Struct) + p.Length();
|
|
||||||
auto pack = new ServerPacket(ServerOP_DataBucketCacheUpdate, static_cast<uint32_t>(pack_size));
|
|
||||||
auto buf = reinterpret_cast<ServerDataBucketCacheUpdate_Struct *>(pack->pBuffer);
|
|
||||||
|
|
||||||
buf->cereal_size = static_cast<uint32_t>(p.Length());
|
|
||||||
|
|
||||||
memcpy(buf->cereal_data, p.Data(), p.Length());
|
|
||||||
|
|
||||||
worldserver.SendPacket(pack);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataBucket::HandleWorldMessage(ServerPacket *p)
|
|
||||||
{
|
|
||||||
DataBucketCacheEntry n;
|
|
||||||
auto s = (ServerDataBucketCacheUpdate_Struct *) p->pBuffer;
|
|
||||||
EQ::Util::MemoryStreamReader ss(s->cereal_data, s->cereal_size);
|
|
||||||
cereal::BinaryInputArchive archive(ss);
|
|
||||||
archive(n);
|
|
||||||
|
|
||||||
LogDataBucketsDetail(
|
|
||||||
"Received cache packet for id [{}] key [{}] value [{}] action [{}]",
|
|
||||||
n.e.id,
|
|
||||||
n.e.key_,
|
|
||||||
n.e.value,
|
|
||||||
static_cast<int>(n.update_action)
|
|
||||||
);
|
|
||||||
|
|
||||||
// delete
|
|
||||||
if (n.update_action == DataBucketCacheUpdateAction::Delete) {
|
|
||||||
DeleteFromMissesCache(n.e);
|
|
||||||
|
|
||||||
g_data_bucket_cache.erase(
|
|
||||||
std::remove_if(
|
|
||||||
g_data_bucket_cache.begin(),
|
|
||||||
g_data_bucket_cache.end(),
|
|
||||||
[&](DataBucketCacheEntry &ce) {
|
|
||||||
bool match = n.e.id > 0 && ce.e.id == n.e.id;
|
|
||||||
if (match) {
|
|
||||||
LogDataBuckets(
|
|
||||||
"[delete] cache key [{}] id [{}] cache_size before [{}] after [{}]",
|
|
||||||
ce.e.key_,
|
|
||||||
ce.e.id,
|
|
||||||
g_data_bucket_cache.size(),
|
|
||||||
g_data_bucket_cache.size() - 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
g_data_bucket_cache.end()
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update
|
|
||||||
bool has_key = false;
|
|
||||||
for (auto &ce: g_data_bucket_cache) {
|
|
||||||
// update cache
|
|
||||||
if (ce.e.id == n.e.id) {
|
|
||||||
// reject old updates
|
|
||||||
int64 time_delta = ce.updated_time - n.updated_time;
|
|
||||||
if (ce.updated_time >= n.updated_time) {
|
|
||||||
LogDataBuckets(
|
|
||||||
"Attempted to update older cache key [{}] rejecting old time [{}] new time [{}] delta [{}] cache_size [{}]",
|
|
||||||
ce.e.key_,
|
|
||||||
ce.updated_time,
|
|
||||||
n.updated_time,
|
|
||||||
time_delta,
|
|
||||||
g_data_bucket_cache.size()
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteFromMissesCache(n.e);
|
|
||||||
|
|
||||||
LogDataBuckets(
|
|
||||||
"[update] cache id [{}] key [{}] value [{}] old time [{}] new time [{}] delta [{}] cache_size [{}]",
|
|
||||||
ce.e.id,
|
|
||||||
ce.e.key_,
|
|
||||||
n.e.value,
|
|
||||||
ce.updated_time,
|
|
||||||
n.updated_time,
|
|
||||||
time_delta,
|
|
||||||
g_data_bucket_cache.size()
|
|
||||||
);
|
|
||||||
ce.e = n.e;
|
|
||||||
ce.updated_time = n.updated_time;
|
|
||||||
has_key = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create
|
|
||||||
if (!has_key) {
|
|
||||||
DeleteFromMissesCache(n.e);
|
|
||||||
|
|
||||||
size_t size_before = g_data_bucket_cache.size();
|
|
||||||
|
|
||||||
g_data_bucket_cache.emplace_back(
|
|
||||||
DataBucketCacheEntry{
|
|
||||||
.e = n.e,
|
|
||||||
.updated_time = GetCurrentTimeUNIX()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
LogDataBuckets(
|
|
||||||
"[create] Adding new cache id [{}] key [{}] value [{}] cache size before [{}] after [{}]",
|
|
||||||
n.e.id,
|
|
||||||
n.e.key_,
|
|
||||||
n.e.value,
|
|
||||||
size_before,
|
|
||||||
g_data_bucket_cache.size()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e)
|
void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e)
|
||||||
{
|
{
|
||||||
// delete from cache where there might have been a written bucket miss to the cache
|
// delete from cache where there might have been a written bucket miss to the cache
|
||||||
@ -645,11 +479,11 @@ void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e)
|
|||||||
std::remove_if(
|
std::remove_if(
|
||||||
g_data_bucket_cache.begin(),
|
g_data_bucket_cache.begin(),
|
||||||
g_data_bucket_cache.end(),
|
g_data_bucket_cache.end(),
|
||||||
[&](DataBucketCacheEntry &ce) {
|
[&](DataBucketsRepository::DataBuckets &ce) {
|
||||||
return ce.e.id == 0 && ce.e.key_ == e.key_ &&
|
return ce.id == 0 && ce.key_ == e.key_ &&
|
||||||
ce.e.character_id == e.character_id &&
|
ce.character_id == e.character_id &&
|
||||||
ce.e.npc_id == e.npc_id &&
|
ce.npc_id == e.npc_id &&
|
||||||
ce.e.bot_id == e.bot_id;
|
ce.bot_id == e.bot_id;
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
g_data_bucket_cache.end()
|
g_data_bucket_cache.end()
|
||||||
@ -667,3 +501,47 @@ void DataBucket::ClearCache()
|
|||||||
g_data_bucket_cache.clear();
|
g_data_bucket_cache.clear();
|
||||||
LogInfo("Cleared data buckets cache");
|
LogInfo("Cleared data buckets cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type)
|
||||||
|
{
|
||||||
|
size_t size_before = g_data_bucket_cache.size();
|
||||||
|
|
||||||
|
g_data_bucket_cache.erase(
|
||||||
|
std::remove_if(
|
||||||
|
g_data_bucket_cache.begin(),
|
||||||
|
g_data_bucket_cache.end(),
|
||||||
|
[&](DataBucketsRepository::DataBuckets &e) {
|
||||||
|
switch (type) {
|
||||||
|
case DataBucketLoadType::Bot:
|
||||||
|
return e.bot_id == id;
|
||||||
|
case DataBucketLoadType::Client:
|
||||||
|
return e.character_id == id;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
g_data_bucket_cache.end()
|
||||||
|
);
|
||||||
|
|
||||||
|
LogDataBuckets(
|
||||||
|
"Deleted [{}] id [{}] from cache size before [{}] after [{}]",
|
||||||
|
DataBucketLoadType::Name[type],
|
||||||
|
id,
|
||||||
|
size_before,
|
||||||
|
g_data_bucket_cache.size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanCache returns whether a bucket can be cached or not
|
||||||
|
// characters are only in one zone at a time so we can cache locally to the zone
|
||||||
|
// bots (not implemented) are only in one zone at a time so we can cache locally to the zone
|
||||||
|
// 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) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
//
|
|
||||||
// Created by Akkadius on 7/7/18.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef EQEMU_DATABUCKET_H
|
#ifndef EQEMU_DATABUCKET_H
|
||||||
#define EQEMU_DATABUCKET_H
|
#define EQEMU_DATABUCKET_H
|
||||||
|
|
||||||
@ -12,27 +8,6 @@
|
|||||||
#include "../common/json/json_archive_single_line.h"
|
#include "../common/json/json_archive_single_line.h"
|
||||||
#include "../common/servertalk.h"
|
#include "../common/servertalk.h"
|
||||||
|
|
||||||
enum DataBucketCacheUpdateAction : uint8 {
|
|
||||||
Upsert,
|
|
||||||
Delete
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DataBucketCacheEntry {
|
|
||||||
DataBucketsRepository::DataBuckets e;
|
|
||||||
int64_t updated_time{};
|
|
||||||
DataBucketCacheUpdateAction update_action{};
|
|
||||||
|
|
||||||
template<class Archive>
|
|
||||||
void serialize(Archive &ar)
|
|
||||||
{
|
|
||||||
ar(
|
|
||||||
CEREAL_NVP(e),
|
|
||||||
CEREAL_NVP(updated_time),
|
|
||||||
CEREAL_NVP(update_action)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DataBucketKey {
|
struct DataBucketKey {
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string value;
|
std::string value;
|
||||||
@ -46,14 +21,12 @@ namespace DataBucketLoadType {
|
|||||||
enum Type : uint8 {
|
enum Type : uint8 {
|
||||||
Bot,
|
Bot,
|
||||||
Client,
|
Client,
|
||||||
NPC,
|
|
||||||
MaxType
|
MaxType
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::string Name[Type::MaxType] = {
|
static const std::string Name[Type::MaxType] = {
|
||||||
"Bot",
|
"Bot",
|
||||||
"Client",
|
"Client",
|
||||||
"NPC",
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +41,6 @@ public:
|
|||||||
|
|
||||||
static bool GetDataBuckets(Mob *mob);
|
static bool GetDataBuckets(Mob *mob);
|
||||||
|
|
||||||
static int64_t GetCurrentTimeUNIX();
|
|
||||||
|
|
||||||
// scoped bucket methods
|
// scoped bucket methods
|
||||||
static void SetData(const DataBucketKey &k);
|
static void SetData(const DataBucketKey &k);
|
||||||
static bool DeleteData(const DataBucketKey &k);
|
static bool DeleteData(const DataBucketKey &k);
|
||||||
@ -80,15 +51,15 @@ public:
|
|||||||
|
|
||||||
// bucket repository versus key matching
|
// bucket repository versus key matching
|
||||||
static bool CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe, const DataBucketKey &k);
|
static bool CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe, const DataBucketKey &k);
|
||||||
static bool ExistsInCache(const DataBucketsRepository::DataBuckets &e);
|
static bool ExistsInCache(const DataBucketsRepository::DataBuckets &entry);
|
||||||
|
|
||||||
static void BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32> ids);
|
static void BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector<uint32> ids);
|
||||||
static void DeleteCachedBuckets(DataBucketLoadType::Type t, uint32 id);
|
static void DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id);
|
||||||
|
|
||||||
static bool SendDataBucketCacheUpdate(const DataBucketCacheEntry &e);
|
|
||||||
static void HandleWorldMessage(ServerPacket *p);
|
|
||||||
static void DeleteFromMissesCache(DataBucketsRepository::DataBuckets e);
|
static void DeleteFromMissesCache(DataBucketsRepository::DataBuckets e);
|
||||||
static void ClearCache();
|
static void ClearCache();
|
||||||
|
static void DeleteFromCache(uint64 id, DataBucketLoadType::Type type);
|
||||||
|
static bool CanCache(const DataBucketKey &key);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_DATABUCKET_H
|
#endif //EQEMU_DATABUCKET_H
|
||||||
|
|||||||
20
zone/mob.cpp
20
zone/mob.cpp
@ -572,6 +572,8 @@ Mob::~Mob()
|
|||||||
|
|
||||||
m_close_mobs.clear();
|
m_close_mobs.clear();
|
||||||
|
|
||||||
|
ClearDataBucketCache();
|
||||||
|
|
||||||
LeaveHealRotationTargetPool();
|
LeaveHealRotationTargetPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8606,3 +8608,21 @@ std::unordered_map<uint16, Mob *> &Mob::GetCloseMobList(float distance)
|
|||||||
{
|
{
|
||||||
return entity_list.GetCloseMobList(this, distance);
|
return entity_list.GetCloseMobList(this, distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mob::ClearDataBucketCache()
|
||||||
|
{
|
||||||
|
if (IsOfClientBot()) {
|
||||||
|
uint64 id = 0;
|
||||||
|
DataBucketLoadType::Type t{};
|
||||||
|
if (IsBot()) {
|
||||||
|
id = CastToBot()->GetBotID();
|
||||||
|
t = DataBucketLoadType::Bot;
|
||||||
|
}
|
||||||
|
else if (IsClient()) {
|
||||||
|
id = CastToClient()->CharacterID();
|
||||||
|
t = DataBucketLoadType::Client;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBucket::DeleteFromCache(id, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1491,6 +1491,8 @@ public:
|
|||||||
std::unordered_map<uint16, Mob *> &GetCloseMobList(float distance = 0.0f);
|
std::unordered_map<uint16, Mob *> &GetCloseMobList(float distance = 0.0f);
|
||||||
void CheckScanCloseMobsMovingTimer();
|
void CheckScanCloseMobsMovingTimer();
|
||||||
|
|
||||||
|
void ClearDataBucketCache();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None);
|
void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None);
|
||||||
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
|
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
|
||||||
|
|||||||
@ -3644,11 +3644,6 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
|||||||
SharedTaskZoneMessaging::HandleWorldMessage(pack);
|
SharedTaskZoneMessaging::HandleWorldMessage(pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ServerOP_DataBucketCacheUpdate:
|
|
||||||
{
|
|
||||||
DataBucket::HandleWorldMessage(pack);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ServerOP_GuildTributeUpdate: {
|
case ServerOP_GuildTributeUpdate: {
|
||||||
GuildTributeUpdate* in = (GuildTributeUpdate*)pack->pBuffer;
|
GuildTributeUpdate* in = (GuildTributeUpdate*)pack->pBuffer;
|
||||||
|
|
||||||
|
|||||||
@ -1923,8 +1923,6 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DataBucket::BulkLoadEntities(DataBucketLoadType::NPC, npc_ids);
|
|
||||||
|
|
||||||
if (!npc_faction_ids.empty()) {
|
if (!npc_faction_ids.empty()) {
|
||||||
zone->LoadNPCFactions(npc_faction_ids);
|
zone->LoadNPCFactions(npc_faction_ids);
|
||||||
zone->LoadNPCFactionAssociations(npc_faction_ids);
|
zone->LoadNPCFactionAssociations(npc_faction_ids);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user