Merge branch 'master' into bot-rewrite

This commit is contained in:
nytmyr
2025-01-22 15:56:37 -06:00
51 changed files with 1402 additions and 288 deletions
+1
View File
@@ -68,3 +68,4 @@ compile_flags.txt
# CMake Files
cmake-build-relwithdebinfo/*
skill-caps.diff
+1
View File
@@ -99,6 +99,7 @@ SET(common_sources
json/json.hpp
json/jsoncpp.cpp
zone_store.cpp
memory/ksm.hpp
net/console_server.cpp
net/console_server_connection.cpp
net/crc32.cpp
+7
View File
@@ -50,6 +50,7 @@
#include "../common/repositories/raid_members_repository.h"
#include "../common/repositories/reports_repository.h"
#include "../common/repositories/variables_repository.h"
#include "../common/repositories/character_pet_name_repository.h"
#include "../common/events/player_event_logs.h"
// Disgrace: for windows compile
@@ -313,6 +314,12 @@ bool Database::ReserveName(uint32 account_id, const std::string& name)
return false;
}
const auto& p = CharacterPetNameRepository::GetWhere(*this, where_filter);
if (!p.empty()) {
LogInfo("Account [{}] requested name [{}] but name is already taken by an Pet", account_id, name);
return false;
}
auto e = CharacterDataRepository::NewEntity();
e.account_id = account_id;
+11 -1
View File
@@ -169,7 +169,10 @@ bool DatabaseUpdate::UpdateManifest(
LogSys.EnableMySQLErrorLogs();
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
if (!missing_migrations.empty()) {
if (!missing_migrations.empty() && m_skip_backup) {
LogInfo("Skipping database backup");
}
else if (!missing_migrations.empty()) {
LogInfo("Automatically backing up database before applying updates");
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
auto s = DatabaseDumpService();
@@ -271,6 +274,13 @@ DatabaseUpdate *DatabaseUpdate::SetContentDatabase(Database *db)
return this;
}
DatabaseUpdate *DatabaseUpdate::SetSkipBackup(bool skip)
{
m_skip_backup = skip;
return this;
}
bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
{
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
+3
View File
@@ -29,12 +29,15 @@ public:
DatabaseUpdate *SetDatabase(Database *db);
DatabaseUpdate *SetContentDatabase(Database *db);
DatabaseUpdate *SetSkipBackup(bool skip);
bool HasPendingUpdates();
private:
bool m_skip_backup = false;
Database *m_database;
Database *m_content_database;
static bool CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b);
void InjectBotsVersionColumn();
};
#endif //EQEMU_DATABASE_UPDATE_H
+44 -1
View File
@@ -6289,7 +6289,50 @@ INSERT INTO `items_evolving_details` VALUES
)",
.content_schema_update = true
}
},
ManifestEntry{
.version = 9291,
.description = "2025_01_21_add_remove_zone_fields",
.check = "SHOW COLUMNS FROM `zone` LIKE 'client_update_range'",
.condition = "empty",
.match = "",
.sql = R"(
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,
},
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
},
ManifestEntry{
.version = 9293,
.description = "2025_01_10_create_pet_names_table.sql",
.check = "SHOW TABLES LIKE 'character_pet_name'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `character_pet_name` (
`character_id` INT(11) NOT NULL PRIMARY KEY,
`name` VARCHAR(64) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
)",
},
// -- template; copy/paste this when you need to create a new entry
// ManifestEntry{
// .version = 9228,
+1
View File
@@ -64,6 +64,7 @@ namespace DatabaseSchema {
{"character_pet_buffs", "char_id"},
{"character_pet_info", "char_id"},
{"character_pet_inventory", "char_id"},
{"character_pet_name", "character_id"},
{"character_peqzone_flags", "id"},
{"character_potionbelt", "id"},
{"character_skills", "id"},
+3
View File
@@ -77,6 +77,7 @@ N(OP_CashReward),
N(OP_CastSpell),
N(OP_ChangeSize),
N(OP_ChannelMessage),
N(OP_ChangePetName),
N(OP_CharacterCreate),
N(OP_CharacterCreateRequest),
N(OP_CharInventory),
@@ -284,6 +285,8 @@ N(OP_InspectMessageUpdate),
N(OP_InspectRequest),
N(OP_InstillDoubt),
N(OP_InterruptCast),
N(OP_InvokeChangePetName),
N(OP_InvokeChangePetNameImmediate),
N(OP_ItemLinkClick),
N(OP_ItemLinkResponse),
N(OP_ItemLinkText),
+15
View File
@@ -5819,6 +5819,21 @@ struct ChangeSize_Struct
/*16*/
};
struct ChangePetName_Struct {
/*00*/ char new_pet_name[64];
/*40*/ char pet_owner_name[64];
/*80*/ int response_code;
};
enum ChangePetNameResponse : int {
Denied = 0, // 5167 You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name.
Accepted = 1, // 5976 Your request for a name change was successful.
Timeout = -3, // 5979 You must wait longer before submitting another name request. Please try again in a few minutes.
NotEligible = -4, // 5980 Your character is not eligible for a name change.
Pending = -5, // 5193 You already have a name change pending. Please wait until it is fully processed before attempting another name change.
Unhandled = -1
};
// New OpCode/Struct for SoD+
struct GroupMakeLeader_Struct
{
+4
View File
@@ -143,6 +143,8 @@ namespace Logs {
Corpses,
XTargets,
EvolveItem,
PositionUpdate,
KSM,
BotSettings,
BotSpellChecks,
BotSpellTypeChecks,
@@ -249,6 +251,8 @@ namespace Logs {
"Corpses",
"XTargets",
"EvolveItem",
"PositionUpdate",
"KSM" // Kernel Samepage Merging
"Bot Settings",
"Bot Spell Checks",
"Bot Spell Type Checks",
+20
View File
@@ -854,6 +854,26 @@
OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogPositionUpdate(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::General, Logs::PositionUpdate))\
OutF(LogSys, Logs::General, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogPositionUpdateDetail(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::Detail, Logs::PositionUpdate))\
OutF(LogSys, Logs::Detail, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__); \
} while (0)
#define LogKSM(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::General, Logs::KSM))\
OutF(LogSys, Logs::General, Logs::KSM, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogKSMDetail(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::Detail, Logs::KSM))\
OutF(LogSys, Logs::Detail, Logs::KSM, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogBotSettings(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::General, Logs::BotSettings))\
OutF(LogSys, Logs::General, Logs::BotSettings, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
+220
View File
@@ -0,0 +1,220 @@
#ifndef EQEMU_KSM_HPP
#define EQEMU_KSM_HPP
#include "../eqemu_logsys.h"
#include <iostream>
#include <vector>
#include <cstring>
#ifdef _WIN32
#include <malloc.h> // For _aligned_malloc, _aligned_free
#include <windows.h>
#else
#include <sys/mman.h> // For madvise
#include <unistd.h> // For sysconf, sbrk
#endif
// Page-aligned allocator for std::vector
template <typename T>
class PageAlignedAllocator {
public:
using value_type = T;
PageAlignedAllocator() noexcept = default;
template <typename U>
PageAlignedAllocator(const PageAlignedAllocator<U>&) noexcept {}
T* allocate(std::size_t n) {
void* ptr = nullptr;
size_t size = n * sizeof(T);
#ifdef _WIN32
// Simply allocate memory without alignment
ptr = malloc(size);
if (!ptr) throw std::bad_alloc();
#else
size_t alignment = getPageSize(); // Get the system's page size
if (posix_memalign(&ptr, alignment, size) != 0) {
throw std::bad_alloc();
}
#endif
return static_cast<T*>(ptr);
}
void deallocate(T* p, std::size_t) noexcept {
free(p);
}
private:
size_t getPageSize() const
{
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.dwPageSize; // Page size in bytes
#else
return static_cast<size_t>(sysconf(_SC_PAGESIZE));
#endif
};
};
template <typename T, typename U>
bool operator==(const PageAlignedAllocator<T>&, const PageAlignedAllocator<U>&) noexcept {
return true;
}
template <typename T, typename U>
bool operator!=(const PageAlignedAllocator<T>&, const PageAlignedAllocator<U>&) noexcept {
return false;
}
// Kernel Samepage Merging (KSM) functionality
namespace KSM {
#ifdef _WIN32
// Windows-specific placeholder functions (no-op)
inline void CheckPageAlignment(void* ptr) {
}
inline void* AllocatePageAligned(size_t size) {
return memset(malloc(size), 0, size);
}
inline void MarkMemoryForKSM(void* start, size_t size) {
}
inline void AlignHeapToPageBoundary() {
}
inline void* MarkHeapStart() {
return nullptr;
}
inline size_t MeasureHeapUsage(void* start) {
return 0;
}
#else
// Linux-specific functionality
inline void CheckPageAlignment(void* ptr) {
size_t page_size = sysconf(_SC_PAGESIZE);
if (reinterpret_cast<uintptr_t>(ptr) % page_size == 0) {
LogKSMDetail("Memory is page-aligned [{}]", ptr);
} else {
LogKSMDetail("Memory is NOT page-aligned [{}]", ptr);
}
}
inline void* AllocatePageAligned(size_t size) {
size_t page_size = sysconf(_SC_PAGESIZE);
void* aligned_ptr = nullptr;
if (posix_memalign(&aligned_ptr, page_size, size) != 0) {
LogKSM("Failed to allocate page-aligned memory on Linux. page_size [{}] size [{}] bytes", page_size, size);
}
std::memset(aligned_ptr, 0, size);
return aligned_ptr;
}
inline void MarkMemoryForKSM(void* start, size_t size) {
if (madvise(start, size, MADV_MERGEABLE) == 0) {
LogKSM("Marked memory for KSM | start [{}] size [{}] bytes", start, size);
} else {
perror("madvise failed");
}
}
inline void AlignHeapToPageBoundary() {
size_t page_size = sysconf(_SC_PAGESIZE);
if (page_size == 0) {
LogKSM("Failed to retrieve page size SC_PAGESIZE [{}]", page_size);
return;
}
void* current_break = sbrk(0);
if (current_break == (void*)-1) {
LogKSM("Failed to retrieve the current program break");
return;
}
uintptr_t current_address = reinterpret_cast<uintptr_t>(current_break);
size_t misalignment = current_address % page_size;
if (misalignment != 0) {
size_t adjustment = page_size - misalignment;
if (sbrk(adjustment) == (void*)-1) {
LogKSM("Failed to align heap to page boundary. adjustment [{}] bytes", adjustment);
return;
}
}
LogKSMDetail("Heap aligned to next page boundary. Current break [{}]", sbrk(0));
}
inline void* MarkHeapStart() {
void* current_pos = sbrk(0);
AlignHeapToPageBoundary();
return current_pos;
}
inline size_t MeasureHeapUsage(void* start) {
void* current_break = sbrk(0);
return static_cast<char*>(current_break) - static_cast<char*>(start);
}
#endif
inline size_t getPageSize()
{
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.dwPageSize; // Page size in bytes
#else
return static_cast<size_t>(sysconf(_SC_PAGESIZE)); // POSIX page size
#endif
};
template <typename T>
inline void PageAlignVectorAligned(std::vector<T, PageAlignedAllocator<T>>& vec) {
if (vec.empty()) {
return;
}
size_t page_size = getPageSize();
void* start = vec.data();
size_t size = vec.size() * sizeof(T);
// Check if the memory is page-aligned
if (reinterpret_cast<std::uintptr_t>(start) % page_size != 0) {
// Allocate a new aligned vector
std::vector<T, PageAlignedAllocator<T>> aligned_vec(vec.get_allocator());
aligned_vec.reserve(vec.capacity()); // Match capacity to avoid reallocation during copy
// Copy elements from the original vector
aligned_vec.insert(aligned_vec.end(), vec.begin(), vec.end());
// Swap the aligned vector with the original vector
vec.swap(aligned_vec);
// Clear the temporary aligned vector to free its memory
aligned_vec.clear();
// Verify the new alignment
start = vec.data();
if (reinterpret_cast<std::uintptr_t>(start) % page_size != 0) {
throw std::runtime_error("Failed to align vector memory to page boundaries.");
}
LogKSMDetail("Vector reallocated to ensure page alignment. start [{}] size [{}] bytes", start, size);
} else {
LogKSMDetail("Vector is already page-aligned. start [{}] size [{}] bytes", start, size);
}
#ifndef _WIN32
// Mark memory for KSM (only on non-Windows systems)
MarkMemoryForKSM(start, size);
#endif
}
}
#endif // EQEMU_KSM_HPP
@@ -0,0 +1,392 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_PET_NAME_REPOSITORY_H
#define EQEMU_BASE_CHARACTER_PET_NAME_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseCharacterPetNameRepository {
public:
struct CharacterPetName {
int32_t character_id;
std::string name;
};
static std::string PrimaryKey()
{
return std::string("character_id");
}
static std::vector<std::string> Columns()
{
return {
"character_id",
"name",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"character_id",
"name",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("character_pet_name");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static CharacterPetName NewEntity()
{
CharacterPetName e{};
e.character_id = 0;
e.name = "";
return e;
}
static CharacterPetName GetCharacterPetName(
const std::vector<CharacterPetName> &character_pet_names,
int character_pet_name_id
)
{
for (auto &character_pet_name : character_pet_names) {
if (character_pet_name.character_id == character_pet_name_id) {
return character_pet_name;
}
}
return NewEntity();
}
static CharacterPetName FindOne(
Database& db,
int character_pet_name_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_pet_name_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
CharacterPetName e{};
e.character_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.name = row[1] ? row[1] : "";
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int character_pet_name_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
character_pet_name_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const CharacterPetName &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.character_id));
v.push_back(columns[1] + " = '" + Strings::Escape(e.name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.character_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static CharacterPetName InsertOne(
Database& db,
CharacterPetName e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.character_id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<CharacterPetName> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<CharacterPetName> All(Database& db)
{
std::vector<CharacterPetName> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterPetName e{};
e.character_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.name = row[1] ? row[1] : "";
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<CharacterPetName> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<CharacterPetName> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterPetName e{};
e.character_id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.name = row[1] ? row[1] : "";
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterPetName &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterPetName> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_PET_NAME_REPOSITORY_H
@@ -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));
+12 -12
View File
@@ -111,7 +111,7 @@ public:
int32_t fast_regen_mana;
int32_t fast_regen_endurance;
int32_t npc_max_aggro_dist;
uint32_t max_movement_update_range;
uint32_t client_update_range;
int32_t underworld_teleport_index;
int32_t lava_damage;
int32_t min_lava_damage;
@@ -220,7 +220,7 @@ public:
"fast_regen_mana",
"fast_regen_endurance",
"npc_max_aggro_dist",
"max_movement_update_range",
"client_update_range",
"underworld_teleport_index",
"lava_damage",
"min_lava_damage",
@@ -325,7 +325,7 @@ public:
"fast_regen_mana",
"fast_regen_endurance",
"npc_max_aggro_dist",
"max_movement_update_range",
"client_update_range",
"underworld_teleport_index",
"lava_damage",
"min_lava_damage",
@@ -464,7 +464,7 @@ public:
e.fast_regen_mana = 180;
e.fast_regen_endurance = 180;
e.npc_max_aggro_dist = 600;
e.max_movement_update_range = 600;
e.client_update_range = 600;
e.underworld_teleport_index = 0;
e.lava_damage = 50;
e.min_lava_damage = 10;
@@ -599,7 +599,7 @@ public:
e.fast_regen_mana = row[89] ? static_cast<int32_t>(atoi(row[89])) : 180;
e.fast_regen_endurance = row[90] ? static_cast<int32_t>(atoi(row[90])) : 180;
e.npc_max_aggro_dist = row[91] ? static_cast<int32_t>(atoi(row[91])) : 600;
e.max_movement_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.client_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.underworld_teleport_index = row[93] ? static_cast<int32_t>(atoi(row[93])) : 0;
e.lava_damage = row[94] ? static_cast<int32_t>(atoi(row[94])) : 50;
e.min_lava_damage = row[95] ? static_cast<int32_t>(atoi(row[95])) : 10;
@@ -730,7 +730,7 @@ public:
v.push_back(columns[89] + " = " + std::to_string(e.fast_regen_mana));
v.push_back(columns[90] + " = " + std::to_string(e.fast_regen_endurance));
v.push_back(columns[91] + " = " + std::to_string(e.npc_max_aggro_dist));
v.push_back(columns[92] + " = " + std::to_string(e.max_movement_update_range));
v.push_back(columns[92] + " = " + std::to_string(e.client_update_range));
v.push_back(columns[93] + " = " + std::to_string(e.underworld_teleport_index));
v.push_back(columns[94] + " = " + std::to_string(e.lava_damage));
v.push_back(columns[95] + " = " + std::to_string(e.min_lava_damage));
@@ -850,7 +850,7 @@ public:
v.push_back(std::to_string(e.fast_regen_mana));
v.push_back(std::to_string(e.fast_regen_endurance));
v.push_back(std::to_string(e.npc_max_aggro_dist));
v.push_back(std::to_string(e.max_movement_update_range));
v.push_back(std::to_string(e.client_update_range));
v.push_back(std::to_string(e.underworld_teleport_index));
v.push_back(std::to_string(e.lava_damage));
v.push_back(std::to_string(e.min_lava_damage));
@@ -978,7 +978,7 @@ public:
v.push_back(std::to_string(e.fast_regen_mana));
v.push_back(std::to_string(e.fast_regen_endurance));
v.push_back(std::to_string(e.npc_max_aggro_dist));
v.push_back(std::to_string(e.max_movement_update_range));
v.push_back(std::to_string(e.client_update_range));
v.push_back(std::to_string(e.underworld_teleport_index));
v.push_back(std::to_string(e.lava_damage));
v.push_back(std::to_string(e.min_lava_damage));
@@ -1110,7 +1110,7 @@ public:
e.fast_regen_mana = row[89] ? static_cast<int32_t>(atoi(row[89])) : 180;
e.fast_regen_endurance = row[90] ? static_cast<int32_t>(atoi(row[90])) : 180;
e.npc_max_aggro_dist = row[91] ? static_cast<int32_t>(atoi(row[91])) : 600;
e.max_movement_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.client_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.underworld_teleport_index = row[93] ? static_cast<int32_t>(atoi(row[93])) : 0;
e.lava_damage = row[94] ? static_cast<int32_t>(atoi(row[94])) : 50;
e.min_lava_damage = row[95] ? static_cast<int32_t>(atoi(row[95])) : 10;
@@ -1233,7 +1233,7 @@ public:
e.fast_regen_mana = row[89] ? static_cast<int32_t>(atoi(row[89])) : 180;
e.fast_regen_endurance = row[90] ? static_cast<int32_t>(atoi(row[90])) : 180;
e.npc_max_aggro_dist = row[91] ? static_cast<int32_t>(atoi(row[91])) : 600;
e.max_movement_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.client_update_range = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 600;
e.underworld_teleport_index = row[93] ? static_cast<int32_t>(atoi(row[93])) : 0;
e.lava_damage = row[94] ? static_cast<int32_t>(atoi(row[94])) : 50;
e.min_lava_damage = row[95] ? static_cast<int32_t>(atoi(row[95])) : 10;
@@ -1406,7 +1406,7 @@ public:
v.push_back(std::to_string(e.fast_regen_mana));
v.push_back(std::to_string(e.fast_regen_endurance));
v.push_back(std::to_string(e.npc_max_aggro_dist));
v.push_back(std::to_string(e.max_movement_update_range));
v.push_back(std::to_string(e.client_update_range));
v.push_back(std::to_string(e.underworld_teleport_index));
v.push_back(std::to_string(e.lava_damage));
v.push_back(std::to_string(e.min_lava_damage));
@@ -1527,7 +1527,7 @@ public:
v.push_back(std::to_string(e.fast_regen_mana));
v.push_back(std::to_string(e.fast_regen_endurance));
v.push_back(std::to_string(e.npc_max_aggro_dist));
v.push_back(std::to_string(e.max_movement_update_range));
v.push_back(std::to_string(e.client_update_range));
v.push_back(std::to_string(e.underworld_teleport_index));
v.push_back(std::to_string(e.lava_damage));
v.push_back(std::to_string(e.min_lava_damage));
@@ -0,0 +1,13 @@
#ifndef EQEMU_CHARACTER_PET_NAME_REPOSITORY_H
#define EQEMU_CHARACTER_PET_NAME_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_character_pet_name_repository.h"
class CharacterPetNameRepository: public BaseCharacterPetNameRepository {
public:
// Custom extended repository methods here
};
#endif //EQEMU_CHARACTER_PET_NAME_REPOSITORY_H
+1 -1
View File
@@ -661,7 +661,7 @@ bool SharedDatabase::GetInventory(Client *c)
// Retrieve character inventory
auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`", char_id));
auto e_results = CharacterEvolvingItemsRepository::GetWhere(
*this, fmt::format("`char_id` = '{}' AND `deleted_at` IS NULL", char_id)
*this, fmt::format("`character_id` = '{}' AND `deleted_at` IS NULL", char_id)
);
if (results.empty()) {
+3
View File
@@ -86,6 +86,9 @@ struct BenchTimer
void reset() { start_time = clock::now(); }
// this is seconds
double elapsed() { return std::chrono::duration<double> (clock::now() - start_time).count(); }
std::chrono::milliseconds::rep elapsedMilliseconds() { return std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - start_time).count(); }
std::chrono::microseconds::rep elapsedMicroseconds() { return std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start_time).count(); }
std::chrono::nanoseconds::rep elapsedNanoseconds() { return std::chrono::duration_cast<std::chrono::nanoseconds>(clock::now() - start_time).count(); }
private:
std::chrono::time_point<std::chrono::high_resolution_clock> start_time;
};
+1 -1
View File
@@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9290
#define CURRENT_BINARY_DATABASE_VERSION 9293
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
#endif
-6
View File
@@ -676,12 +676,6 @@ int ZoneStore::GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version)
return z ? z->npc_max_aggro_dist : DEFAULT_ZONE_MAX_AGGRO_DISTANCE;
}
uint32 ZoneStore::GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version)
{
const auto& z = GetZoneVersionWithFallback(zone_id, version);
return z ? z->max_movement_update_range : DEFAULT_ZONE_MAX_MOVEMENT_UPDATE_RANGE;
}
int8 ZoneStore::GetZoneMinimumExpansion(uint32 zone_id, int version)
{
const auto& z = GetZoneVersionWithFallback(zone_id, version);
-1
View File
@@ -94,7 +94,6 @@ public:
int GetZoneFastRegenMana(uint32 zone_id, int version = 0);
int GetZoneFastRegenEndurance(uint32 zone_id, int version = 0);
int GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version = 0);
uint32 GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version = 0);
int8 GetZoneMinimumExpansion(uint32 zone_id, int version = 0);
int8 GetZoneMaximumExpansion(uint32 zone_id, int version = 0);
const std::string GetZoneContentFlags(uint32 zone_id, int version = 0);
+9
View File
@@ -735,3 +735,12 @@ OP_PickZone=0xaaba
#evolve item related
OP_EvolveItem=0x7cfb
# This is bugged in ROF2
# OP_InvokeChangePetNameImmediate is supposed to write DisablePetNameChangeReminder=0 to reset the 'nag reminder'
# It actually sets DisableNameChangeReminder=0 (player name change nag reminder). Additionally, clicking the
# 'Don't remind me later' button sets DisableNameChangeReminder=1
# This can be fixed with a client patch.
OP_InvokeChangePetNameImmediate=0x046d
OP_InvokeChangePetName=0x4506
OP_ChangePetName=0x5dab
+3 -3
View File
@@ -11,13 +11,13 @@ void WorldserverCLI::CopyCharacter(int argc, char **argv, argh::parser &cmd, std
"destination_account_name"
};
std::vector<std::string> options = {};
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
if (cmd[{"-h", "--help"}]) {
return;
}
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
std::string source_character_name = cmd(2).str();
std::string destination_character_name = cmd(3).str();
std::string destination_account_name = cmd(4).str();
+2 -2
View File
@@ -24,12 +24,12 @@ void WorldserverCLI::DatabaseDump(int argc, char **argv, argh::parser &cmd, std:
"--compress"
};
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
if (cmd[{"-h", "--help"}]) {
return;
}
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
auto s = new DatabaseDumpService();
bool dump_all = cmd[{"-a", "--all"}];
+5
View File
@@ -4,12 +4,17 @@ void WorldserverCLI::DatabaseUpdates(int argc, char **argv, argh::parser &cmd, s
{
description = "Runs database updates manually";
std::vector<std::string> options = {
"--skip-backup",
};
if (cmd[{"-h", "--help"}]) {
return;
}
DatabaseUpdate update;
update.SetDatabase(&database)
->SetSkipBackup(cmd[{"--skip-backup"}] )
->SetContentDatabase(&content_db)
->CheckDbUpdates();
}
+19 -1
View File
@@ -1,6 +1,8 @@
#include <cereal/archives/json.hpp>
#include <cereal/types/vector.hpp>
#include <iomanip>
#include "../../common/events/player_events.h"
#include "../../common/memory/ksm.hpp"
void WorldserverCLI::TestCommand(int argc, char **argv, argh::parser &cmd, std::string &description)
{
@@ -10,5 +12,21 @@ void WorldserverCLI::TestCommand(int argc, char **argv, argh::parser &cmd, std::
return;
}
void* start_marker = KSM::MarkHeapStart();
std::cout << "Start marker: " << start_marker << "\n";
std::vector<std::string> vec = {};
for (int i = 0; i < 100000; i++) {
vec.push_back("Some random string");
}
// Measure allocated memory size
size_t allocated_size = KSM::MeasureHeapUsage(start_marker);
// Convert to MB as a float and output with precision
double allocated_size_mb = static_cast<double>(allocated_size) / (1024 * 1024);
std::cout << std::fixed << std::setprecision(3)
<< "Allocated size: " << allocated_size_mb << " MB\n";
// Mark memory for KSM
KSM::MarkMemoryForKSM(start_marker, allocated_size);
}
-34
View File
@@ -7549,40 +7549,6 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) {
return;
}
/**
* @param close_mobs
* @param scanning_mob
*/
void EntityList::ScanCloseClientMobs(std::unordered_map<uint16, Mob*>& close_mobs, Mob* scanning_mob)
{
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
close_mobs.clear();
for (auto& e : mob_list) {
auto mob = e.second;
if (!mob->IsClient()) {
continue;
}
if (mob->GetID() <= 0) {
continue;
}
float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition());
if (distance <= scan_range) {
close_mobs.insert(std::pair<uint16, Mob*>(mob->GetID(), mob));
}
else if (mob->GetAggroRange() >= scan_range) {
close_mobs.insert(std::pair<uint16, Mob*>(mob->GetID(), mob));
}
}
LogAIScanCloseDetail("Close Client Mob List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName());
}
uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool include_pets, Raid* raid) {
uint8 need_healed = 0;
+214 -32
View File
@@ -67,6 +67,7 @@ extern volatile bool RunLoops;
#include "../common/repositories/character_spells_repository.h"
#include "../common/repositories/character_disciplines_repository.h"
#include "../common/repositories/character_data_repository.h"
#include "../common/repositories/character_pet_name_repository.h"
#include "../common/repositories/discovered_items_repository.h"
#include "../common/repositories/inventory_repository.h"
#include "../common/repositories/keyring_repository.h"
@@ -159,7 +160,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
endupkeep_timer(1000),
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
m_client_npc_aggro_scan_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)),
m_client_zone_wide_full_position_update_timer(5 * 60 * 1000),
m_client_bulk_npc_pos_update_timer(60 * 1000),
tribute_timer(Tribute_duration),
proximity_timer(ClientProximity_interval),
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
@@ -405,6 +406,7 @@ Client::~Client() {
mMovementManager->RemoveClient(this);
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Account, AccountID());
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Client, CharacterID());
if (RuleB(Bots, Enabled)) {
@@ -1832,7 +1834,7 @@ void Client::FriendsWho(char *FriendsString) {
Message(0, "Error: World server disconnected");
else {
auto pack =
new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString));
new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString));
ServerFriendsWho_Struct* FriendsWho = (ServerFriendsWho_Struct*) pack->pBuffer;
FriendsWho->FromID = GetID();
strcpy(FriendsWho->FromName, GetName());
@@ -2213,7 +2215,7 @@ void Client::Stand() {
}
void Client::Sit() {
SetAppearance(eaSitting, false);
SetAppearance(eaSitting, false);
}
void Client::ChangeLastName(std::string last_name) {
@@ -4396,6 +4398,83 @@ void Client::KeyRingList()
}
}
bool Client::IsPetNameChangeAllowed() {
DataBucketKey k = GetScopedBucketKeys();
k.key = "PetNameChangesAllowed";
auto b = DataBucket::GetData(k);
if (!b.value.empty()) {
return true;
}
return false;
}
void Client::InvokeChangePetName(bool immediate) {
if (!IsPetNameChangeAllowed()) {
return;
}
auto packet_op = immediate ? OP_InvokeChangePetNameImmediate : OP_InvokeChangePetName;
auto outapp = new EQApplicationPacket(packet_op, 0);
QueuePacket(outapp);
safe_delete(outapp);
}
void Client::GrantPetNameChange() {
DataBucketKey k = GetScopedBucketKeys();
k.key = "PetNameChangesAllowed";
k.value = "true";
DataBucket::SetData(k);
InvokeChangePetName(true);
}
void Client::ClearPetNameChange() {
DataBucketKey k = GetScopedBucketKeys();
k.key = "PetNameChangesAllowed";
DataBucket::DeleteData(k);
}
bool Client::ChangePetName(std::string new_name)
{
if (new_name.empty()) {
return false;
}
if (!IsPetNameChangeAllowed()) {
return false;
}
auto pet = GetPet();
if (!pet) {
return false;
}
if (pet->GetName() == new_name) {
return false;
}
if (!database.CheckNameFilter(new_name) || database.IsNameUsed(new_name)) {
return false;
}
CharacterPetNameRepository::ReplaceOne(
database,
CharacterPetNameRepository::CharacterPetName{
.character_id = static_cast<int32_t>(CharacterID()),
.name = new_name
}
);
pet->TempName(new_name.c_str());
ClearPetNameChange();
return true;
}
bool Client::IsDiscovered(uint32 item_id) {
const auto& l = DiscoveredItemsRepository::GetWhere(
database,
@@ -5632,13 +5711,13 @@ bool Client::TryReward(uint32 claim_id)
if (amt == 1) {
query = StringFormat("DELETE FROM account_rewards "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
} else {
query = StringFormat("UPDATE account_rewards SET amount = (amount-1) "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
}
@@ -8371,7 +8450,7 @@ int Client::GetQuiverHaste(int delay)
for (int r = EQ::invslot::GENERAL_BEGIN; r <= EQ::invslot::GENERAL_END; r++) {
pi = GetInv().GetItem(r);
if (pi && pi->IsClassBag() && pi->GetItem()->BagType == EQ::item::BagTypeQuiver &&
pi->GetItem()->BagWR > 0)
pi->GetItem()->BagWR > 0)
break;
if (r == EQ::invslot::GENERAL_END)
// we will get here if we don't find a valid quiver
@@ -9845,7 +9924,7 @@ const ExpeditionLockoutTimer* Client::GetExpeditionLockout(
for (const auto& expedition_lockout : m_expedition_lockouts)
{
if ((include_expired || !expedition_lockout.IsExpired()) &&
expedition_lockout.IsSameLockout(expedition_name, event_name))
expedition_lockout.IsSameLockout(expedition_name, event_name))
{
return &expedition_lockout;
}
@@ -9860,7 +9939,7 @@ std::vector<ExpeditionLockoutTimer> Client::GetExpeditionLockouts(
for (const auto& lockout : m_expedition_lockouts)
{
if ((include_expired || !lockout.IsExpired()) &&
lockout.GetExpeditionName() == expedition_name)
lockout.GetExpeditionName() == expedition_name)
{
lockouts.emplace_back(lockout);
}
@@ -12944,50 +13023,77 @@ void Client::SendTopLevelInventory()
}
}
// On a normal basis we limit mob movement updates based on distance
// This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes
//
// For very large zones we will also force a full update based on distance
//
// We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear
// to full stop when they are actually still pathing
void Client::CheckSendBulkClientPositionUpdate()
void Client::CheckSendBulkNpcPositions()
{
float distance_moved = DistanceNoZ(m_last_position_before_bulk_update, GetPosition());
bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance();
float update_range = RuleI(Range, MobCloseScanDistance);
bool moved_far_enough_before_bulk_update = distance_moved >= update_range;
bool is_ready_to_update = (
m_client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update
m_client_bulk_npc_pos_update_timer.Check() || moved_far_enough_before_bulk_update
);
if (IsMoving() && is_ready_to_update) {
LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName());
int updated_count = 0;
int skipped_count = 0;
if (is_ready_to_update) {
auto &mob_movement_manager = MobMovementManager::Get();
auto &mob_list = entity_list.GetMobList();
for (auto &it : mob_list) {
Mob *entity = it.second;
if (!entity->IsNPC()) {
for (auto &e: entity_list.GetMobList()) {
Mob *mob = e.second;
if (!mob->IsNPC()) {
continue;
}
int animation_speed = 0;
if (entity->IsMoving()) {
if (entity->IsRunning()) {
animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed());
if (mob->IsMoving()) {
if (mob->IsRunning()) {
animation_speed = (mob->IsFeared() ? mob->GetFearSpeed() : mob->GetRunspeed());
}
else {
animation_speed = entity->GetWalkspeed();
animation_speed = mob->GetWalkspeed();
}
}
mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this);
// if we have seen this mob before, and it hasn't moved, skip it
if (m_last_seen_mob_position.contains(mob->GetID())) {
if (m_last_seen_mob_position[mob->GetID()] == mob->GetPosition()) {
LogPositionUpdateDetail(
"Mob [{}] has already been sent to client [{}] at this position, skipping",
mob->GetCleanName(),
GetCleanName()
);
skipped_count++;
continue;
}
}
mob_movement_manager.SendCommandToClients(
mob,
0.0,
0.0,
0.0,
0.0,
animation_speed,
ClientRangeAny,
this
);
updated_count++;
}
LogPositionUpdate(
"[{}] Sent [{}] bulk updated NPC positions, skipped [{}] distance_moved [{}] update_range [{}]",
GetCleanName(),
updated_count,
skipped_count,
distance_moved,
update_range
);
m_last_position_before_bulk_update = GetPosition();
}
}
const uint16 scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval);
const uint16 scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval);
@@ -13278,3 +13384,79 @@ void Client::SetAAEXPPercentage(uint8 percentage)
SendAlternateAdvancementStats();
SendAlternateAdvancementTable();
}
void Client::BroadcastPositionUpdate()
{
EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
PlayerPositionUpdateServer_Struct *spu = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer;
memset(spu, 0x00, sizeof(PlayerPositionUpdateServer_Struct));
spu->spawn_id = GetID();
spu->x_pos = FloatToEQ19(GetX());
spu->y_pos = FloatToEQ19(GetY());
spu->z_pos = FloatToEQ19(GetZ());
spu->heading = FloatToEQ12(GetHeading());
spu->delta_x = FloatToEQ13(0);
spu->delta_y = FloatToEQ13(0);
spu->delta_z = FloatToEQ13(0);
spu->delta_heading = FloatToEQ10(0);
spu->animation = 0;
entity_list.QueueCloseClients(this, &outapp, true, zone->GetClientUpdateRange());
Group *g = GetGroup();
if (g) {
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);
}
+16 -2
View File
@@ -320,6 +320,11 @@ public:
void KeyRingAdd(uint32 item_id);
bool KeyRingCheck(uint32 item_id);
void KeyRingList();
bool IsPetNameChangeAllowed();
void GrantPetNameChange();
void ClearPetNameChange();
void InvokeChangePetName(bool immediate = true);
bool ChangePetName(std::string new_name);
bool IsClient() const override { return true; }
bool IsOfClientBot() const override { return true; }
bool IsOfClientBotMerc() const override { return true; }
@@ -1848,6 +1853,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);
@@ -2107,12 +2119,13 @@ private:
Timer m_client_npc_aggro_scan_timer;
void CheckClientToNpcAggroTimer();
void ClientToNpcAggroProcess();
void BroadcastPositionUpdate();
// bulk position updates
glm::vec4 m_last_position_before_bulk_update;
Timer m_client_zone_wide_full_position_update_timer;
Timer m_client_bulk_npc_pos_update_timer;
Timer m_position_update_timer;
void CheckSendBulkClientPositionUpdate();
void CheckSendBulkNpcPositions();
void BulkSendInventoryItems();
@@ -2300,6 +2313,7 @@ public:
const std::string &GetMailKeyFull() const;
const std::string &GetMailKey() const;
void ShowZoneShardMenu();
void Handle_OP_ChangePetName(const EQApplicationPacket *app);
};
#endif
+38 -1
View File
@@ -66,6 +66,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/repositories/character_corpses_repository.h"
#include "../common/repositories/guild_tributes_repository.h"
#include "../common/repositories/buyer_buy_lines_repository.h"
#include "../common/repositories/character_pet_name_repository.h"
#include "../common/events/player_event_logs.h"
#include "../common/repositories/character_stats_record_repository.h"
@@ -160,6 +161,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade;
ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell;
ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage;
ConnectedOpcodes[OP_ChangePetName] = &Client::Handle_OP_ChangePetName;
ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs;
ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks;
ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname;
@@ -824,6 +826,10 @@ void Client::CompleteConnect()
CharacterID()
)
);
if (IsPetNameChangeAllowed()) {
InvokeChangePetName(false);
}
}
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
@@ -975,6 +981,16 @@ void Client::CompleteConnect()
RecordStats();
AutoGrantAAPoints();
// set initial position for mob tracking
m_last_seen_mob_position.reserve(entity_list.GetMobList().size());
for (auto& mob : entity_list.GetMobList()) {
if (!mob.second->IsNPC()) {
continue;
}
m_last_seen_mob_position[mob.second->GetID()] = mob.second->GetPosition();
}
// enforce some rules..
if (!CanEnterZone()) {
LogInfo("Kicking character [{}] from zone, not allowed here (missing requirements)", GetCleanName());
@@ -4573,6 +4589,27 @@ void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app)
return;
}
void Client::Handle_OP_ChangePetName(const EQApplicationPacket *app) {
if (app->size != sizeof(ChangePetName_Struct)) {
LogError("Got OP_ChangePetName of incorrect size. Expected [{}], got [{}].", sizeof(ChangePetName_Struct), app->size);
return;
}
auto p = (ChangePetName_Struct *) app->pBuffer;
if (!IsPetNameChangeAllowed()) {
p->response_code = ChangePetNameResponse::NotEligible;
QueuePacket(app);
return;
}
p->response_code = ChangePetNameResponse::Denied;
if (ChangePetName(p->new_pet_name)) {
p->response_code = ChangePetNameResponse::Accepted;
}
QueuePacket(app);
}
void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app)
{
if (!RuleB(Spells, EnableBlockedBuffs))
@@ -4971,7 +5008,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
CheckScanCloseMobsMovingTimer();
}
CheckSendBulkClientPositionUpdate();
CheckSendBulkNpcPositions();
int32 new_animation = ppu->animation;
+1 -1
View File
@@ -122,7 +122,7 @@ bool Client::Process() {
/* I haven't naturally updated my position in 10 seconds, updating manually */
if (!IsMoving() && m_position_update_timer.Check()) {
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
BroadcastPositionUpdate();
}
if (mana_timer.Check())
+37 -12
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;
}
+6 -3
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",
};
}
-12
View File
@@ -5653,16 +5653,6 @@ int Perl__GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version)
return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, version);
}
uint32 Perl__GetZoneMaximumMovementUpdateRange(uint32 zone_id)
{
return zone_store.GetZoneMaximumMovementUpdateRange(zone_id);
}
uint32 Perl__GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version)
{
return zone_store.GetZoneMaximumMovementUpdateRange(zone_id, version);
}
int8 Perl__GetZoneMinimumExpansion(uint32 zone_id)
{
return zone_store.GetZoneMinimumExpansion(zone_id);
@@ -6112,8 +6102,6 @@ void perl_register_quest()
package.add("GetZoneMaximumExpansion", (int8(*)(uint32, int))&Perl__GetZoneMaximumExpansion);
package.add("GetZoneMaximumLevel", (uint8(*)(uint32))&Perl__GetZoneMaximumLevel);
package.add("GetZoneMaximumLevel", (uint8(*)(uint32, int))&Perl__GetZoneMaximumLevel);
package.add("GetZoneMaximumMovementUpdateRange", (uint32(*)(uint32))&Perl__GetZoneMaximumMovementUpdateRange);
package.add("GetZoneMaximumMovementUpdateRange", (uint32(*)(uint32, int))&Perl__GetZoneMaximumMovementUpdateRange);
package.add("GetZoneMaximumPlayers", (int(*)(uint32))&Perl__GetZoneMaximumPlayers);
package.add("GetZoneMaximumPlayers", (int(*)(uint32, int))&Perl__GetZoneMaximumPlayers);
package.add("GetZoneMinimumClip", (float(*)(uint32))&Perl__GetZoneMinimumClip);
+13 -18
View File
@@ -1717,15 +1717,6 @@ void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *a
}
}
/**
* @param sender
* @param app
* @param ignore_sender
* @param distance
* @param skipped_mob
* @param is_ack_required
* @param filter
*/
void EntityList::QueueCloseClients(
Mob *sender,
const EQApplicationPacket *app,
@@ -1742,7 +1733,7 @@ void EntityList::QueueCloseClients(
}
if (distance <= 0) {
distance = 600;
distance = zone->GetClientUpdateRange();
}
float distance_squared = distance * distance;
@@ -2871,6 +2862,8 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob)
);
it->second->m_close_mobs.erase(entity_id);
it->second->m_last_seen_mob_position.erase(entity_id);
++it;
}
@@ -2924,6 +2917,9 @@ void EntityList::RemoveAuraFromMobs(Mob *aura)
// All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude
// less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire
// entity list (zone wide)
BenchTimer g_scan_bench_timer;
void EntityList::ScanCloseMobs(Mob *scanning_mob)
{
if (!scanning_mob) {
@@ -2934,7 +2930,9 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
return;
}
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
g_scan_bench_timer.reset();
float scan_range = RuleI(Range, MobCloseScanDistance);
// Reserve memory in m_close_mobs to avoid frequent re-allocations if not already reserved.
// Assuming mob_list.size() as an upper bound for reservation.
@@ -2951,7 +2949,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
continue;
}
float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition());
float distance = Distance(scanning_mob->GetPosition(), mob->GetPosition());
if (distance <= scan_range || mob->GetAggroRange() >= scan_range) {
// add mob to scanning_mob's close list and vice versa
// check if the mob is already in the close mobs list before inserting
@@ -2963,10 +2961,11 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
}
LogAIScanClose(
"[{}] Scanning close list > list_size [{}] moving [{}]",
"[{}] Scanning close list > list_size [{}] moving [{}] elapsed [{}] us",
scanning_mob->GetCleanName(),
scanning_mob->m_close_mobs.size(),
scanning_mob->IsMoving() ? "true" : "false"
scanning_mob->IsMoving() ? "true" : "false",
g_scan_bench_timer.elapsedMicroseconds()
);
}
@@ -5752,10 +5751,6 @@ void EntityList::ReloadMerchants() {
* then we return the full list
*
* See comments @EntityList::ScanCloseMobs for system explanation
*
* @param mob
* @param distance
* @return
*/
std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance)
{
-2
View File
@@ -630,8 +630,6 @@ private:
void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff
void ScanCloseClientMobs(std::unordered_map<uint16, Mob*>& close_mobs, Mob* scanning_mob);
void GetBotList(std::list<Bot*> &b_list);
private:
std::unordered_map<uint16, Bot*> bot_list;
+13
View File
@@ -9,6 +9,19 @@ void command_loc(Client *c, const Seperator *sep)
auto target_position = target->GetPosition();
// check los benchmark
BenchTimer timer;
for (int i = 0; i < 1000; i++) {
zone->zonemap->CheckLoS(c->GetPosition(), target_position);
}
c->Message(
Chat::White,
fmt::format(
"CheckLoS benchmark took [{}]",
timer.elapsed()
).c_str()
);
c->Message(
Chat::White,
fmt::format(
+49
View File
@@ -3458,12 +3458,54 @@ void Lua_Client::ShowZoneShardMenu()
self->ShowZoneShardMenu();
}
void Lua_Client::GrantPetNameChange()
{
Lua_Safe_Call_Void();
self->GrantPetNameChange();
}
void Lua_Client::SetAAEXPPercentage(uint8 percentage)
{
Lua_Safe_Call_Void();
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<>())
@@ -3532,6 +3574,7 @@ luabind::scope lua_register_client() {
.def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill)
.def("CashReward", &Lua_Client::CashReward)
.def("ChangeLastName", (void(Lua_Client::*)(std::string))&Lua_Client::ChangeLastName)
.def("GrantPetNameChange", &Lua_Client::GrantPetNameChange)
.def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID)
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob))&Lua_Client::CheckIncreaseSkill)
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill)
@@ -3552,6 +3595,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 +3674,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 +3969,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)
+9
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);
@@ -589,6 +597,7 @@ public:
bool ReloadDataBuckets();
void ShowZoneShardMenu();
void GrantPetNameChange();
Lua_Expedition CreateExpedition(luabind::object expedition_info);
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players);
-12
View File
@@ -4668,16 +4668,6 @@ int lua_get_zone_npc_maximum_aggro_distance(uint32 zone_id, int version)
return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, version);
}
uint32 lua_get_zone_maximum_movement_update_range(uint32 zone_id)
{
return zone_store.GetZoneMaximumMovementUpdateRange(zone_id);
}
uint32 lua_get_zone_maximum_movement_update_range(uint32 zone_id, int version)
{
return zone_store.GetZoneMaximumMovementUpdateRange(zone_id, version);
}
int8 lua_get_zone_minimum_expansion(uint32 zone_id)
{
return zone_store.GetZoneMinimumExpansion(zone_id);
@@ -6287,8 +6277,6 @@ luabind::scope lua_register_general() {
luabind::def("get_zone_fast_regen_endurance", (int(*)(uint32,int))&lua_get_zone_fast_regen_endurance),
luabind::def("get_zone_npc_maximum_aggro_distance", (int(*)(uint32))&lua_get_zone_npc_maximum_aggro_distance),
luabind::def("get_zone_npc_maximum_aggro_distance", (int(*)(uint32,int))&lua_get_zone_npc_maximum_aggro_distance),
luabind::def("get_zone_npc_maximum_movement_update_range", (uint32(*)(uint32))&lua_get_zone_maximum_movement_update_range),
luabind::def("get_zone_npc_maximum_movement_update_range", (uint32(*)(uint32,int))&lua_get_zone_maximum_movement_update_range),
luabind::def("get_zone_minimum_expansion", (int8(*)(uint32))&lua_get_zone_minimum_expansion),
luabind::def("get_zone_minimum_expansion", (int8(*)(uint32,int))&lua_get_zone_minimum_expansion),
luabind::def("get_zone_maximum_expansion", (int8(*)(uint32))&lua_get_zone_maximum_expansion),
+2
View File
@@ -7,6 +7,7 @@
#include "raycast_mesh.h"
#include "zone.h"
#include "../common/file.h"
#include "../common/memory/ksm.hpp"
#include <algorithm>
#include <map>
@@ -953,6 +954,7 @@ bool Map::LoadV2(FILE *f) {
return true;
}
void Map::RotateVertex(glm::vec3 &v, float rx, float ry, float rz) {
glm::vec3 nv = v;
+4 -3
View File
@@ -129,6 +129,7 @@ Mob::Mob(
position_update_melee_push_timer(500),
hate_list_cleanup_timer(6000),
m_scan_close_mobs_timer(6000),
m_see_close_mobs_timer(1000),
m_mob_check_moving_timer(1000),
bot_attack_flag_timer(10000)
{
@@ -8621,15 +8622,15 @@ std::unordered_map<uint16, Mob*>& Mob::GetCloseMobList(float distance)
void Mob::ClearDataBucketCache()
{
if (IsOfClientBot()) {
uint64 id = 0;
uint64 id = 0;
DataBucketLoadType::Type t{};
if (IsBot()) {
id = CastToBot()->GetBotID();
t = DataBucketLoadType::Bot;
t = DataBucketLoadType::Bot;
}
else if (IsClient()) {
id = CastToClient()->CharacterID();
t = DataBucketLoadType::Client;
t = DataBucketLoadType::Client;
}
DataBucket::DeleteFromCache(id, t);
+5 -3
View File
@@ -223,9 +223,11 @@ public:
void DisplayInfo(Mob *mob);
std::unordered_map<uint16, Mob *> m_close_mobs;
Timer m_scan_close_mobs_timer;
Timer m_mob_check_moving_timer;
std::unordered_map<uint16, Mob *> m_close_mobs;
std::unordered_map<int, glm::vec4> m_last_seen_mob_position;
Timer m_scan_close_mobs_timer;
Timer m_see_close_mobs_timer;
Timer m_mob_check_moving_timer;
// Bot attack flag
Timer bot_attack_flag_timer;
+25 -1
View File
@@ -851,12 +851,24 @@ void MobMovementManager::SendCommandToClients(
_impl->Stats.TotalSentPosition++;
}
if (c->m_last_seen_mob_position.contains(mob->GetID())) {
if (c->m_last_seen_mob_position[mob->GetID()] == mob->GetPosition() && anim == 0) {
LogPositionUpdate(
"Mob [{}] has already been sent to client [{}] at this position, skipping",
mob->GetCleanName(),
c->GetCleanName()
);
continue;
}
}
c->QueuePacket(&outapp, false);
c->m_last_seen_mob_position[mob->GetID()] = mob->GetPosition();
}
}
else {
float short_range = RuleR(Pathing, ShortMovementUpdateRange);
float long_range = zone->GetNpcPositionUpdateDistance();
float long_range = RuleI(Range, MobCloseScanDistance);
for (auto &c : _impl->Clients) {
if (single_client && c != single_client) {
@@ -901,7 +913,19 @@ void MobMovementManager::SendCommandToClients(
_impl->Stats.TotalSentPosition++;
}
if (c->m_last_seen_mob_position.contains(mob->GetID())) {
if (c->m_last_seen_mob_position[mob->GetID()] == mob->GetPosition() && anim == 0) {
LogPositionUpdate(
"Mob [{}] has already been sent to client [{}] at this position, skipping",
mob->GetCleanName(),
c->GetCleanName()
);
continue;
}
}
c->QueuePacket(&outapp, false);
c->m_last_seen_mob_position[mob->GetID()] = mob->GetPosition();
}
}
}
+2 -2
View File
@@ -997,8 +997,8 @@ NPC * NPC::SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4
npc_type->current_hp = 4000000;
npc_type->max_hp = 4000000;
npc_type->race = 2254;
npc_type->gender = 2;
npc_type->race = 127;
npc_type->gender = 0;
npc_type->class_ = 9;
npc_type->deity = 1;
npc_type->level = 200;
+42
View File
@@ -3229,11 +3229,46 @@ perl::array Perl_Client_GetInventorySlots(Client* self)
return result;
}
void Perl_Client_GrantPetNameChange(Client* self)
{
self->GrantPetNameChange();
}
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);
@@ -3305,6 +3340,7 @@ void perl_register_client()
package.add("CanHaveSkill", &Perl_Client_CanHaveSkill);
package.add("CashReward", &Perl_Client_CashReward);
package.add("ChangeLastName", &Perl_Client_ChangeLastName);
package.add("GrantPetNameChange", &Perl_Client_GrantPetNameChange);
package.add("CharacterID", &Perl_Client_CharacterID);
package.add("CheckIncreaseSkill", (bool(*)(Client*, int))&Perl_Client_CheckIncreaseSkill);
package.add("CheckIncreaseSkill", (bool(*)(Client*, int, int))&Perl_Client_CheckIncreaseSkill);
@@ -3325,6 +3361,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 +3399,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 +3708,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);
+7
View File
@@ -22,6 +22,7 @@
#include "../common/repositories/pets_repository.h"
#include "../common/repositories/pets_beastlord_data_repository.h"
#include "../common/repositories/character_pet_name_repository.h"
#include "entity.h"
#include "client.h"
@@ -164,6 +165,12 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
// 4 - Keep DB name
// 5 - `s ward
if (IsClient() && !petname) {
const auto vanity_name = CharacterPetNameRepository::FindOne(database, CastToClient()->CharacterID());
if (!vanity_name.name.empty()) {
petname = vanity_name.name.c_str();
}
}
if (petname != nullptr) {
// Name was provided, use it.
+87 -30
View File
@@ -1,4 +1,6 @@
#include "raycast_mesh.h"
#include "../common/memory/ksm.hpp"
#include "../common/eqemu_logsys.h"
#include <math.h>
#include <assert.h>
#include <stdlib.h>
@@ -9,7 +11,7 @@
// This code snippet allows you to create an axis aligned bounding volume tree for a triangle mesh so that you can do
// high-speed raycasting.
//
// There are much better implementations of this available on the internet. In particular I recommend that you use
// There are much better implementations of this available on the internet. In particular I recommend that you use
// OPCODE written by Pierre Terdiman.
// @see: http://www.codercorner.com/Opcode.htm
//
@@ -17,7 +19,7 @@
//
// I am providing this code snippet for the use case where you *only* want to do quick and dirty optimized raycasting.
// I have not done performance testing between this version and OPCODE; so I don't know how much slower it is. However,
// anytime you switch to using a spatial data structure for raycasting, you increase your performance by orders and orders
// anytime you switch to using a spatial data structure for raycasting, you increase your performance by orders and orders
// of magnitude; so this implementation should work fine for simple tools and utilities.
//
// It also serves as a nice sample for people who are trying to learn the algorithm of how to implement AABB trees.
@@ -32,14 +34,14 @@
//
// The official source can be found at: http://code.google.com/p/raycastmesh/
//
//
//
#pragma warning(disable:4100)
namespace RAYCAST_MESH
{
typedef std::vector< RmUint32 > TriVector;
typedef std::vector<RmUint32, PageAlignedAllocator<RmUint32>> TriVector;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
@@ -365,7 +367,7 @@ public:
{
RmUint32 ret = 0;
if ( p[0] < mMin[0] )
if ( p[0] < mMin[0] )
{
ret|=CC_MINX;
}
@@ -374,7 +376,7 @@ public:
ret|=CC_MAXX;
}
if ( p[1] < mMin[1] )
if ( p[1] < mMin[1] )
{
ret|=CC_MINY;
}
@@ -383,7 +385,7 @@ public:
ret|=CC_MAXY;
}
if ( p[2] < mMin[2] )
if ( p[2] < mMin[2] )
{
ret|=CC_MINZ;
}
@@ -514,7 +516,7 @@ public:
// the width of the longest axis is less than the minimum axis size then...
// we create the leaf node and copy the triangles into the leaf node triangle array.
if ( count < minLeafSize || depth >= maxDepth || laxis < minAxisSize )
{
{
// Copy the triangle indices into the leaf triangles array
mLeafTriangleIndex = leafTriangles.size(); // assign the array start location for these leaf triangles.
leafTriangles.push_back(count);
@@ -542,7 +544,7 @@ public:
// and another array that includes all triangles which intersect the 'right' half of the bounding volume node.
for (auto i = triangles.begin(); i != triangles.end(); ++i) {
RmUint32 tri = (*i);
RmUint32 tri = (*i);
{
RmUint32 i1 = indices[tri*3+0];
@@ -590,7 +592,7 @@ public:
{
leftBounds.clamp(b1); // we have to clamp the bounding volume so it stays inside the parent volume.
mLeft = callback->getNode(); // get a new AABB node
new ( mLeft ) NodeAABB(leftBounds); // initialize it to default constructor values.
new ( mLeft ) NodeAABB(leftBounds); // initialize it to default constructor values.
// Then recursively split this node.
mLeft->split(leftTriangles,vcount,vertices,tcount,indices,depth+1,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles);
}
@@ -662,7 +664,7 @@ public:
RmReal nd = nearestDistance;
if ( !intersectLineSegmentAABB(mBounds.mMin,mBounds.mMax,from,dir,nd,sect) )
{
return;
return;
}
if ( mLeafTriangleIndex != TRI_EOF )
{
@@ -754,28 +756,60 @@ public:
{
mMaxNodeCount+=pow2Table[i];
}
mNodes = new NodeAABB[mMaxNodeCount];
// Allocate page-aligned memory
mNodes = static_cast<NodeAABB*>(KSM::AllocatePageAligned(sizeof(NodeAABB) * mMaxNodeCount));
if (!mNodes) {
throw std::bad_alloc();
}
mNodeCount = 0;
KSM::CheckPageAlignment(mNodes);
mVertices = static_cast<RmReal*>(KSM::AllocatePageAligned(sizeof(RmReal) * 3 * vcount));
if (!mVertices) {
throw std::bad_alloc();
}
std::memcpy(mVertices, vertices, sizeof(RmReal) * 3 * vcount);
mVcount = vcount;
mVertices = (RmReal *)::malloc(sizeof(RmReal)*3*vcount);
memcpy(mVertices,vertices,sizeof(RmReal)*3*vcount);
mIndices = static_cast<RmUint32*>(KSM::AllocatePageAligned(sizeof(RmUint32) * 3 * tcount));
if (!mIndices) {
throw std::bad_alloc();
}
std::memcpy(mIndices, indices, sizeof(RmUint32) * 3 * tcount);
mTcount = tcount;
mIndices = (RmUint32 *)::malloc(sizeof(RmUint32)*tcount*3);
memcpy(mIndices,indices,sizeof(RmUint32)*tcount*3);
mRaycastTriangles = (RmUint32 *)::malloc(tcount*sizeof(RmUint32));
memset(mRaycastTriangles,0,tcount*sizeof(RmUint32));
mRaycastTriangles = static_cast<RmUint32*>(KSM::AllocatePageAligned(sizeof(RmUint32) * tcount));
if (!mRaycastTriangles) {
throw std::bad_alloc();
}
std::memset(mRaycastTriangles, 0, sizeof(RmUint32) * tcount);
mFaceNormals = static_cast<RmReal*>(KSM::AllocatePageAligned(sizeof(RmReal) * 3 * tcount));
if (!mFaceNormals) {
throw std::bad_alloc();
}
std::memset(mFaceNormals, 0, sizeof(RmReal) * 3 * tcount);
// Mark memory as mergeable for KSM
KSM::MarkMemoryForKSM(mVertices, sizeof(RmReal) * 3 * vcount);
KSM::MarkMemoryForKSM(mIndices, sizeof(RmUint32) * 3 * tcount);
KSM::MarkMemoryForKSM(mRaycastTriangles, sizeof(RmUint32) * tcount);
KSM::MarkMemoryForKSM(mFaceNormals, sizeof(RmReal) * 3 * tcount);
mRoot = getNode();
mFaceNormals = NULL;
new ( mRoot ) NodeAABB(mVcount,mVertices,mTcount,mIndices,maxDepth,minLeafSize,minAxisSize,this,mLeafTriangles);
KSM::MarkMemoryForKSM(mLeafTriangles.data(), mLeafTriangles.size() * sizeof(RmUint32));
}
~MyRaycastMesh(void)
{
delete []mNodes;
::free(mVertices);
::free(mIndices);
::free(mFaceNormals);
::free(mRaycastTriangles);
if (mNodes) { free(mNodes); }
if (mVertices) { free(mVertices); }
if (mIndices) { free(mIndices); }
if (mRaycastTriangles) { free(mRaycastTriangles); }
if (mFaceNormals) { free(mFaceNormals); }
}
virtual bool raycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance)
@@ -812,7 +846,7 @@ public:
return mRoot->mBounds.mMax;
}
virtual NodeAABB * getNode(void)
virtual NodeAABB * getNode(void)
{
assert( mNodeCount < mMaxNodeCount );
NodeAABB *ret = &mNodes[mNodeCount];
@@ -820,7 +854,7 @@ public:
return ret;
}
virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal)
virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal)
{
if ( mFaceNormals == NULL )
{
@@ -938,6 +972,29 @@ RaycastMesh * createRaycastMesh(RmUint32 vcount, // The number of vertices in t
)
{
auto m = new MyRaycastMesh(vcount, vertices, tcount, indices, maxDepth, minLeafSize, minAxisSize);
// Calculate memory usage
size_t vertex_size = vcount * sizeof(RmReal) * 3; // Each vertex has 3 floats
size_t index_size = tcount * 3 * sizeof(RmUint32); // Each triangle has 3 indices
size_t bvh_node_size = m->mNodeCount * sizeof(NodeAABB); // BVH Node memory usage
size_t bvh_leaf_size = m->mLeafTriangles.size() * sizeof(RmUint32); // BVH leaf triangles
size_t bvh_size = bvh_node_size + bvh_leaf_size; // Total BVH size
size_t total_size = vertex_size + index_size + bvh_size;
KSM::CheckPageAlignment(m->mNodes);
KSM::CheckPageAlignment(m->mVertices);
LogInfo(
"Map Raycast Memory Usage | Vertices [{:.2f}] MB Indices [{:.2f}] MB BVH Nodes [{:.2f}] MB BVH Leaves [{:.2f}] MB BVH Total [{:.2f}] MB",
vertex_size / (1024.0 * 1024.0),
index_size / (1024.0 * 1024.0),
bvh_node_size / (1024.0 * 1024.0),
bvh_leaf_size / (1024.0 * 1024.0),
bvh_size / (1024.0 * 1024.0)
);
LogInfo("Total Raycast Memory [{:.2f}] MB", total_size / (1024.0 * 1024.0));
return static_cast< RaycastMesh * >(m);
}
@@ -984,12 +1041,12 @@ MyRaycastMesh::MyRaycastMesh(std::vector<char>& rm_buffer)
return;
char* buf = rm_buffer.data();
chunk_size = sizeof(RmUint32);
memcpy(&mVcount, buf, chunk_size);
buf += chunk_size;
bytes_read += chunk_size;
chunk_size = (sizeof(RmReal) * (3 * mVcount));
mVertices = (RmReal *)::malloc(chunk_size);
memcpy(mVertices, buf, chunk_size);
@@ -1037,7 +1094,7 @@ MyRaycastMesh::MyRaycastMesh(std::vector<char>& rm_buffer)
buf += chunk_size;
bytes_read += chunk_size;
}
chunk_size = sizeof(RmUint32);
memcpy(&mNodeCount, buf, chunk_size);
buf += chunk_size;
@@ -1071,7 +1128,7 @@ MyRaycastMesh::MyRaycastMesh(std::vector<char>& rm_buffer)
mNodes[index].mLeft = &mNodes[lNodeIndex];
buf += chunk_size;
bytes_read += chunk_size;
RmUint32 rNodeIndex;
chunk_size = sizeof(RmUint32);
memcpy(&rNodeIndex, buf, chunk_size);
@@ -1106,7 +1163,7 @@ MyRaycastMesh::MyRaycastMesh(std::vector<char>& rm_buffer)
void MyRaycastMesh::serialize(std::vector<char>& rm_buffer)
{
rm_buffer.clear();
size_t rm_buffer_size_ = 0;
rm_buffer_size_ += sizeof(RmUint32); // mVcount
+4 -2
View File
@@ -7513,15 +7513,17 @@ void Mob::SetHP(int64 hp)
void Mob::DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec)
{
NPC *node = nullptr;
NPC *node = nullptr;
for (const auto &n: entity_list.GetNPCList()) {
if (n.second->GetCleanName() == node_name) {
if (n.second->GetEntityVariable("node_parent_id") == std::to_string(GetID())) {
node = n.second;
break;
}
}
if (!node) {
node = NPC::SpawnNodeNPC(node_name, "", GetPosition());
node->SetEntityVariable("node_parent_id", std::to_string(GetID()));
}
}
+11 -72
View File
@@ -1091,7 +1091,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
mMovementManager = &MobMovementManager::Get();
SetNpcPositionUpdateDistance(0);
SetQuestHotReloadQueued(false);
}
@@ -1374,17 +1373,17 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_version)
newzone_data.suspend_buffs = z->suspendbuffs;
// local attributes
can_bind = z->canbind != 0;
is_city = z->canbind == 2;
can_combat = z->cancombat != 0;
can_levitate = z->canlevitate != 0;
can_castoutdoor = z->castoutdoor != 0;
is_hotzone = z->hotzone != 0;
max_movement_update_range = z->max_movement_update_range;
default_ruleset = z->ruleset;
allow_mercs = true;
m_graveyard_id = z->graveyard_id;
m_max_clients = z->maxclients;
can_bind = z->canbind != 0;
is_city = z->canbind == 2;
can_combat = z->cancombat != 0;
can_levitate = z->canlevitate != 0;
can_castoutdoor = z->castoutdoor != 0;
is_hotzone = z->hotzone != 0;
m_client_update_range = z->client_update_range;
default_ruleset = z->ruleset;
allow_mercs = true;
m_graveyard_id = z->graveyard_id;
m_max_clients = z->maxclients;
SetIdleWhenEmpty(z->idle_when_empty);
SetSecondsBeforeIdle(z->seconds_before_idle);
@@ -1538,10 +1537,6 @@ bool Zone::Process() {
if (adv_data && !did_adventure_actions) {
DoAdventureActions();
}
if (GetNpcPositionUpdateDistance() == 0) {
CalculateNpcUpdateDistanceSpread();
}
}
if (hot_reload_timer.Check() && IsQuestHotReloadQueued()) {
@@ -2735,62 +2730,6 @@ void Zone::SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp) {
m_ucss_available = ucss_available;
}
int Zone::GetNpcPositionUpdateDistance() const
{
return npc_position_update_distance;
}
void Zone::SetNpcPositionUpdateDistance(int in_npc_position_update_distance)
{
Zone::npc_position_update_distance = in_npc_position_update_distance;
}
void Zone::CalculateNpcUpdateDistanceSpread()
{
float max_x = 0;
float max_y = 0;
float min_x = 0;
float min_y = 0;
auto &mob_list = entity_list.GetMobList();
for (auto &it : mob_list) {
Mob *entity = it.second;
if (!entity->IsNPC()) {
continue;
}
if (entity->GetX() <= min_x) {
min_x = entity->GetX();
}
if (entity->GetY() <= min_y) {
min_y = entity->GetY();
}
if (entity->GetX() >= max_x) {
max_x = entity->GetX();
}
if (entity->GetY() >= max_y) {
max_y = entity->GetY();
}
}
int x_spread = int(std::abs(max_x - min_x));
int y_spread = int(std::abs(max_y - min_y));
int combined_spread = int(std::abs((x_spread + y_spread) / 2));
int update_distance = EQ::ClampLower(int(combined_spread / 4), int(zone->GetMaxMovementUpdateRange()));
SetNpcPositionUpdateDistance(update_distance);
Log(Logs::General, Logs::Debug,
"NPC update spread distance set to [%i] combined_spread [%i]",
update_distance,
combined_spread
);
}
bool Zone::IsQuestHotReloadQueued() const
{
return quest_hot_reload_queued;
+2 -5
View File
@@ -156,9 +156,6 @@ public:
bool SaveZoneCFG();
bool DoesAlternateCurrencyExist(uint32 currency_id);
int GetNpcPositionUpdateDistance() const;
void SetNpcPositionUpdateDistance(int in_npc_position_update_distance);
char *adv_data;
const char *GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &location);
@@ -417,7 +414,7 @@ public:
SendDiscordMessage(webhook_id, message_prefix + Discord::FormatDiscordMessage(log_category, message));
};
double GetMaxMovementUpdateRange() const { return max_movement_update_range; }
double GetClientUpdateRange() const { return m_client_update_range; }
void SetIsHotzone(bool is_hotzone);
@@ -469,7 +466,7 @@ private:
bool staticzone;
bool zone_has_current_time;
bool quest_hot_reload_queued;
double max_movement_update_range;
double m_client_update_range;
char *long_name;
char *map_name;
char *short_name;