[Skill Caps] Remove from shared memory and simplify (#4069)

* [Skill Caps] Remove from shared memory and simplify

- Removes Skill Caps loading from shared memory and puts it into zone.
- Adds `id` column to `skill_caps`.
- Remove primary keys and use `id` as primary key.
- Add unique index using `skill_id`, `class_id`, `level`, and `cap`.
- Renames `class` to `class_id` in `skill_caps` table.
- Renames `skillID` to `skill_id` in `skill_caps` table.
- Regenerates Skill Caps repository.
- Adds `#reload skill_caps` to reload skill caps in real time.

* Update groups.cpp

* Update groups.cpp
This commit is contained in:
Alex King 2024-03-23 19:52:40 -04:00 committed by GitHub
parent 036309ebec
commit d7ea290b6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 306 additions and 414 deletions

View File

@ -29,6 +29,8 @@
#include "../../common/content/world_content_service.h"
#include "../../common/zone_store.h"
#include "../../common/path_manager.h"
#include "../../common/repositories/skill_caps_repository.h"
#include "../../common/file.h"
EQEmuLogSys LogSys;
WorldContentService content_service;
@ -164,81 +166,76 @@ void ExportSpells(SharedDatabase *db)
fclose(f);
}
bool SkillUsable(SharedDatabase *db, int skill_id, int class_id)
bool SkillUsable(SharedDatabase* db, int skill_id, int class_id)
{
bool res = false;
std::string query = StringFormat(
"SELECT max(cap) FROM skill_caps WHERE class=%d AND skillID=%d",
class_id, skill_id
const auto& l = SkillCapsRepository::GetWhere(
*db,
fmt::format(
"`class_id` = {} AND `skill_id` = {} ORDER BY `cap` DESC LIMIT 1",
class_id,
skill_id
)
);
auto results = db->QueryDatabase(query);
if (!results.Success()) {
return false;
}
if (results.RowCount() == 0) {
return false;
}
auto row = results.begin();
if (row[0] && Strings::ToInt(row[0]) > 0) {
return true;
}
return false;
return !l.empty();
}
int GetSkill(SharedDatabase *db, int skill_id, int class_id, int level)
uint32 GetSkill(SharedDatabase* db, int skill_id, int class_id, int level)
{
std::string query = StringFormat(
"SELECT cap FROM skill_caps WHERE class=%d AND skillID=%d AND level=%d",
class_id, skill_id, level
const auto& l = SkillCapsRepository::GetWhere(
*db,
fmt::format(
"`class_id` = {} AND `skill_id` = {} AND `level` = {}",
class_id,
skill_id,
level
)
);
auto results = db->QueryDatabase(query);
if (!results.Success()) {
if (l.empty()) {
return 0;
}
if (results.RowCount() == 0) {
return 0;
}
auto e = l.front();
auto row = results.begin();
return Strings::ToInt(row[0]);
return e.cap;
}
void ExportSkillCaps(SharedDatabase *db)
void ExportSkillCaps(SharedDatabase* db)
{
LogInfo("Exporting Skill Caps");
std::string file = fmt::format("{}/export/SkillCaps.txt", path.GetServerPath());
FILE *f = fopen(file.c_str(), "w");
if (!f) {
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", path.GetServerPath()));
if (!file || !file.is_open()) {
LogError("Unable to open export/SkillCaps.txt to write, skipping.");
return;
}
for (int cl = 1; cl <= 16; ++cl) {
for (int skill = 0; skill <= 77; ++skill) {
if (SkillUsable(db, skill, cl)) {
int previous_cap = 0;
for (int level = 1; level <= 100; ++level) {
int cap = GetSkill(db, skill, cl, level);
const uint8 skill_cap_max_level = (
RuleI(Character, SkillCapMaxLevel) > 0 ?
RuleI(Character, SkillCapMaxLevel) :
RuleI(Character, MaxLevel)
);
for (uint8 class_id = Class::Warrior; class_id <= Class::Berserker; class_id++) {
for (uint8 skill_id = EQ::skills::Skill1HBlunt; skill_id <= EQ::skills::Skill2HPiercing; skill_id++) {
if (SkillUsable(db, skill_id, class_id)) {
uint32 previous_cap = 0;
for (uint8 level = 1; level <= skill_cap_max_level; level++) {
uint32 cap = GetSkill(db, skill_id, class_id, level);
if (cap < previous_cap) {
cap = previous_cap;
}
fprintf(f, "%d^%d^%d^%d^0\n", cl, skill, level, cap);
file << fmt::format("{}^{}^{}^{}^0", class_id, skill_id, level, cap) << std::endl;
previous_cap = cap;
}
}
}
}
fclose(f);
file.close();
}
void ExportBaseData(SharedDatabase *db)

View File

@ -1150,56 +1150,6 @@ uint8 Database::GetRaceSkill(uint8 skillid, uint8 in_race)
return Strings::ToUnsignedInt(row[0]);
}
uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level)
{
uint8 skill_level = 0, skill_formula = 0;
uint16 base_cap = 0, skill_cap = 0, skill_cap2 = 0, skill_cap3 = 0;
//Fetch the data from DB.
std::string query = StringFormat("SELECT level, formula, pre50cap, post50cap, post60cap from skillcaps where skill = %i && class = %i", skillid, in_class);
auto results = QueryDatabase(query);
if (results.Success() && results.RowsAffected() != 0)
{
auto row = results.begin();
skill_level = Strings::ToUnsignedInt(row[0]);
skill_formula = Strings::ToUnsignedInt(row[1]);
skill_cap = Strings::ToUnsignedInt(row[2]);
if (Strings::ToUnsignedInt(row[3]) > skill_cap)
skill_cap2 = (Strings::ToUnsignedInt(row[3])-skill_cap)/10; //Split the post-50 skill cap into difference between pre-50 cap and post-50 cap / 10 to determine amount of points per level.
skill_cap3 = Strings::ToUnsignedInt(row[4]);
}
int race_skill = GetRaceSkill(skillid,in_race);
if (race_skill > 0 && (race_skill > skill_cap || skill_cap == 0 || in_level < skill_level))
return race_skill;
if (skill_cap == 0) //Can't train this skill at all.
return 255; //Untrainable
if (in_level < skill_level)
return 254; //Untrained
//Determine pre-51 level-based cap
if (skill_formula > 0)
base_cap = in_level*skill_formula+skill_formula;
if (base_cap > skill_cap || skill_formula == 0)
base_cap = skill_cap;
//If post 50, add post 50 cap to base cap.
if (in_level > 50 && skill_cap2 > 0)
base_cap += skill_cap2*(in_level-50);
//No cap should ever go above its post50cap
if (skill_cap3 > 0 && base_cap > skill_cap3)
base_cap = skill_cap3;
//Base cap is now the max value at the person's level, return it!
return base_cap;
}
uint32 Database::GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id)
{
auto query = fmt::format(

View File

@ -244,7 +244,6 @@ public:
uint8 GetMinStatus(uint32 zone_id, uint32 instance_version);
uint8 GetRaceSkill(uint8 skillid, uint8 in_race);
uint8 GetServerType();
uint8 GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level);
void AddReport(std::string who, std::string against, std::string lines);
struct TimeOfDay_Struct LoadTime(time_t &realtime);

View File

@ -5451,6 +5451,22 @@ DROP PRIMARY KEY,
ADD PRIMARY KEY (`group_id`, `character_id`, `bot_id`, `merc_id`) USING BTREE;
ALTER TABLE `group_id`
MODIFY COLUMN `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `name`;
)"
},
ManifestEntry{
.version = 9268,
.description = "2024_03_23_skill_caps.sql",
.check = "SHOW COLUMNS FROM `skill_caps` LIKE 'skill_id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `skill_caps`
CHANGE COLUMN `skillID` `skill_id` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 FIRST,
CHANGE COLUMN `class` `class_id` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `skill_id`,
ADD COLUMN `id` int(3) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`id`) USING BTREE,
ADD INDEX `level_skill_cap`(`skill_id`, `class_id`, `level`, `cap`);
)"
}
// -- template; copy/paste this when you need to create a new entry

View File

@ -19,8 +19,9 @@
class BaseSkillCapsRepository {
public:
struct SkillCaps {
uint8_t skillID;
uint8_t class_;
uint32_t id;
uint8_t skill_id;
uint8_t class_id;
uint8_t level;
uint32_t cap;
uint8_t class_;
@ -28,14 +29,15 @@ public:
static std::string PrimaryKey()
{
return std::string("skillID");
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"skillID",
"`class`",
"id",
"skill_id",
"class_id",
"level",
"cap",
"class_",
@ -45,8 +47,9 @@ public:
static std::vector<std::string> SelectColumns()
{
return {
"skillID",
"`class`",
"id",
"skill_id",
"class_id",
"level",
"cap",
"class_",
@ -90,11 +93,12 @@ public:
{
SkillCaps e{};
e.skillID = 0;
e.class_ = 0;
e.level = 0;
e.cap = 0;
e.class_ = 0;
e.id = 0;
e.skill_id = 0;
e.class_id = 0;
e.level = 0;
e.cap = 0;
e.class_ = 0;
return e;
}
@ -105,7 +109,7 @@ public:
)
{
for (auto &skill_caps : skill_capss) {
if (skill_caps.skillID == skill_caps_id) {
if (skill_caps.id == skill_caps_id) {
return skill_caps;
}
}
@ -131,11 +135,12 @@ public:
if (results.RowCount() == 1) {
SkillCaps e{};
e.skillID = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.class_ = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.level = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.cap = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.class_ = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.skill_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.class_id = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.level = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.cap = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.class_ = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 0;
return e;
}
@ -169,11 +174,11 @@ public:
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.skillID));
v.push_back(columns[1] + " = " + std::to_string(e.class_));
v.push_back(columns[2] + " = " + std::to_string(e.level));
v.push_back(columns[3] + " = " + std::to_string(e.cap));
v.push_back(columns[4] + " = " + std::to_string(e.class_));
v.push_back(columns[1] + " = " + std::to_string(e.skill_id));
v.push_back(columns[2] + " = " + std::to_string(e.class_id));
v.push_back(columns[3] + " = " + std::to_string(e.level));
v.push_back(columns[4] + " = " + std::to_string(e.cap));
v.push_back(columns[5] + " = " + std::to_string(e.class_));
auto results = db.QueryDatabase(
fmt::format(
@ -181,7 +186,7 @@ public:
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.skillID
e.id
)
);
@ -195,8 +200,9 @@ public:
{
std::vector<std::string> v;
v.push_back(std::to_string(e.skillID));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.class_id));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.cap));
v.push_back(std::to_string(e.class_));
@ -210,7 +216,7 @@ public:
);
if (results.Success()) {
e.skillID = results.LastInsertedID();
e.id = results.LastInsertedID();
return e;
}
@ -229,8 +235,9 @@ public:
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.skillID));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.class_id));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.cap));
v.push_back(std::to_string(e.class_));
@ -267,11 +274,12 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
SkillCaps e{};
e.skillID = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.class_ = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.level = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.cap = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.class_ = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.skill_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.class_id = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.level = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.cap = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.class_ = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@ -296,11 +304,12 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
SkillCaps e{};
e.skillID = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.class_ = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.level = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.cap = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.class_ = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.skill_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.class_id = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.level = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.cap = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.class_ = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@ -375,8 +384,9 @@ public:
{
std::vector<std::string> v;
v.push_back(std::to_string(e.skillID));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.class_id));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.cap));
v.push_back(std::to_string(e.class_));
@ -402,8 +412,9 @@ public:
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.skillID));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.class_id));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.cap));
v.push_back(std::to_string(e.class_));

View File

@ -273,6 +273,7 @@
#define ServerOP_ReloadFactions 0x4126
#define ServerOP_ReloadLoot 0x4127
#define ServerOP_ReloadBaseData 0x4128
#define ServerOP_ReloadSkillCaps 0x4129
#define ServerOP_CZDialogueWindow 0x4500
#define ServerOP_CZLDoNUpdate 0x4501

View File

@ -45,6 +45,7 @@
#include "repositories/loottable_repository.h"
#include "repositories/character_item_recast_repository.h"
#include "repositories/character_corpses_repository.h"
#include "repositories/skill_caps_repository.h"
namespace ItemField
{
@ -1626,136 +1627,6 @@ bool SharedDatabase::GetCommandSubSettings(std::vector<CommandSubsettingsReposit
return true;
}
bool SharedDatabase::LoadSkillCaps(const std::string &prefix) {
skill_caps_mmf.reset(nullptr);
try {
const auto Config = EQEmuConfig::get();
EQ::IPCMutex mutex("skill_caps");
mutex.Lock();
std::string file_name = fmt::format("{}/{}{}", path.GetSharedMemoryPath(), prefix, std::string("skill_caps"));
LogInfo("Loading [{}]", file_name);
skill_caps_mmf = std::make_unique<EQ::MemoryMappedFile>(file_name);
LogInfo("Loaded skill caps via shared memory");
mutex.Unlock();
} catch(std::exception &ex) {
LogError("Error loading skill caps: {}", ex.what());
return false;
}
return true;
}
void SharedDatabase::LoadSkillCaps(void *data) {
const uint32 class_count = Class::PLAYER_CLASS_COUNT;
const uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
const uint32 level_count = HARD_LEVEL_CAP + 1;
uint16 *skill_caps_table = static_cast<uint16*>(data);
const std::string query = "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level";
auto results = QueryDatabase(query);
if (!results.Success()) {
LogError("Error loading skill caps from database: {}", results.ErrorMessage().c_str());
return;
}
for(auto& row = results.begin(); row != results.end(); ++row) {
const uint8 skillID = Strings::ToUnsignedInt(row[0]);
const uint8 class_ = Strings::ToUnsignedInt(row[1]) - 1;
const uint8 level = Strings::ToUnsignedInt(row[2]);
const uint16 cap = Strings::ToUnsignedInt(row[3]);
if(skillID >= skill_count || class_ >= class_count || level >= level_count)
continue;
const uint32 index = (((class_ * skill_count) + skillID) * level_count) + level;
skill_caps_table[index] = cap;
}
}
uint16 SharedDatabase::GetSkillCap(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const
{
if(!skill_caps_mmf) {
return 0;
}
if(Class_ == 0)
return 0;
int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel);
if(SkillMaxLevel < 1) {
SkillMaxLevel = RuleI(Character, MaxLevel);
}
const uint32 class_count = Class::PLAYER_CLASS_COUNT;
const uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
const uint32 level_count = HARD_LEVEL_CAP + 1;
if(Class_ > class_count || static_cast<uint32>(Skill) > skill_count || Level > level_count) {
return 0;
}
if(Level > static_cast<uint8>(SkillMaxLevel)){
Level = static_cast<uint8>(SkillMaxLevel);
}
const uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count) + Level;
const uint16 *skill_caps_table = static_cast<uint16*>(skill_caps_mmf->Get());
return skill_caps_table[index];
}
uint8 SharedDatabase::GetTrainLevel(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const
{
if(!skill_caps_mmf) {
return 0;
}
if(Class_ == 0)
return 0;
int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel);
if (SkillMaxLevel < 1) {
SkillMaxLevel = RuleI(Character, MaxLevel);
}
const uint32 class_count = Class::PLAYER_CLASS_COUNT;
const uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
const uint32 level_count = HARD_LEVEL_CAP + 1;
if(Class_ > class_count || static_cast<uint32>(Skill) > skill_count || Level > level_count) {
return 0;
}
uint8 ret = 0;
if(Level > static_cast<uint8>(SkillMaxLevel)) {
const uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count);
const uint16 *skill_caps_table = static_cast<uint16*>(skill_caps_mmf->Get());
for(uint8 x = 0; x < Level; x++){
if(skill_caps_table[index + x]){
ret = x;
break;
}
}
}
else
{
const uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count);
const uint16 *skill_caps_table = static_cast<uint16*>(skill_caps_mmf->Get());
for(int x = 0; x < SkillMaxLevel; x++){
if(skill_caps_table[index + x]){
ret = x;
break;
}
}
}
if(ret > GetSkillCap(Class_, Skill, Level))
ret = static_cast<uint8>(GetSkillCap(Class_, Skill, Level));
return ret;
}
void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpellID) {
const std::string query = StringFormat("SELECT `spellid`, `type` FROM `damageshieldtypes` WHERE `spellid` > 0 "
"AND `spellid` <= %i", iMaxSpellID);
@ -2044,3 +1915,24 @@ void SharedDatabase::SetSharedSpellsCount(uint32 shared_spells_count)
{
SharedDatabase::m_shared_spells_count = shared_spells_count;
}
uint16 SharedDatabase::GetSkillCap(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level)
{
const auto& l = SkillCapsRepository::GetWhere(
*this,
fmt::format(
"`class_id` = {} AND `skill_id` = {} AND `level` = {} LIMIT 1",
class_id,
static_cast<uint16>(skill_id),
level
)
);
if (l.empty()) {
return 0;
}
const auto& e = l.front();
return e.cap;
}

View File

@ -160,13 +160,7 @@ public:
uint32 GetSharedItemsCount() { return m_shared_items_count; }
uint32 GetItemsCount();
/**
* skills
*/
void LoadSkillCaps(void *data);
bool LoadSkillCaps(const std::string &prefix);
uint16 GetSkillCap(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const;
uint8 GetTrainLevel(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const;
uint16 GetSkillCap(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
/**
* spells

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 9267
#define CURRENT_BINARY_DATABASE_VERSION 9268
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9043
#endif

View File

@ -4,13 +4,11 @@ SET(shared_memory_sources
items.cpp
main.cpp
spells.cpp
skill_caps.cpp
)
SET(shared_memory_headers
items.h
spells.h
skill_caps.h
)
ADD_EXECUTABLE(shared_memory ${shared_memory_sources} ${shared_memory_headers})

View File

@ -28,7 +28,6 @@
#include "../common/eqemu_exception.h"
#include "../common/strings.h"
#include "items.h"
#include "skill_caps.h"
#include "spells.h"
#include "../common/content/world_content_service.h"
#include "../common/zone_store.h"
@ -183,7 +182,6 @@ int main(int argc, char **argv)
bool load_all = true;
bool load_items = false;
bool load_loot = false;
bool load_skill_caps = false;
bool load_spells = false;
if (argc > 1) {
@ -197,11 +195,7 @@ int main(int argc, char **argv)
break;
case 's':
if (strcasecmp("skill_caps", argv[i]) == 0) {
load_skill_caps = true;
load_all = false;
}
else if (strcasecmp("spells", argv[i]) == 0) {
if (strcasecmp("spells", argv[i]) == 0) {
load_spells = true;
load_all = false;
}
@ -236,16 +230,6 @@ int main(int argc, char **argv)
}
}
if (load_all || load_skill_caps) {
LogInfo("Loading skill caps");
try {
LoadSkillCaps(&content_db, hotfix_name);
} catch (std::exception &ex) {
LogError("{}", ex.what());
return 1;
}
}
if (load_all || load_spells) {
LogInfo("Loading spells");
try {

View File

@ -1,45 +0,0 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "skill_caps.h"
#include "../common/global_define.h"
#include "../common/shareddb.h"
#include "../common/ipc_mutex.h"
#include "../common/memory_mapped_file.h"
#include "../common/eqemu_exception.h"
#include "../common/classes.h"
#include "../common/features.h"
void LoadSkillCaps(SharedDatabase *database, const std::string &prefix) {
EQ::IPCMutex mutex("skill_caps");
mutex.Lock();
uint32 class_count = Class::PLAYER_CLASS_COUNT;
uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
uint32 level_count = HARD_LEVEL_CAP + 1;
uint32 size = (class_count * skill_count * level_count * sizeof(uint16));
auto Config = EQEmuConfig::get();
std::string file_name = Config->SharedMemDir + prefix + std::string("skill_caps");
EQ::MemoryMappedFile mmf(file_name, size);
mmf.ZeroFile();
void *ptr = mmf.Get();
database->LoadSkillCaps(ptr);
mutex.Unlock();
}

View File

@ -1,29 +0,0 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __EQEMU_SHARED_MEMORY_SKILL_CAPS_H
#define __EQEMU_SHARED_MEMORY_SKILL_CAPS_H
#include <string>
#include "../common/eqemu_config.h"
class SharedDatabase;
void LoadSkillCaps(SharedDatabase *database, const std::string &prefix);
#endif

View File

@ -156,6 +156,7 @@ std::vector<Reload> reload_types = {
Reload{.command = "opcodes", .opcode = ServerOP_ReloadOpcodes, .desc = "Opcodes"},
Reload{.command = "perl_export", .opcode = ServerOP_ReloadPerlExportSettings, .desc = "Perl Event Export Settings"},
Reload{.command = "rules", .opcode = ServerOP_ReloadRules, .desc = "Rules"},
Reload{.command = "skill_caps", .opcode = ServerOP_ReloadSkillCaps, .desc = "Skill Caps"},
Reload{.command = "static", .opcode = ServerOP_ReloadStaticZoneData, .desc = "Static Zone Data"},
Reload{.command = "tasks", .opcode = ServerOP_ReloadTasks, .desc = "Tasks"},
Reload{.command = "titles", .opcode = ServerOP_ReloadTitles, .desc = "Titles"},

View File

@ -292,10 +292,6 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
LogError("Error: Could not load item data. But ignoring");
}
if (!content_db.LoadSkillCaps(std::string(hotfix_name))) {
LogError("Error: Could not load skill cap data. But ignoring");
}
guild_mgr.LoadGuilds();
guild_mgr.LoadTributes();

View File

@ -1411,6 +1411,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_ReloadNPCEmotes:
case ServerOP_ReloadObjects:
case ServerOP_ReloadPerlExportSettings:
case ServerOP_ReloadSkillCaps:
case ServerOP_ReloadStaticZoneData:
case ServerOP_ReloadTitles:
case ServerOP_ReloadTraps:
@ -1496,11 +1497,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
LogInfo("Error: Could not load item data. But ignoring");
}
LogInfo("Loading skill caps");
if (!content_db.LoadSkillCaps(hotfix_name)) {
LogInfo("Error: Could not load skill cap data. But ignoring");
}
zoneserver_list.SendPacket(pack);
break;
}

View File

@ -166,6 +166,7 @@ SET(zone_sources
zone_event_scheduler.cpp
zone_npc_factions.cpp
zone_reload.cpp
zone_skill_caps.cpp
zoning.cpp
)

View File

@ -2756,31 +2756,48 @@ void Client::CheckLanguageSkillIncrease(uint8 language_id, uint8 teacher_skill)
}
}
bool Client::HasSkill(EQ::skills::SkillType skill_id) const {
return((GetSkill(skill_id) > 0) && CanHaveSkill(skill_id));
}
bool Client::CanHaveSkill(EQ::skills::SkillType skill_id) const {
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skill_id == EQ::skills::Skill1HPiercing)
skill_id = EQ::skills::Skill2HPiercing;
return(content_db.GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)) > 0);
//if you don't have it by max level, then odds are you never will?
}
uint16 Client::MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const {
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skillid == EQ::skills::Skill1HPiercing)
skillid = EQ::skills::Skill2HPiercing;
return(content_db.GetSkillCap(class_, skillid, level));
}
uint8 Client::SkillTrainLevel(EQ::skills::SkillType skillid, uint16 class_)
bool Client::HasSkill(EQ::skills::SkillType skill_id) const
{
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skillid == EQ::skills::Skill1HPiercing)
skillid = EQ::skills::Skill2HPiercing;
return GetSkill(skill_id) > 0 && CanHaveSkill(skill_id);
}
return(content_db.GetTrainLevel(class_, skillid, RuleI(Character, MaxLevel)));
bool Client::CanHaveSkill(EQ::skills::SkillType skill_id) const
{
if (
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
class_ == Class::Berserker &&
skill_id == EQ::skills::Skill1HPiercing
) {
skill_id = EQ::skills::Skill2HPiercing;
}
return zone->GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)).cap > 0;
}
uint16 Client::MaxSkill(EQ::skills::SkillType skill_id, uint8 class_id, uint8 level) const
{
if (
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
class_id == Class::Berserker &&
skill_id == EQ::skills::Skill1HPiercing
) {
skill_id = EQ::skills::Skill2HPiercing;
}
return(content_db.GetSkillCap(class_id, skill_id, level));
}
uint8 Client::SkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id)
{
if (
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
class_id == Class::Berserker &&
skill_id == EQ::skills::Skill1HPiercing
) {
skill_id = EQ::skills::Skill2HPiercing;
}
return zone->GetTrainLevel(class_id, skill_id, RuleI(Character, MaxLevel));
}
uint16 Client::GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid, uint16 maxSkill)
@ -9226,6 +9243,7 @@ void Client::ShowDevToolsMenu()
menu_reload_six += " | " + Saylink::Silent("#reload quest", "Quests");
menu_reload_seven += Saylink::Silent("#reload rules", "Rules");
menu_reload_seven += " | " + Saylink::Silent("#reload skill_caps", "Skill Caps");
menu_reload_seven += " | " + Saylink::Silent("#reload static", "Static Zone Data");
menu_reload_seven += " | " + Saylink::Silent("#reload tasks", "Tasks");
@ -11353,6 +11371,16 @@ void Client::SendReloadCommandMessages() {
).c_str()
);
auto skill_caps_link = Saylink::Silent("#reload skill_caps");
Message(
Chat::White,
fmt::format(
"Usage: {} - Reloads Skill Caps globally",
skill_caps_link
).c_str()
);
auto static_link = Saylink::Silent("#reload static");
Message(

View File

@ -815,9 +815,9 @@ public:
void SetHoTT(uint32 mobid);
void ShowSkillsWindow();
uint16 MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const;
inline uint16 MaxSkill(EQ::skills::SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
uint8 SkillTrainLevel(EQ::skills::SkillType skillid, uint16 class_);
uint16 MaxSkill(EQ::skills::SkillType skill_id, uint8 class_id, uint8 level) const;
inline uint16 MaxSkill(EQ::skills::SkillType skill_id) const { return MaxSkill(skill_id, GetClass(), GetLevel()); }
uint8 SkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id);
void MaxSkills();
void SendTradeskillSearchResults(const std::string &query, unsigned long objtype, unsigned long someid);

View File

@ -34,6 +34,7 @@ void command_reload(Client *c, const Seperator *sep)
bool is_perl_export = !strcasecmp(sep->arg[1], "perl_export");
bool is_quest = !strcasecmp(sep->arg[1], "quest") || (is_rq_alias);
bool is_rules = !strcasecmp(sep->arg[1], "rules");
bool is_skill_caps = !strcasecmp(sep->arg[1], "skill_caps");
bool is_static = !strcasecmp(sep->arg[1], "static");
bool is_tasks = !strcasecmp(sep->arg[1], "tasks");
bool is_titles = !strcasecmp(sep->arg[1], "titles");
@ -66,6 +67,7 @@ void command_reload(Client *c, const Seperator *sep)
!is_perl_export &&
!is_quest &&
!is_rules &&
!is_skill_caps &&
!is_static &&
!is_tasks &&
!is_titles &&
@ -161,6 +163,9 @@ void command_reload(Client *c, const Seperator *sep)
} else if (is_rules) {
c->Message(Chat::White, "Attempting to reload Rules globally.");
pack = new ServerPacket(ServerOP_ReloadRules, 0);
} else if (is_skill_caps) {
c->Message(Chat::White, "Attempting to reload Skill Caps globally.");
pack = new ServerPacket(ServerOP_ReloadSkillCaps, 0);
} else if (is_static) {
c->Message(Chat::White, "Attempting to reload Static Zone Data globally.");
pack = new ServerPacket(ServerOP_ReloadStaticZoneData, 0);

View File

@ -372,10 +372,6 @@ int main(int argc, char **argv)
LogError("Failed. But ignoring error and going on..");
}
if (!content_db.LoadSkillCaps(std::string(hotfix_name))) {
LogError("Loading skill caps failed!");
return 1;
}
if (!database.LoadSpells(hotfix_name, &SPDAT_RECORDS, &spells)) {
LogError("Loading spells failed!");
return 1;

View File

@ -2097,6 +2097,15 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true);
break;
}
case ServerOP_ReloadSkillCaps:
{
if (zone && zone->IsLoaded()) {
zone->SendReloadMessage("Skill Caps");
zone->ReloadSkillCaps();
}
break;
}
case ServerOP_ReloadDataBucketsCache:
{
zone->SendReloadMessage("Data buckets cache");
@ -3563,11 +3572,6 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
LogError("Loading items failed!");
}
LogInfo("Loading skill caps");
if (!content_db.LoadSkillCaps(std::string(hotfix_name))) {
LogError("Loading skill caps failed!");
}
LogInfo("Loading spells");
if (!content_db.LoadSpells(hotfix_name, &SPDAT_RECORDS, &spells)) {
LogError("Loading spells failed!");

View File

@ -1177,6 +1177,8 @@ bool Zone::Init(bool is_static) {
LoadBaseData();
LoadSkillCaps();
//Load merchant data
LoadMerchants();

View File

@ -46,6 +46,7 @@
#include "../common/repositories/lootdrop_repository.h"
#include "../common/repositories/lootdrop_entries_repository.h"
#include "../common/repositories/base_data_repository.h"
#include "../common/repositories/skill_caps_repository.h"
struct EXPModifier
{
@ -452,6 +453,13 @@ public:
void LoadBaseData();
void ReloadBaseData();
// Skill Caps
inline void ClearSkillCaps() { m_skill_caps.clear(); }
SkillCapsRepository::SkillCaps GetSkillCap(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
uint8 GetTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
void LoadSkillCaps();
void ReloadSkillCaps();
private:
bool allow_mercs;
bool can_bind;
@ -515,6 +523,9 @@ private:
// Base Data
std::vector<BaseDataRepository::BaseData> m_base_data = { };
// Skill Caps
std::vector<SkillCapsRepository::SkillCaps> m_skill_caps = { };
};
#endif

84
zone/zone_skill_caps.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "zone.h"
SkillCapsRepository::SkillCaps Zone::GetSkillCap(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level)
{
if (!IsPlayerClass(class_id)) {
return SkillCapsRepository::NewEntity();
}
for (const auto& e : m_skill_caps) {
if (
e.class_id == class_id &&
e.level == level &&
static_cast<EQ::skills::SkillType>(e.skill_id) == skill_id
) {
return e;
}
}
return SkillCapsRepository::NewEntity();
}
uint8 Zone::GetTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level)
{
if (
!IsPlayerClass(class_id) ||
class_id > Class::PLAYER_CLASS_COUNT ||
static_cast<uint32>(skill_id) > (EQ::skills::HIGHEST_SKILL + 1)
) {
return 0;
}
const uint8 skill_cap_max_level = (
RuleI(Character, SkillCapMaxLevel) > 0 ?
RuleI(Character, SkillCapMaxLevel) :
RuleI(Character, MaxLevel)
);
const uint8 max_level = level > skill_cap_max_level ? level : skill_cap_max_level;
for (const auto& e : m_skill_caps) {
for (uint8 current_level = 1; current_level <= max_level; current_level++) {
if (
e.class_id == class_id &&
static_cast<EQ::skills::SkillType>(e.skill_id) == skill_id &&
e.level == current_level
) {
return current_level;
}
}
}
return 0;
}
void Zone::LoadSkillCaps()
{
const auto& l = SkillCapsRepository::All(content_db);
m_skill_caps.reserve(l.size());
for (const auto& e : l) {
if (
e.level < 1 ||
!IsPlayerClass(e.class_id) ||
static_cast<EQ::skills::SkillType>(e.skill_id) >= EQ::skills::SkillCount
) {
continue;
}
m_skill_caps.emplace_back(e);
}
LogInfo(
"Loaded [{}] Skill Cap Entr{}",
l.size(),
l.size() != 1 ? "ies" : "y"
);
}
void Zone::ReloadSkillCaps()
{
ClearSkillCaps();
LoadSkillCaps();
}