[Rules] Implement Targeted Rule Sets

This commit is contained in:
Chris Miles 2025-06-17 02:32:22 -05:00
parent b8884d6572
commit a65db13ec3
9 changed files with 209 additions and 12 deletions

View File

@ -6,6 +6,8 @@
#include "../rulesys.h"
#include "../eqemu_logsys.h"
#include "../repositories/instance_list_repository.h"
#include "../repositories/rule_sets_repository.h"
#include "../repositories/rule_values_repository.h"
#include "../zone_store.h"
@ -347,3 +349,70 @@ bool WorldContentService::DoesZonePassContentFiltering(const ZoneRepository::Zon
return DoesPassContentFiltering(f);
}
void WorldContentService::LoadTargetedRulesets(Database* db)
{
if (!m_zone_id) {
LogError("Zone ID is not set. Cannot load targeted rulesets.");
return;
}
LogInfo("Zone ID [{}] Instance Version [{}] - Loading targeted rulesets", m_zone_id, m_instance_version);
constexpr int8 EXPANSION_ZERO_VALUE = -2;
auto rules = RuleValuesRepository::GetWhere(*db, "TRUE ORDER BY ruleset_id, rule_name");
auto inst = RuleManager::Instance();
auto sets = RuleSetsRepository::GetWhere(*db, "TRUE ORDER BY ruleset_id");
for (auto& e : sets) {
bool has_filters =
!e.zone_ids.empty() ||
!e.instance_versions.empty() ||
!e.content_flags.empty() ||
!e.content_flags_disabled.empty() ||
e.min_expansion != EXPANSION_ZERO_VALUE ||
e.max_expansion != EXPANSION_ZERO_VALUE;
if (!has_filters) {
continue; // not a targeted ruleset
}
auto zone_id = std::to_string(m_zone_id);
if (!e.zone_ids.empty() && !Strings::Contains(e.zone_ids, zone_id)) {
continue;
}
auto instance_id = std::to_string(m_instance_version);
if (!e.instance_versions.empty() && !Strings::Contains(e.instance_versions, instance_id)) {
continue;
}
if (!DoesPassContentFiltering(
ContentFlags{
.min_expansion = e.min_expansion,
.max_expansion = e.max_expansion,
.content_flags = e.content_flags,
.content_flags_disabled = e.content_flags_disabled
}
)) {
continue;
}
for (auto &r : rules) {
if (r.ruleset_id != e.ruleset_id) {
continue;
}
inst->SetRule(r.rule_name, r.rule_value);
LogInfo(
"Loading targeted rule from ruleset [{}] ruleset_name [{}] rule_name [{}] rule_value [{}]",
e.ruleset_id,
e.name,
r.rule_name,
r.rule_value
);
}
}
return;
}

View File

@ -187,6 +187,11 @@ public:
return &instance;
}
// targeted rulesets
void LoadTargetedRulesets(Database* db);
inline void SetZoneId(int zone_id) { m_zone_id = zone_id; }
inline void SetInstanceVersion(int instance_version) { m_instance_version = instance_version; }
private:
int current_expansion{};
std::vector<ContentFlagsRepository::ContentFlags> content_flags;
@ -195,6 +200,9 @@ private:
Database *m_database;
Database *m_content_database;
int m_zone_id = 0;
int m_instance_version = 0;
// holds a record of the zone table from the database
WorldContentService *LoadStaticGlobalZoneInstances();
std::vector<InstanceListRepository::InstanceList> m_zone_static_instances;

View File

@ -7133,6 +7133,24 @@ CREATE INDEX idx_event_type_char_id ON player_event_logs (event_type_id, charact
.sql = R"(
ALTER TABLE `character_corpses`
ADD COLUMN `entity_variables` TEXT DEFAULT NULL AFTER `rezzable`;
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9326,
.description = "2025_06_16_rule_set_expansion.sql",
.check = "SHOW COLUMNS FROM `rule_sets` LIKE 'zone_ids'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `rule_sets`
ADD COLUMN `zone_ids` TEXT NOT NULL DEFAULT '',
ADD COLUMN `instance_versions` TEXT NOT NULL DEFAULT '',
ADD COLUMN `content_flags` TEXT NOT NULL DEFAULT '',
ADD COLUMN `content_flags_disabled` TEXT NOT NULL DEFAULT '',
ADD COLUMN `min_expansion` TINYINT NOT NULL DEFAULT -2,
ADD COLUMN `max_expansion` TINYINT NOT NULL DEFAULT -2,
ADD COLUMN `notes` VARCHAR(255) NOT NULL DEFAULT '';
)",
.content_schema_update = false
},

View File

@ -21,6 +21,13 @@ public:
struct RuleSets {
uint8_t ruleset_id;
std::string name;
std::string zone_ids;
std::string instance_versions;
std::string content_flags;
std::string content_flags_disabled;
int8_t min_expansion;
int8_t max_expansion;
std::string notes;
};
static std::string PrimaryKey()
@ -33,6 +40,13 @@ public:
return {
"ruleset_id",
"name",
"zone_ids",
"instance_versions",
"content_flags",
"content_flags_disabled",
"min_expansion",
"max_expansion",
"notes",
};
}
@ -41,6 +55,13 @@ public:
return {
"ruleset_id",
"name",
"zone_ids",
"instance_versions",
"content_flags",
"content_flags_disabled",
"min_expansion",
"max_expansion",
"notes",
};
}
@ -81,8 +102,15 @@ public:
{
RuleSets e{};
e.ruleset_id = 0;
e.name = "";
e.ruleset_id = 0;
e.name = "";
e.zone_ids = "";
e.instance_versions = "";
e.content_flags = "";
e.content_flags_disabled = "";
e.min_expansion = -2;
e.max_expansion = -2;
e.notes = "";
return e;
}
@ -119,8 +147,15 @@ public:
if (results.RowCount() == 1) {
RuleSets e{};
e.ruleset_id = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.name = row[1] ? row[1] : "";
e.ruleset_id = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.name = row[1] ? row[1] : "";
e.zone_ids = row[2] ? row[2] : "";
e.instance_versions = row[3] ? row[3] : "";
e.content_flags = row[4] ? row[4] : "";
e.content_flags_disabled = row[5] ? row[5] : "";
e.min_expansion = row[6] ? static_cast<int8_t>(atoi(row[6])) : -2;
e.max_expansion = row[7] ? static_cast<int8_t>(atoi(row[7])) : -2;
e.notes = row[8] ? row[8] : "";
return e;
}
@ -155,6 +190,13 @@ public:
auto columns = Columns();
v.push_back(columns[1] + " = '" + Strings::Escape(e.name) + "'");
v.push_back(columns[2] + " = '" + Strings::Escape(e.zone_ids) + "'");
v.push_back(columns[3] + " = '" + Strings::Escape(e.instance_versions) + "'");
v.push_back(columns[4] + " = '" + Strings::Escape(e.content_flags) + "'");
v.push_back(columns[5] + " = '" + Strings::Escape(e.content_flags_disabled) + "'");
v.push_back(columns[6] + " = " + std::to_string(e.min_expansion));
v.push_back(columns[7] + " = " + std::to_string(e.max_expansion));
v.push_back(columns[8] + " = '" + Strings::Escape(e.notes) + "'");
auto results = db.QueryDatabase(
fmt::format(
@ -178,6 +220,13 @@ public:
v.push_back(std::to_string(e.ruleset_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back("'" + Strings::Escape(e.zone_ids) + "'");
v.push_back("'" + Strings::Escape(e.instance_versions) + "'");
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.notes) + "'");
auto results = db.QueryDatabase(
fmt::format(
@ -209,6 +258,13 @@ public:
v.push_back(std::to_string(e.ruleset_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back("'" + Strings::Escape(e.zone_ids) + "'");
v.push_back("'" + Strings::Escape(e.instance_versions) + "'");
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.notes) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@ -242,8 +298,15 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
RuleSets e{};
e.ruleset_id = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.name = row[1] ? row[1] : "";
e.ruleset_id = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.name = row[1] ? row[1] : "";
e.zone_ids = row[2] ? row[2] : "";
e.instance_versions = row[3] ? row[3] : "";
e.content_flags = row[4] ? row[4] : "";
e.content_flags_disabled = row[5] ? row[5] : "";
e.min_expansion = row[6] ? static_cast<int8_t>(atoi(row[6])) : -2;
e.max_expansion = row[7] ? static_cast<int8_t>(atoi(row[7])) : -2;
e.notes = row[8] ? row[8] : "";
all_entries.push_back(e);
}
@ -268,8 +331,15 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
RuleSets e{};
e.ruleset_id = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.name = row[1] ? row[1] : "";
e.ruleset_id = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.name = row[1] ? row[1] : "";
e.zone_ids = row[2] ? row[2] : "";
e.instance_versions = row[3] ? row[3] : "";
e.content_flags = row[4] ? row[4] : "";
e.content_flags_disabled = row[5] ? row[5] : "";
e.min_expansion = row[6] ? static_cast<int8_t>(atoi(row[6])) : -2;
e.max_expansion = row[7] ? static_cast<int8_t>(atoi(row[7])) : -2;
e.notes = row[8] ? row[8] : "";
all_entries.push_back(e);
}
@ -346,6 +416,13 @@ public:
v.push_back(std::to_string(e.ruleset_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back("'" + Strings::Escape(e.zone_ids) + "'");
v.push_back("'" + Strings::Escape(e.instance_versions) + "'");
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.notes) + "'");
auto results = db.QueryDatabase(
fmt::format(
@ -370,6 +447,13 @@ public:
v.push_back(std::to_string(e.ruleset_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back("'" + Strings::Escape(e.zone_ids) + "'");
v.push_back("'" + Strings::Escape(e.instance_versions) + "'");
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.notes) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}

View File

@ -26,6 +26,7 @@
#include "../common/repositories/rule_sets_repository.h"
#include "../common/repositories/rule_values_repository.h"
#include "../common/content/world_content_service.h"
const char *RuleManager::s_categoryNames[_CatCount + 1] = {
#define RULE_CATEGORY(category_name) \
@ -268,7 +269,6 @@ bool RuleManager::LoadRules(Database *db, const std::string &rule_set_name, bool
const std::string default_ruleset_name = "default";
bool is_default = rule_set_name == default_ruleset_name;
if (!is_default) {
const auto default_rule_set_id = RuleSetsRepository::GetRuleSetID(*db, default_ruleset_name);
@ -327,6 +327,10 @@ bool RuleManager::LoadRules(Database *db, const std::string &rule_set_name, bool
rule_set_id
);
if (m_post_load_callback) {
m_post_load_callback(db);
}
return true;
}
@ -661,4 +665,4 @@ bool RuleManager::GetBoolRule(RuleManager::BoolType t) const
std::string RuleManager::GetStringRule(RuleManager::StringType t) const
{
return m_RuleStringValues[t];
}
}

View File

@ -40,6 +40,7 @@
RuleManager::Instance()->GetStringRule( RuleManager::String__##rule_name )
#include <functional>
#include <vector>
#include <string>
#include <map>
@ -156,6 +157,9 @@ public:
bool UpdateInjectedRules(Database* db, const std::string& rule_set_name, bool quiet_update = false);
bool UpdateOrphanedRules(Database* db, bool quiet_update = false);
bool RestoreRuleNotes(Database* db);
void SetPostLoadCallback(std::function<void(Database*)> cb) {
m_post_load_callback = std::move(cb);
}
private:
RuleManager();
@ -170,6 +174,8 @@ private:
uint32 m_RuleBoolValues[BoolRuleCount];
std::string m_RuleStringValues[StringRuleCount];
std::function<void(Database*)> m_post_load_callback;
typedef enum {
IntRule,
RealRule,

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 9325
#define CURRENT_BINARY_DATABASE_VERSION 9326
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
#define CUSTOM_BINARY_DATABASE_VERSION 0

View File

@ -413,6 +413,11 @@ int main(int argc, char **argv)
->ReloadContentFlags();
ZoneEventScheduler::Instance()->SetDatabase(&database)->LoadScheduledEvents();
RuleManager::Instance()->SetPostLoadCallback(
[&](Database* db) {
WorldContentService::Instance()->LoadTargetedRulesets(db);
}
);
EQ::SayLinkEngine::LoadCachedSaylinks();

View File

@ -1151,6 +1151,10 @@ bool Zone::Init(bool is_static) {
);
} // if that fails, try the file name, then load defaults
content_service.SetZoneId(GetZoneID());
content_service.SetInstanceVersion(GetInstanceVersion());
RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true);
if (RuleManager::Instance()->GetActiveRulesetID() != default_ruleset) {
std::string r_name = RuleSetsRepository::GetRuleSetName(database, default_ruleset);
if (r_name.size() > 0) {
@ -1293,7 +1297,6 @@ void Zone::ReloadStaticData() {
WorldContentService::Instance()->SetExpansionContext()->ReloadContentFlags();
LogInfo("Zone Static Data Reloaded");
}