diff --git a/common/repositories/rule_sets_repository.h b/common/repositories/rule_sets_repository.h index 8e209435d..d4633c422 100644 --- a/common/repositories/rule_sets_repository.h +++ b/common/repositories/rule_sets_repository.h @@ -44,7 +44,53 @@ public: */ // Custom extended repository methods here + static int GetRuleSetID(Database& db, std::string rule_set_name) + { + const auto query = fmt::format( + "SELECT ruleset_id FROM {} WHERE `name` = '{}'", + TableName(), + Strings::Escape(rule_set_name) + ); + auto results = db.QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { + return -1; + } + auto row = results.begin(); + return std::stoi(row[0]); + } + + static int CreateNewRuleSet(Database& db, std::string rule_set_name) + { + const auto query = fmt::format( + "INSERT INTO {} (`name`) VALUES ('{}')", + TableName(), + rule_set_name + ); + auto results = db.QueryDatabase(query); + if (!results.Success() || !results.RowsAffected()) { + return -1; + } + + return static_cast(results.LastInsertedID()); + } + + static std::string GetRuleSetName(Database& db, int rule_set_id) + { + const auto query = fmt::format( + "SELECT `name` FROM {} WHERE ruleset_id = {}", + TableName(), + rule_set_id + ); + auto results = db.QueryDatabase(query); + if (!results.Success() || !results.RowsAffected()) { + return std::string(); + } + + auto row = results.begin(); + + return std::string(row[0]); + } }; #endif //EQEMU_RULE_SETS_REPOSITORY_H diff --git a/common/repositories/rule_values_repository.h b/common/repositories/rule_values_repository.h index 6565893ef..072175d5f 100644 --- a/common/repositories/rule_values_repository.h +++ b/common/repositories/rule_values_repository.h @@ -44,6 +44,87 @@ public: */ // Custom extended repository methods here + static std::vector GetRuleNames(Database &db, int rule_set_id) + { + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "SELECT rule_name FROM {} WHERE ruleset_id = {}", + TableName(), + rule_set_id + ) + ); + if (!results.Success() || !results.RowCount()) { + return v; + } + + for (auto row : results) { + v.push_back(row[0]); + } + + return v; + } + + static std::vector GetGroupedRules(Database &db) + { + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "SELECT rule_name FROM {} GROUP BY rule_name", + TableName() + ) + ); + if (!results.Success() || !results.RowCount()) { + return v; + } + + for (auto row : results) { + v.push_back(row[0]); + } + + return v; + } + + static bool DeleteOrphanedRules(Database& db, std::vector& v) + { + const auto query = fmt::format( + "DELETE FROM {} WHERE rule_name IN ({})", + TableName(), + Strings::ImplodePair(",", std::pair('\'', '\''), v) + ); + + return db.QueryDatabase(query).Success(); + } + + static bool InjectRules(Database& db, std::vector>& v) + { + const auto query = fmt::format( + "REPLACE INTO {} (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES {}", + TableName(), + Strings::ImplodePair( + ",", + std::pair('(', ')'), + join_tuple(",", std::pair('\'', '\''), v) + ) + ); + + return db.QueryDatabase(query).Success(); + } + + static bool UpdateRuleNote(Database& db, int rule_set_id, std::string rule_name, std::string notes) + { + const auto query = fmt::format( + "UPDATE {} SET notes = '{}' WHERE ruleset_id = {} AND rule_name = '{}'", + TableName(), + Strings::Escape(notes), + rule_set_id, + rule_name + ); + + return db.QueryDatabase(query).Success(); + } }; diff --git a/common/rulesys.cpp b/common/rulesys.cpp index c5806613a..b58e83e6a 100644 --- a/common/rulesys.cpp +++ b/common/rulesys.cpp @@ -24,462 +24,451 @@ #include #include -/* - Commands: - #rules: - current -> lists current set name - switch (set name) -> change set in the DB, but dont reload - load (set name) -> load set into this zone without changing the world - wload (set name) -> tell world and all zones to load this rule set - store [set name] -> store the current rules in this zone to the set (or active if not specified) - reset -> reset all rule values to their defaults. - list [catname] - set (cat) (rule) (value) - values [catname] -> show the values of all rules in the specified category/ -*/ +#include "../common/repositories/rule_sets_repository.h" +#include "../common/repositories/rule_values_repository.h" -const char *RuleManager::s_categoryNames[_CatCount+1] = { - #define RULE_CATEGORY(catname) \ - #catname , +const char *RuleManager::s_categoryNames[_CatCount + 1] = { + #define RULE_CATEGORY(category_name) \ + #category_name , #include "ruletypes.h" "InvalidCategory" }; -const RuleManager::RuleInfo RuleManager::s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount+1] = { +const RuleManager::RuleInfo RuleManager::s_RuleInfo[_IntRuleCount + _RealRuleCount + _BoolRuleCount + 1] = { /* this is done in three steps so we can reliably get to them by index*/ - #define RULE_INT(cat, rule, default_value, notes) \ - { #cat ":" #rule, Category__##cat, IntRule, Int__##rule, notes }, + #define RULE_INT(category_name, rule_name, default_value, notes) \ + { #category_name ":" #rule_name, Category__##category_name, IntRule, Int__##rule_name, notes }, #include "ruletypes.h" - #define RULE_REAL(cat, rule, default_value, notes) \ - { #cat ":" #rule, Category__##cat, RealRule, Real__##rule, notes }, + #define RULE_REAL(category_name, rule_name, default_value, notes) \ + { #category_name ":" #rule_name, Category__##category_name, RealRule, Real__##rule_name, notes }, #include "ruletypes.h" - #define RULE_BOOL(cat, rule, default_value, notes) \ - { #cat ":" #rule, Category__##cat, BoolRule, Bool__##rule, notes }, + #define RULE_BOOL(category_name, rule_name, default_value, notes) \ + { #category_name ":" #rule_name, Category__##category_name, BoolRule, Bool__##rule_name, notes }, #include "ruletypes.h" { "Invalid Rule", _CatCount, IntRule } }; RuleManager::RuleManager() -: m_activeRuleset(0), - m_activeName("default") + : m_activeRuleset(0), + m_activeName("default") { ResetRules(false); } -RuleManager::CategoryType RuleManager::FindCategory(const char *catname) { - int i; - for (i = 0; i < _CatCount; i++) { - if (strcasecmp(catname, s_categoryNames[i]) == 0) - return((CategoryType)i); - } - return(InvalidCategory); -} - -bool RuleManager::ListRules(const char *catname, std::vector &into) { - CategoryType cat = InvalidCategory; - if (catname != nullptr) { - cat = FindCategory(catname); - if (cat == InvalidCategory) { - Log(Logs::Detail, Logs::Rules, "Unable to find category '%s'", catname); - return(false); +RuleManager::CategoryType RuleManager::FindCategory(const std::string &category_name) +{ + for (int i = 0; i < _CatCount; i++) { + if (Strings::Contains(category_name, s_categoryNames[i])) { + return static_cast(i); } } - int i; - int rule_count = CountRules(); - for (i = 0; i < rule_count; i++) { - const RuleInfo &rule = s_RuleInfo[i]; - if (catname == nullptr || cat == rule.category) { - into.push_back(rule.name); + + return InvalidCategory; +} + +bool RuleManager::ListRules(const std::string &category_name, std::vector &l) +{ + CategoryType category_type = InvalidCategory; + if (!category_name.empty()) { + category_type = FindCategory(category_name); + if (category_type == InvalidCategory) { + LogRulesDetail("Unable to find category '{}'.", category_name); + return false; } } - return(true); -} -bool RuleManager::ListCategories(std::vector &into) { - int i; - for (i = 0; i < _CatCount; i++) { - into.push_back(s_categoryNames[i]); + for (int i = 0; i < CountRules(); i++) { + const auto& r = s_RuleInfo[i]; + if (category_name.empty() || category_type == r.category) { + l.push_back(r.name); + } } - return(true); -} - -bool RuleManager::GetRule(const char *rule_name, std::string &return_value) { - RuleType type; - uint16 index; - if (!_FindRule(rule_name, type, index)) - return false; - - char tmp[255] = ""; - switch(type) { - case IntRule: - sprintf(tmp, "%i", m_RuleIntValues[index]); - break; - case RealRule: - sprintf(tmp, "%f", m_RuleRealValues[index]); - break; - case BoolRule: - std::string tmp_val = m_RuleBoolValues[index] ? "true" : "false"; - sprintf(tmp, "%s", tmp_val.c_str()); - break; - } - - return_value = tmp; return true; } -bool RuleManager::SetRule(const char *rule_name, const char *rule_value, Database *database, bool db_save, bool reload) { - if(rule_name == nullptr || rule_value == nullptr) - return(false); +bool RuleManager::ListCategories(std::vector &l) +{ + for (int i = 0; i < _CatCount; i++) { + l.push_back(s_categoryNames[i]); + } + + return true; +} + +bool RuleManager::GetRule(const std::string &rule_name, std::string &rule_value) +{ + RuleType type; + uint16 index; + if (!_FindRule(rule_name, type, index)) { + return false; + } + + switch (type) { + case IntRule: + rule_value = fmt::format("{}", m_RuleIntValues[index]); + break; + case RealRule: + rule_value = fmt::format("{}", m_RuleRealValues[index]); + break; + case BoolRule: + rule_value = m_RuleBoolValues[index] ? "true" : "false"; + break; + } + + return true; +} + +bool RuleManager::SetRule(const std::string &rule_name, const std::string &rule_value, Database *db, bool db_save, bool reload) +{ + if (rule_name.empty() || rule_value.empty()) { + return false; + } RuleType type; uint16 index; - if(!_FindRule(rule_name, type, index)) - return(false); - - if (reload) { - if (strcasecmp(rule_name, "World:ExpansionSettings") == 0) - return(false); - if (strcasecmp(rule_name, "World:UseClientBasedExpansionSettings") == 0) - return(false); + if (!_FindRule(rule_name, type, index)) { + return (false); } - switch(type) { + if (reload) { + bool is_client = Strings::EqualFold(rule_name, "World:UseClientBasedExpansionSettings"); + bool is_world = Strings::EqualFold(rule_name, "World:ExpansionSettings"); + if (is_client || is_world) { + return false; + } + } + + switch (type) { case IntRule: - m_RuleIntValues[index] = atoi(rule_value); + m_RuleIntValues[index] = atoi(rule_value.c_str()); LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleIntValues[index]); break; case RealRule: - m_RuleRealValues[index] = atof(rule_value); - LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleRealValues[index]); + m_RuleRealValues[index] = atof(rule_value.c_str()); + LogRules("Set rule [{}] to value [{:.2f}]", rule_name, m_RuleRealValues[index]); break; case BoolRule: - uint32 val = 0; - if (!strcasecmp(rule_value, "on") || !strcasecmp(rule_value, "true") || !strcasecmp(rule_value, "yes") || !strcasecmp(rule_value, "enabled") || !strcmp(rule_value, "1")) - val = 1; - - m_RuleBoolValues[index] = val; + m_RuleBoolValues[index] = static_cast(Strings::ToBool(rule_value)); LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleBoolValues[index] == 1 ? "true" : "false"); break; } - if(db_save) - _SaveRule(database, type, index); + if (db_save) { + _SaveRule(db, type, index); + } - return(true); + return true; } void RuleManager::ResetRules(bool reload) { - std::string expansion1; - std::string expansion2; + std::string client_rule; + std::string world_rule; // these rules must not change during server runtime if (reload) { - GetRule("World:ExpansionSettings", expansion1); - GetRule("World:UseClientBasedExpansionSettings", expansion2); + GetRule("World:UseClientBasedExpansionSettings", client_rule); + GetRule("World:ExpansionSettings", world_rule); } - Log(Logs::Detail, Logs::Rules, "Resetting running rules to default values"); - #define RULE_INT(cat, rule, default_value, notes) \ - m_RuleIntValues[ Int__##rule ] = default_value; - #define RULE_REAL(cat, rule, default_value, notes) \ - m_RuleRealValues[ Real__##rule ] = default_value; - #define RULE_BOOL(cat, rule, default_value, notes) \ - m_RuleBoolValues[ Bool__##rule ] = default_value; + LogRulesDetail("Resetting running rules to default values."); + + #define RULE_INT(category_name, rule_name, default_value, notes) \ + m_RuleIntValues[ Int__##rule_name ] = default_value; + #define RULE_REAL(category_name, rule_name, default_value, notes) \ + m_RuleRealValues[ Real__##rule_name ] = default_value; + #define RULE_BOOL(category_name, rule_name, default_value, notes) \ + m_RuleBoolValues[ Bool__##rule_name ] = default_value; #include "ruletypes.h" // restore these rules to their pre-reset values if (reload) { - SetRule("World:ExpansionSettings", expansion1.c_str(), nullptr, false, false); - SetRule("World:UseClientBasedExpansionSettings", expansion2.c_str(), nullptr, false, false); + SetRule("World:UseClientBasedExpansionSettings", client_rule); + SetRule("World:ExpansionSettings", world_rule); } } -bool RuleManager::_FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into) { - if (rule_name == nullptr) - return(false); +bool RuleManager::_FindRule(const std::string &rule_name, RuleType &type_into, uint16 &index_into) { + if (rule_name.empty()) { + return false; + } - int i; - int rule_count = CountRules(); - for (i = 0; i < rule_count; i++) { - const RuleInfo &rule = s_RuleInfo[i]; - if (strcmp(rule_name, rule.name) == 0) { - type_into = rule.type; - index_into = rule.rule_index; - return(true); + for (int i = 0; i < CountRules(); i++) { + const auto& r = s_RuleInfo[i]; + if (rule_name == r.name) { + type_into = r.type; + index_into = r.rule_index; + return true; } } - Log(Logs::Detail, Logs::Rules, "Unable to find rule '%s'", rule_name); - return(false); + + LogRulesDetail("Unable to find rule '{}'.", rule_name); + return false; } //assumes index is valid! -const char *RuleManager::_GetRuleName(RuleType type, uint16 index) { +std::string RuleManager::_GetRuleName(RuleType type, uint16 index) { switch (type) { - case IntRule: - return(s_RuleInfo[index].name); - case RealRule: - return(s_RuleInfo[index+_IntRuleCount].name); - case BoolRule: - return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount].name); - default: - break; + case IntRule: + return s_RuleInfo[index].name; + case RealRule: + return s_RuleInfo[index + _IntRuleCount].name; + case BoolRule: + return s_RuleInfo[index + _IntRuleCount + _RealRuleCount].name; + default: + break; } - //should never happen - return(s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount].name); // no need to create a string when one already exists... + + return s_RuleInfo[_IntRuleCount + _RealRuleCount + _BoolRuleCount].name; } //assumes index is valid! const std::string &RuleManager::_GetRuleNotes(RuleType type, uint16 index) { switch (type) { - case IntRule: - return(s_RuleInfo[index].notes); - case RealRule: - return(s_RuleInfo[index+_IntRuleCount].notes); - case BoolRule: - return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount].notes); - default: - break; + case IntRule: + return s_RuleInfo[index].notes; + case RealRule: + return s_RuleInfo[index + _IntRuleCount].notes; + case BoolRule: + return s_RuleInfo[index + _IntRuleCount + _RealRuleCount].notes; + default: + break; } - //should never happen - return(s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount].notes); + + return s_RuleInfo[_IntRuleCount + _RealRuleCount + _BoolRuleCount].notes; } -bool RuleManager::LoadRules(Database *database, const char *ruleset_name, bool reload) { - - int ruleset_id = GetRulesetID(database, ruleset_name); - if (ruleset_id < 0) { - Log(Logs::Detail, Logs::Rules, "Failed to find ruleset '%s' for load operation. Canceling.", ruleset_name); - return (false); +bool RuleManager::LoadRules(Database *db, const std::string &rule_set_name, bool reload) { + const auto rule_set_id = RuleSetsRepository::GetRuleSetID(*db, rule_set_name); + if (rule_set_id < 0) { + LogRulesDetail("Failed to find Rule Set {} for load operation. Canceling.", rule_set_name); + return false; } - m_activeRuleset = ruleset_id; - m_activeName = ruleset_name; + m_activeRuleset = rule_set_id; + m_activeName = rule_set_name; /* Load default ruleset values first if we're loading something other than default */ - if (strcasecmp(ruleset_name, "default") != 0) { - std::string default_ruleset_name = "default"; - int default_ruleset_id = GetRulesetID(database, default_ruleset_name.c_str()); + const std::string default_ruleset_name = "default"; + bool is_default = rule_set_name == default_ruleset_name; - if (default_ruleset_id < 0) { - Log(Logs::Detail, - Logs::Rules, - "Failed to find default ruleset '%s' for load operation. Canceling.", - default_ruleset_name.c_str() + if (!is_default) { + const auto default_rule_set_id = RuleSetsRepository::GetRuleSetID(*db, default_ruleset_name); + + if (default_rule_set_id < 0) { + LogRulesDetail( + "Failed to load default Rule Set {} for load operation.", + default_ruleset_name ); - return (false); - } - - Log(Logs::Detail, Logs::Rules, "Processing rule set '%s' (%d) load...", default_ruleset_name.c_str(), default_ruleset_id); - - std::string query = StringFormat( - "SELECT `rule_name`, `rule_value` FROM `rule_values` WHERE `ruleset_id` = %d", - default_ruleset_id - ); - - auto results = database->QueryDatabase(query); - if (!results.Success()) { return false; } - for (auto row = results.begin(); row != results.end(); ++row) { - if (!SetRule(row[0], row[1], nullptr, false, reload)) { - Log(Logs::Detail, Logs::Rules, "Unable to interpret rule record for '%s'", row[0]); + LogRulesDetail("Loading Rule Set {} ({}).", default_ruleset_name, default_rule_set_id); + + const auto& l = RuleValuesRepository::GetWhere( + *db, + fmt::format( + "ruleset_id = {}", + default_rule_set_id + ) + ); + if (l.empty()) { + return false; + } + + for (const auto& e : l) { + if (!SetRule(e.rule_name, e.rule_value, nullptr, false, reload)) { + LogRulesDetail("Unable to interpret rule record for '{}'.", e.rule_name); } } } - Log(Logs::Detail, Logs::Rules, "Processing rule set '%s' (%d) load...", ruleset_name, ruleset_id); + LogRulesDetail("Loading Rule Set {} ({}).", rule_set_name, rule_set_id); - std::string query = StringFormat("SELECT `rule_name`, `rule_value` FROM `rule_values` WHERE `ruleset_id` = %d", ruleset_id); - - auto results = database->QueryDatabase(query); - if (!results.Success()) { + const auto& l = RuleValuesRepository::GetWhere( + *db, + fmt::format( + "ruleset_id = {}", + rule_set_id + ) + ); + if (l.empty()) { return false; } - for (auto row = results.begin(); row != results.end(); ++row) { - if (!SetRule(row[0], row[1], nullptr, false, reload)) { - Log(Logs::Detail, Logs::Rules, "Unable to interpret rule record for '%s'", row[0]); + for (const auto& e : l) { + if (!SetRule(e.rule_name, e.rule_value, nullptr, false, reload)) { + LogRulesDetail("Unable to interpret Rule record for Rule '{}'.", e.rule_name); } } return true; } -void RuleManager::SaveRules(Database *database, const char *ruleset_name) { - - if (ruleset_name != nullptr) { - //saving to a specific name - if (m_activeName != ruleset_name) { - //a new name... - - m_activeRuleset = _FindOrCreateRuleset(database, ruleset_name); +void RuleManager::SaveRules(Database *db, const std::string &rule_set_name) { + if (!rule_set_name.empty()) { + if (m_activeName != rule_set_name) { + m_activeRuleset = _FindOrCreateRuleset(db, rule_set_name); if (m_activeRuleset == -1) { - Log(Logs::Detail, Logs::Rules, "Unable to find or create rule set %s", ruleset_name); + LogRulesDetail("Unable to find or create Rule Set {}.", rule_set_name); return; } - m_activeName = ruleset_name; + + m_activeName = rule_set_name; } - Log(Logs::Detail, Logs::Rules, "Saving running rules into rule set %s (%d)", ruleset_name, m_activeRuleset); - } - else { - Log(Logs::Detail, Logs::Rules, "Saving running rules into running rule set %s", m_activeName.c_str(), m_activeRuleset); + + LogRulesDetail("Saving running rules into Rule Set {} ({}).", rule_set_name, m_activeRuleset); + } else { + LogRulesDetail("Saving running rules into running Rule Set {} ({}).", m_activeName, m_activeRuleset); } int i; for (i = 0; i < _IntRuleCount; i++) { - _SaveRule(database, IntRule, i); + _SaveRule(db, IntRule, i); } + for (i = 0; i < _RealRuleCount; i++) { - _SaveRule(database, RealRule, i); + _SaveRule(db, RealRule, i); } + for (i = 0; i < _BoolRuleCount; i++) { - _SaveRule(database, BoolRule, i); + _SaveRule(db, BoolRule, i); } } -void RuleManager::_SaveRule(Database *database, RuleType type, uint16 index) { - char value_string[100]; +void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) { + const auto rule_name = _GetRuleName(type, index); - if (type == IntRule && strcasecmp(_GetRuleName(type, index), "World:ExpansionSettings") == 0) - return; - if (type == BoolRule && strcasecmp(_GetRuleName(type, index), "World:UseClientBasedExpansionSettings") == 0) + if ( + (type == BoolRule && Strings::EqualFold(rule_name, "World:UseClientBasedExpansionSettings")) || + (type == IntRule && Strings::EqualFold(rule_name, "World:ExpansionSettings")) + ) { return; + } + + std::string rule_value; switch (type) { case IntRule: - sprintf(value_string, "%d", m_RuleIntValues[index]); + rule_value = fmt::format("{}", m_RuleIntValues[index]); break; case RealRule: - sprintf(value_string, "%.13f", m_RuleRealValues[index]); + rule_value = fmt::format("{:.13f}", m_RuleRealValues[index]); break; case BoolRule: - sprintf(value_string, "%s", m_RuleBoolValues[index] ? "true" : "false"); + rule_value = m_RuleBoolValues[index] ? "true" : "false"; break; } - std::string query = StringFormat( - "REPLACE INTO `rule_values`" - "(`ruleset_id`, `rule_name`, `rule_value`, `notes`)" - " VALUES('%d', '%s', '%s', '%s')", + const auto rule_notes = _GetRuleNotes(type, index); + + const auto& l = RuleValuesRepository::GetWhere( + *db, + fmt::format( + "ruleset_id = {} AND rule_name = '{}' LIMIT 1", m_activeRuleset, - _GetRuleName(type, index), - value_string, - Strings::Escape(_GetRuleNotes(type, index)).c_str() + rule_name + ) ); - database->QueryDatabase(query); + if (!l.empty()) { + auto e = l[0]; + e.rule_value = rule_value; + e.notes = rule_notes; + + RuleValuesRepository::UpdateOne(*db, e); + return; + } + + auto e = RuleValuesRepository::NewEntity(); + + e.ruleset_id = m_activeRuleset; + e.rule_name = _GetRuleName(type, index); + e.rule_value = rule_value; + e.notes = rule_notes; + + RuleValuesRepository::InsertOne(*db, e); } -bool RuleManager::UpdateInjectedRules(Database *db, const char *ruleset_name, bool quiet_update) +bool RuleManager::UpdateInjectedRules(Database *db, const std::string &rule_set_name, bool quiet_update) { - std::vector database_data; - std::map> rule_data; + std::map> rule_data; std::vector> injected_rule_entries; - if (ruleset_name == nullptr) { + if (rule_set_name.empty()) { return false; } - int ruleset_id = GetRulesetID(db, ruleset_name); - if (ruleset_id < 0) { + const auto rule_set_id = RuleSetsRepository::GetRuleSetID(*db, rule_set_name); + if (rule_set_id < 0) { return false; } - // load database rule names - std::string query(StringFormat("SELECT `rule_name` FROM `rule_values` WHERE `ruleset_id` = '%i'", ruleset_id)); - - auto results = db->QueryDatabase(query); - if (!results.Success()) { + const auto& v = RuleValuesRepository::GetRuleNames(*db, rule_set_id); + if (v.empty()) { return false; } - // build database data entries - for (auto row : results) { - database_data.push_back(std::string(row[0])); - } - // build rule data entries - for (const auto &ri_iter : s_RuleInfo) { - if (strcasecmp(ri_iter.name, "Invalid Rule") == 0) { + for (const auto& r : s_RuleInfo) { + if (Strings::EqualFold(r.name, "Invalid Rule")) { continue; } - char buffer[100]; - - switch (ri_iter.type) { - case IntRule: - sprintf(buffer, "%d", m_RuleIntValues[ri_iter.rule_index]); - rule_data[ri_iter.name].first = buffer; - rule_data[ri_iter.name].second = &ri_iter.notes; - break; - case RealRule: - sprintf(buffer, "%.13f", m_RuleRealValues[ri_iter.rule_index]); - rule_data[ri_iter.name].first = buffer; - rule_data[ri_iter.name].second = &ri_iter.notes; - break; - case BoolRule: - sprintf(buffer, "%s", (m_RuleBoolValues[ri_iter.rule_index] ? "true" : "false")); - rule_data[ri_iter.name].first = buffer; - rule_data[ri_iter.name].second = &ri_iter.notes; - break; - default: - break; + switch (r.type) { + case IntRule: + rule_data[r.name].first = fmt::format("{}", m_RuleIntValues[r.rule_index]); + rule_data[r.name].second = &r.notes; + break; + case RealRule: + rule_data[r.name].first = fmt::format("{:.13f}", m_RuleRealValues[r.rule_index]); + rule_data[r.name].second = &r.notes; + break; + case BoolRule: + rule_data[r.name].first = fmt::format("{}", m_RuleBoolValues[r.rule_index] ? "true" : "false"); + rule_data[r.name].second = &r.notes; + break; + default: + break; } } // build injected entries - for (const auto &rd_iter : rule_data) { - - const auto &dd_iter = std::find(database_data.begin(), database_data.end(), rd_iter.first); - if (dd_iter == database_data.end()) { - + for (const auto &d : rule_data) { + if (std::find(v.begin(), v.end(), d.first) == v.end()) { injected_rule_entries.push_back( std::tuple( - ruleset_id, // `ruleset_id` - rd_iter.first, // `rule_name` - rd_iter.second.first, // `rule_value` - Strings::Escape(*rd_iter.second.second) // `notes` + rule_set_id, + d.first, + d.second.first, + Strings::Escape(*d.second.second) ) ); if (!quiet_update) { LogInfo( "Adding new rule [{}] ruleset [{}] ({}) value [{}]", - rd_iter.first.c_str(), - ruleset_name, - ruleset_id, - rd_iter.second.first.c_str() + d.first, + rule_set_name, + rule_set_id, + d.second.first ); } } } if (injected_rule_entries.size()) { - - std::string query( - fmt::format( - "REPLACE INTO `rule_values`(`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES {}", - Strings::ImplodePair( - ",", - std::pair('(', ')'), - join_tuple(",", std::pair('\'', '\''), injected_rule_entries) - ) - ) - ); - - if (!db->QueryDatabase(query).Success()) { + if (!RuleValuesRepository::InjectRules(*db, injected_rule_entries)) { return false; } LogInfo( "[{}] New rule(s) added to ruleset [{}] [{}]", injected_rule_entries.size(), - ruleset_name, - ruleset_id + rule_set_name, + rule_set_id ); } @@ -491,50 +480,36 @@ bool RuleManager::UpdateOrphanedRules(Database *db, bool quiet_update) std::vector rule_data; std::vector orphaned_rule_entries; - // load database rule names - std::string query("SELECT `rule_name` FROM `rule_values` GROUP BY `rule_name`"); - - auto results = db->QueryDatabase(query); - if (!results.Success()) { + const auto& l = RuleValuesRepository::GetGroupedRules(*db); + if (l.empty()) { return false; } // build rule data entries - for (const auto &ri_iter : s_RuleInfo) { - if (strcasecmp(ri_iter.name, "Invalid Rule") == 0) { + for (const auto &r : s_RuleInfo) { + if (Strings::EqualFold(r.name, "Invalid Rule")) { continue; } - rule_data.push_back(ri_iter.name); + rule_data.push_back(r.name); } - // build orphaned entries - for (auto row : results) { - - const auto &rd_iter = std::find(rule_data.begin(), rule_data.end(), row[0]); - if (rd_iter == rule_data.end()) { - - orphaned_rule_entries.push_back(std::string(row[0])); + for (const auto& e : l) { + const auto &d = std::find(rule_data.begin(), rule_data.end(), e); + if (d == rule_data.end()) { + orphaned_rule_entries.push_back(e); if (!quiet_update) { LogInfo( "Rule [{}] no longer exists... Deleting orphaned entry from `rule_values` table...", - row[0] + e ); } } } if (orphaned_rule_entries.size()) { - - std::string query ( - fmt::format( - "DELETE FROM `rule_values` WHERE `rule_name` IN ({})", - Strings::ImplodePair(",", std::pair('\'', '\''), orphaned_rule_entries) - ) - ); - - if (!db->QueryDatabase(query).Success()) { + if (!RuleValuesRepository::DeleteOrphanedRules(*db, orphaned_rule_entries)) { return false; } @@ -546,139 +521,83 @@ bool RuleManager::UpdateOrphanedRules(Database *db, bool quiet_update) bool RuleManager::RestoreRuleNotes(Database *db) { - std::string query("SELECT `ruleset_id`, `rule_name`, `notes` FROM `rule_values`"); - - auto results = db->QueryDatabase(query); - if (!results.Success()) { + const auto& l = RuleValuesRepository::All(*db); + if (l.empty()) { return false; } int update_count = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - - auto rule = [](const char *rule_name) { + for (const auto& e : l) { + auto rule = [](std::string rule_name) { for (auto rule_iter : s_RuleInfo) { - if (strcasecmp(rule_iter.name, rule_name) == 0) { + if (Strings::EqualFold(rule_iter.name, rule_name)) { return rule_iter; } } - return s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount]; - }(row[1]); + return s_RuleInfo[_IntRuleCount + _RealRuleCount + _BoolRuleCount]; + }(e.rule_name); - if (strcasecmp(rule.name, row[1]) != 0) { + if (Strings::Contains(rule.name, e.rule_name)) { continue; } - if (row[2] != nullptr && rule.notes.compare(row[2]) == 0) { + if (!e.notes.empty() && !rule.notes.compare(e.notes)) { continue; } - std::string query( - fmt::format( - "UPDATE `rule_values` SET `notes` = '{}' WHERE `ruleset_id` = '{}' AND `rule_name` = '{}'", - Strings::Escape(rule.notes), - row[0], - row[1] - ) - ); - - if (!db->QueryDatabase(query).Success()) { + if (!RuleValuesRepository::UpdateRuleNote(*db, e.ruleset_id, e.rule_name, rule.notes)) { continue; } ++update_count; } - if (update_count > 0) { - LogInfo("[{}] Rule Note [{}] Restored", update_count, (update_count == 1 ? "" : "s")); + if (update_count) { + LogInfo("[{}] Rule Note{} Restored", update_count, update_count != 1 ? "s" : ""); } return true; } -int RuleManager::GetRulesetID(Database *database, const char *ruleset_name) { +int RuleManager::_FindOrCreateRuleset(Database *db, const std::string &rule_set_name) +{ + const auto rule_set_id = RuleSetsRepository::GetRuleSetID(*db, rule_set_name); + if (rule_set_id >= 0) { + return rule_set_id; + } - uint32 len = static_cast(strlen(ruleset_name)); - auto rst = new char[2 * len + 1]; - database->DoEscapeString(rst, ruleset_name, len); - - std::string query = StringFormat("SELECT ruleset_id FROM rule_sets WHERE name='%s'", rst); - safe_delete_array(rst); - auto results = database->QueryDatabase(query); - if (!results.Success()) - return -1; - - if (results.RowCount() == 0) - return -1; - - auto row = results.begin(); - - return atoi(row[0]); + return RuleSetsRepository::CreateNewRuleSet(*db, rule_set_name); } -int RuleManager::_FindOrCreateRuleset(Database *database, const char *in_ruleset_name) { +bool RuleManager::ListRulesets(Database *db, std::map &m) +{ + m[0] = "default"; - int ruleset_id = GetRulesetID(database, in_ruleset_name); - if (ruleset_id >= 0) - return ruleset_id; //found and existing one... - - uint32 len = strlen(in_ruleset_name); - auto ruleset_name = new char[2 * len + 1]; - database->DoEscapeString(ruleset_name, in_ruleset_name, len); - - std::string query = StringFormat("INSERT INTO rule_sets (ruleset_id, name) VALUES(0, '%s')", ruleset_name); - safe_delete_array(ruleset_name); - auto results = database->QueryDatabase(query); - if (!results.Success()) - return -1; - - return results.LastInsertedID(); -} - -std::string RuleManager::GetRulesetName(Database *database, int ruleset_id) { - std::string query = StringFormat("SELECT name FROM rule_sets WHERE ruleset_id=%d", ruleset_id); - auto results = database->QueryDatabase(query); - if (!results.Success()) - return ""; - - if (results.RowCount() == 0) - return ""; - - auto row = results.begin(); - - return row[0]; -} - -bool RuleManager::ListRulesets(Database *database, std::map &into) { - - //start out with the default set which is always present. - into[0] = "default"; - - std::string query = "SELECT ruleset_id, name FROM rule_sets"; - auto results = database->QueryDatabase(query); - if (results.Success()) + const auto& l = RuleSetsRepository::All(*db); + if (l.empty()) { return false; + } - for (auto row = results.begin(); row != results.end(); ++row) - into[atoi(row[0])] = row[1]; + for (const auto& e : l) { + m[e.ruleset_id] = e.name; + } return true; } -int32 RuleManager::GetIntRule(RuleManager::IntType t) const +int RuleManager::GetIntRule(RuleManager::IntType t) const { - return (m_RuleIntValues[t]); + return m_RuleIntValues[t]; } float RuleManager::GetRealRule(RuleManager::RealType t) const { - return (m_RuleRealValues[t]); + return m_RuleRealValues[t]; } bool RuleManager::GetBoolRule(RuleManager::BoolType t) const { - return (m_RuleBoolValues[t] == 1); + return m_RuleBoolValues[t] == 1; } - diff --git a/common/rulesys.h b/common/rulesys.h index e9d3365fe..28e66f105 100644 --- a/common/rulesys.h +++ b/common/rulesys.h @@ -29,12 +29,12 @@ //note, these macros assume there is always a RuleManager *rules in scope, //which makes it a global for now, but with instancing we will do exactly //what we do with the zone global and just make it a member of core classes -#define RuleI(cat, rule) \ - RuleManager::Instance()->GetIntRule( RuleManager::Int__##rule ) -#define RuleR(cat, rule) \ - RuleManager::Instance()->GetRealRule( RuleManager::Real__##rule ) -#define RuleB(cat, rule) \ - RuleManager::Instance()->GetBoolRule( RuleManager::Bool__##rule ) +#define RuleI(category_name, rule_name) \ + RuleManager::Instance()->GetIntRule( RuleManager::Int__##rule_name ) +#define RuleR(category_name, rule_name) \ + RuleManager::Instance()->GetRealRule( RuleManager::Real__##rule_name ) +#define RuleB(category_name, rule_name) \ + RuleManager::Instance()->GetBoolRule( RuleManager::Bool__##rule_name ) #include @@ -49,91 +49,101 @@ class RuleManager { public: //generate our rule enums: typedef enum { - #define RULE_INT(cat, rule, default_value, notes) \ - Int__##rule, - #include "ruletypes.h" +#define RULE_INT(category_name, rule_name, default_value, notes) \ + Int__##rule_name, + +#include "ruletypes.h" + _IntRuleCount } IntType; typedef enum { - #define RULE_REAL(cat, rule, default_value, notes) \ - Real__##rule, - #include "ruletypes.h" +#define RULE_REAL(category_name, rule_name, default_value, notes) \ + Real__##rule_name, + +#include "ruletypes.h" + _RealRuleCount } RealType; typedef enum { - #define RULE_BOOL(cat, rule, default_value, notes) \ - Bool__##rule, - #include "ruletypes.h" +#define RULE_BOOL(category_name, rule_name, default_value, notes) \ + Bool__##rule_name, + +#include "ruletypes.h" + _BoolRuleCount } BoolType; typedef enum { - #define RULE_CATEGORY(catname) \ - Category__##catname, - #include "ruletypes.h" +#define RULE_CATEGORY(category_name) \ + Category__##category_name, + +#include "ruletypes.h" + _CatCount } CategoryType; - static RuleManager* Instance() { + static RuleManager *Instance() + { static RuleManager rules; return &rules; } - static const IntType InvalidInt = _IntRuleCount; - static const RealType InvalidReal = _RealRuleCount; - static const BoolType InvalidBool = _BoolRuleCount; + static const IntType InvalidInt = _IntRuleCount; + static const RealType InvalidReal = _RealRuleCount; + static const BoolType InvalidBool = _BoolRuleCount; static const CategoryType InvalidCategory = _CatCount; - static const uint32 _RulesCount = _IntRuleCount+_RealRuleCount+_BoolRuleCount; + static const uint32 _RulesCount = _IntRuleCount + _RealRuleCount + _BoolRuleCount; //fetch routines, you should generally use the Rule* macros instead of this - int32 GetIntRule (IntType t) const; + int GetIntRule(IntType t) const; float GetRealRule(RealType t) const; bool GetBoolRule(BoolType t) const; //management routines - static const char *GetRuleName(IntType t) { return(s_RuleInfo[t].name); } - static const char *GetRuleName(RealType t) { return(s_RuleInfo[t+_IntRuleCount].name); } - static const char *GetRuleName(BoolType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount].name); } - static const std::string &GetRuleNotes(IntType t) { return(s_RuleInfo[t].notes); } - static const std::string &GetRuleNotes(RealType t) { return(s_RuleInfo[t+_IntRuleCount].notes); } - static const std::string &GetRuleNotes(BoolType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount].notes); } - static uint32 CountRules() { return(_RulesCount); } - static CategoryType FindCategory(const char *catname); - bool ListRules(const char *catname, std::vector &into); - bool ListCategories(std::vector &into); - bool GetRule(const char *rule_name, std::string &ret_val); - bool SetRule(const char *rule_name, const char *rule_value, Database *db = nullptr, bool db_save = false, bool reload = false); + static std::string GetRuleName(IntType t) { return s_RuleInfo[t].name; } + static std::string GetRuleName(RealType t) { return s_RuleInfo[t + _IntRuleCount].name; } + static std::string GetRuleName(BoolType t) { return s_RuleInfo[t + _IntRuleCount + _RealRuleCount].name; } + static const std::string &GetRuleNotes(IntType t) { return s_RuleInfo[t].notes; } + static const std::string &GetRuleNotes(RealType t) { return s_RuleInfo[t + _IntRuleCount].notes; } + static const std::string &GetRuleNotes(BoolType t) { return s_RuleInfo[t + _IntRuleCount + _RealRuleCount].notes; } + static uint32 CountRules() { return _RulesCount; } + static CategoryType FindCategory(const std::string &category_name); + bool ListRules(const std::string &category_name, std::vector &l); + bool ListCategories(std::vector &l); + bool GetRule(const std::string &rule_name, std::string &rule_value); + bool SetRule( + const std::string &rule_name, + const std::string &rule_value, + Database *db = nullptr, + bool db_save = false, + bool reload = false + ); - int GetActiveRulesetID() const { return(m_activeRuleset); } - const char *GetActiveRuleset() const { return(m_activeName.c_str()); } - static int GetRulesetID(Database *db, const char *rulesetname); - static std::string GetRulesetName(Database *db, int id); - static bool ListRulesets(Database *db, std::map &into); + int GetActiveRulesetID() const { return m_activeRuleset; } + std::string GetActiveRuleset() const { return m_activeName; } + static bool ListRulesets(Database *db, std::map &l); void ResetRules(bool reload = false); - bool LoadRules(Database *db, const char *ruleset = nullptr, bool reload = false); - void SaveRules(Database *db, const char *ruleset = nullptr); - bool UpdateInjectedRules(Database *db, const char *ruleset_name, bool quiet_update = false); + bool LoadRules(Database *db, const std::string &rule_set_name, bool reload = false); + void SaveRules(Database *db, const std::string &rule_set_name); + 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); private: RuleManager(); - RuleManager(const RuleManager&); - const RuleManager& operator=(const RuleManager&); + RuleManager(const RuleManager &); + const RuleManager &operator=(const RuleManager &); - int m_activeRuleset; + int m_activeRuleset; std::string m_activeName; -#ifdef WIN64 - uint32 m_RuleIntValues [_IntRuleCount ]; -#else - int m_RuleIntValues [_IntRuleCount ]; -#endif - float m_RuleRealValues[_RealRuleCount]; - uint32 m_RuleBoolValues[_BoolRuleCount]; + + int m_RuleIntValues[_IntRuleCount]; + float m_RuleRealValues[_RealRuleCount]; + uint32 m_RuleBoolValues[_BoolRuleCount]; typedef enum { IntRule, @@ -141,23 +151,21 @@ private: BoolRule } RuleType; - static bool _FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into); - static const char *_GetRuleName(RuleType type, uint16 index); + static bool _FindRule(const std::string &rule_name, RuleType &type_into, uint16 &index_into); + static std::string _GetRuleName(RuleType type, uint16 index); static const std::string &_GetRuleNotes(RuleType type, uint16 index); - static int _FindOrCreateRuleset(Database *db, const char *ruleset); + static int _FindOrCreateRuleset(Database *db, const std::string &rule_set_name); void _SaveRule(Database *db, RuleType type, uint16 index); - - static const char *s_categoryNames[]; + + static const char* s_categoryNames[]; typedef struct { - const char *name; - CategoryType category; - RuleType type; - uint16 rule_index; //index into its 'type' array + std::string name; + CategoryType category; + RuleType type; + uint16 rule_index; const std::string notes; } RuleInfo; static const RuleInfo s_RuleInfo[]; - }; #endif /*RULESYS_H_*/ - diff --git a/common/strings.cpp b/common/strings.cpp index fd31e3272..2e21eb401 100644 --- a/common/strings.cpp +++ b/common/strings.cpp @@ -721,3 +721,20 @@ uint32 Strings::TimeToSeconds(std::string time_string) return duration; } + +bool Strings::ToBool(std::string bool_string) +{ + if ( + Strings::Contains(bool_string, "true") || + Strings::Contains(bool_string, "y") || + Strings::Contains(bool_string, "yes") || + Strings::Contains(bool_string, "on") || + Strings::Contains(bool_string, "enable") || + Strings::Contains(bool_string, "enabled") || + (Strings::IsNumber(bool_string) && std::stoi(bool_string)) + ) { + return true; + } + + return false; +} diff --git a/common/strings.h b/common/strings.h index b4df943f2..ed6810767 100644 --- a/common/strings.h +++ b/common/strings.h @@ -110,6 +110,8 @@ public: static std::vector Wrap(std::vector &src, std::string character); static void FindReplace(std::string &string_subject, const std::string &search_string, const std::string &replace_string); static uint32 TimeToSeconds(std::string time_string); + static bool ToBool(std::string bool_string); + static inline bool EqualFold(const std::string &string_one, const std::string &string_two) { return strcasecmp(string_one.c_str(), string_two.c_str()) == 0; } template static std::string diff --git a/zone/client.cpp b/zone/client.cpp index 7595d8f15..3884cc56f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -11837,7 +11837,7 @@ bool Client::SendGMCommand(std::string message, bool ignore_status) { void Client::RegisterBug(BugReport_Struct* r) { if (!r) { return; - }; + } auto b = BugReportsRepository::NewEntity(); diff --git a/zone/command.h b/zone/command.h index 1007e9173..743c1f211 100644 --- a/zone/command.h +++ b/zone/command.h @@ -32,6 +32,7 @@ void ListModifyNPCStatMap(Client *c); std::map GetModifyNPCStatMap(); std::string GetModifyNPCStatDescription(std::string stat); void SendNPCEditSubCommands(Client *c); +void SendRuleSubCommands(Client *c); // Commands void command_acceptrules(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/rules.cpp b/zone/gm_commands/rules.cpp index 65ef8bc37..2975e4575 100755 --- a/zone/gm_commands/rules.cpp +++ b/zone/gm_commands/rules.cpp @@ -1,232 +1,451 @@ #include "../client.h" +#include "../command.h" + +#include "../../common/repositories/rule_sets_repository.h" +#include "../../common/repositories/rule_values_repository.h" void command_rules(Client *c, const Seperator *sep) { - //super-command for managing rules settings - if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #rules [subcommand]."); - c->Message(Chat::White, "-- Rule Set Manipulation --"); - c->Message(Chat::White, "...listsets - List avaliable rule sets"); - c->Message(Chat::White, "...current - gives the name of the ruleset currently running in this zone"); - c->Message(Chat::White, "...reload - Reload the selected ruleset in this zone"); - c->Message(Chat::White, "...switch (ruleset name) - Change the selected ruleset and load it"); - c->Message( - Chat::White, - "...load (ruleset name) - Load a ruleset in just this zone without changing the selected set" - ); -//too lazy to write this right now: -// c->Message(Chat::White, "...wload (ruleset name) - Load a ruleset in all zones without changing the selected set"); - c->Message(Chat::White, "...store [ruleset name] - Store the running ruleset as the specified name"); - c->Message(Chat::White, "---------------------"); - c->Message(Chat::White, "-- Running Rule Manipulation --"); - c->Message(Chat::White, "...reset - Reset all rules to their default values"); - c->Message(Chat::White, "...get [rule] - Get the specified rule's local value"); - c->Message(Chat::White, "...set (rule) (value) - Set the specified rule to the specified value locally only"); - c->Message( - Chat::White, - "...setdb (rule) (value) - Set the specified rule to the specified value locally and in the DB" - ); - c->Message( - Chat::White, - "...list [catname] - List all rules in the specified category (or all categiries if omitted)" - ); - c->Message(Chat::White, "...values [catname] - List the value of all rules in the specified category"); + auto arguments = sep->argnum; + bool is_help = !strcasecmp(sep->arg[1], "help"); + if (!arguments || is_help) { + SendRuleSubCommands(c); return; } - if (!strcasecmp(sep->arg[1], "current")) { - c->Message( - Chat::White, "Currently running ruleset '%s' (%d)", RuleManager::Instance()->GetActiveRuleset(), - RuleManager::Instance()->GetActiveRulesetID()); + bool is_current = !strcasecmp(sep->arg[1], "current"); + bool is_get = !strcasecmp(sep->arg[1], "get"); + bool is_list = !strcasecmp(sep->arg[1], "list"); + bool is_list_sets = !strcasecmp(sep->arg[1], "listsets"); + bool is_load = !strcasecmp(sep->arg[1], "load"); + bool is_reload = !strcasecmp(sep->arg[1], "reload"); + bool is_reset = !strcasecmp(sep->arg[1], "reset"); + bool is_set = !strcasecmp(sep->arg[1], "set"); + bool is_set_db = !strcasecmp(sep->arg[1], "setdb"); + bool is_store = !strcasecmp(sep->arg[1], "store"); + bool is_switch = !strcasecmp(sep->arg[1], "switch"); + bool is_values = !strcasecmp(sep->arg[1], "values"); + if ( + !is_current && + !is_get && + !is_list && + !is_list_sets && + !is_load && + !is_reload && + !is_reset && + !is_set && + !is_set_db && + !is_store && + !is_switch && + !is_values + ) { + SendRuleSubCommands(c); + return; } - else if (!strcasecmp(sep->arg[1], "listsets")) { - std::map sets; - if (!RuleManager::Instance()->ListRulesets(&database, sets)) { - c->Message(Chat::Red, "Failed to list rule sets!"); + + if (is_current) { + c->Message( + Chat::White, + fmt::format( + "Currently running Rule Set {} ({}).", + RuleManager::Instance()->GetActiveRuleset(), + RuleManager::Instance()->GetActiveRulesetID() + ).c_str() + ); + } else if (is_list_sets) { + std::map m; + if (!RuleManager::Instance()->ListRulesets(&database, m)) { + c->Message(Chat::White, "Failed to list Rule Sets!"); return; } - c->Message(Chat::White, "Avaliable rule sets:"); - std::map::iterator cur, end; - cur = sets.begin(); - end = sets.end(); - for (; cur != end; ++cur) { - c->Message(Chat::White, "(%d) %s", cur->first, cur->second.c_str()); + if (m.empty()) { + c->Message(Chat::White, "There are no available Rule Sets!"); + return; } - } - else if (!strcasecmp(sep->arg[1], "reload")) { + + c->Message(Chat::White, "Available Rule Sets:"); + + auto rule_set_count = 0; + auto rule_set_number = 1; + + for (const auto& e : m) { + c->Message( + Chat::White, + fmt::format( + "Rule Set {} | {} ({})", + e.second, + e.first + ).c_str() + ); + } + + c->Message( + Chat::White, + fmt::format( + "There are {} available Rule Set{}.", + rule_set_count, + rule_set_count != 1 ? "s" : "" + ).c_str() + ); + } else if (is_reload) { RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true); c->Message( - Chat::White, "The active ruleset (%s (%d)) has been reloaded", RuleManager::Instance()->GetActiveRuleset(), - RuleManager::Instance()->GetActiveRulesetID()); - } - else if (!strcasecmp(sep->arg[1], "switch")) { + Chat::White, + fmt::format( + "Active Rule Set {} ({}) has been reloaded.", + RuleManager::Instance()->GetActiveRuleset(), + RuleManager::Instance()->GetActiveRulesetID() + ).c_str() + ); + } else if (is_switch) { //make sure this is a valid rule set.. - int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); + const auto rsid = RuleSetsRepository::GetRuleSetID(database, sep->arg[2]); if (rsid < 0) { - c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); - return; - } - if (!database.SetVariable("RuleSet", sep->arg[2])) { - c->Message(Chat::Red, "Failed to update variables table to change selected rule set"); + c->Message( + Chat::White, + fmt::format( + "Rule Set '{}' does not exist or is invalid.", + sep->arg[2] + ).c_str() + ); + return; + } + + if (!database.SetVariable("RuleSet", sep->arg[2])) { + c->Message(Chat::White, "Failed to update variables table to change selected Rule Set."); return; } - //TODO: we likely want to reload this ruleset everywhere... RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); c->Message( Chat::White, - "The selected ruleset has been changed to (%s (%d)) and reloaded locally", + "The selected ruleset has been changed to {} ({}) and reloaded locally.", sep->arg[2], rsid ); - } - else if (!strcasecmp(sep->arg[1], "load")) { - //make sure this is a valid rule set.. - int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); + } else if (is_load) { + const auto rsid = RuleSetsRepository::GetRuleSetID(database, sep->arg[2]); if (rsid < 0) { - c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); + c->Message( + Chat::White, + fmt::format( + "Rule Set '{}' does not exist or is invalid.", + sep->arg[2] + ).c_str() + ); return; } + RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); - c->Message(Chat::White, "Loaded ruleset '%s' (%d) locally", sep->arg[2], rsid); - } - else if (!strcasecmp(sep->arg[1], "store")) { - if (sep->argnum == 1) { - //store current rule set. - RuleManager::Instance()->SaveRules(&database); - c->Message(Chat::White, "Rules saved"); - } - else if (sep->argnum == 2) { + c->Message( + Chat::White, + fmt::format( + "Loaded Rule Set {} ({}) locally.", + sep->arg[2], + rsid + ).c_str() + ); + } else if (is_store) { + if (arguments == 1) { + RuleManager::Instance()->SaveRules(&database, ""); + c->Message(Chat::White, "Rules saved."); + } else if (arguments == 2) { RuleManager::Instance()->SaveRules(&database, sep->arg[2]); - int prersid = RuleManager::Instance()->GetActiveRulesetID(); - int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); + const auto prersid = RuleManager::Instance()->GetActiveRulesetID(); + const auto rsid = RuleSetsRepository::GetRuleSetID(database, sep->arg[2]); if (rsid < 0) { - c->Message(Chat::Red, "Unable to query ruleset ID after store, it most likely failed."); - } - else { - c->Message(Chat::White, "Stored rules as ruleset '%s' (%d)", sep->arg[2], rsid); + c->Message(Chat::White, "Unable to query Rule Set ID after store."); + } else { + c->Message( + Chat::White, + fmt::format( + "Stored rules as Rule Set {} ({}).", + sep->arg[2], + rsid + ).c_str() + ); + if (prersid != rsid) { - c->Message(Chat::White, "Rule set %s (%d) is now active in this zone", sep->arg[2], rsid); + c->Message( + Chat::White, + fmt::format( + "Rule Set {} ({}) is now active locally.", + sep->arg[2], + rsid + ).c_str() + ); } } - } - else { - c->Message(Chat::Red, "Invalid argument count, see help."); + } else { + SendRuleSubCommands(c); return; } - } - else if (!strcasecmp(sep->arg[1], "reset")) { + } else if (is_reset) { RuleManager::Instance()->ResetRules(true); - c->Message(Chat::White, "The running ruleset has been set to defaults"); - - } - else if (!strcasecmp(sep->arg[1], "get")) { - if (sep->argnum != 2) { - c->Message(Chat::Red, "Invalid argument count, see help."); + c->Message( + Chat::White, + fmt::format( + "Rule Set {} ({}) has been set to defaults.", + RuleManager::Instance()->GetActiveRuleset(), + RuleManager::Instance()->GetActiveRulesetID() + ).c_str() + ); + } else if (is_get) { + if (arguments == 2) { + std::string value; + if (!RuleManager::Instance()->GetRule(sep->arg[2], value)) { + c->Message( + Chat::White, + fmt::format( + "Unable to find rule '{}'.", + sep->arg[2] + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "{} has a value of {}.", + sep->arg[2], + value + ).c_str() + ); + } + } else { + SendRuleSubCommands(c); return; } - std::string value; - if (!RuleManager::Instance()->GetRule(sep->arg[2], value)) { - c->Message(Chat::Red, "Unable to find rule %s", sep->arg[2]); - } - else { - c->Message(Chat::White, "%s - %s", sep->arg[2], value.c_str()); - } - - } - else if (!strcasecmp(sep->arg[1], "set")) { - if (sep->argnum != 3) { - c->Message(Chat::Red, "Invalid argument count, see help."); + } else if (is_set) { + if (arguments == 3) { + if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], nullptr, false, true)) { + c->Message( + Chat::White, + fmt::format( + "Failed to modify Rule {} to a value of {}.", + sep->arg[2], + sep->arg[3] + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "Rule {} modified locally to a value of {}.", + sep->arg[2], + sep->arg[3] + ).c_str() + ); + } + } else { + SendRuleSubCommands(c); return; } - if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], nullptr, false, true)) { - c->Message(Chat::Red, "Failed to modify rule"); - } - else { - c->Message(Chat::White, "Rule modified locally."); - } - } - else if (!strcasecmp(sep->arg[1], "setdb")) { - if (sep->argnum != 3) { - c->Message(Chat::Red, "Invalid argument count, see help."); + } else if (is_set_db) { + if (arguments == 3) { + if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], &database, true, true)) { + c->Message( + Chat::White, + fmt::format( + "Failed to modify Rule {} to a value of {}.", + sep->arg[2], + sep->arg[3] + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "Rule {} modified locally and in database to a value of {}.", + sep->arg[2], + sep->arg[3] + ).c_str() + ); + } + } else { + SendRuleSubCommands(c); return; } - if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], &database, true, true)) { - c->Message(Chat::Red, "Failed to modify rule"); - } - else { - c->Message(Chat::White, "Rule modified locally and in the database."); - } - } - else if (!strcasecmp(sep->arg[1], "list")) { - if (sep->argnum == 1) { - std::vector rule_list; - if (!RuleManager::Instance()->ListCategories(rule_list)) { - c->Message(Chat::Red, "Failed to list categories!"); + } else if (is_list) { + if (arguments == 1) { + std::vector l; + if (!RuleManager::Instance()->ListCategories(l)) { + c->Message(Chat::White, "Failed to list Rule Categories!"); return; } + + if (l.empty()) { + c->Message(Chat::White, "There are no Rule Categories to list!"); + return; + } + c->Message(Chat::White, "Rule Categories:"); - std::vector::iterator cur, end; - cur = rule_list.begin(); - end = rule_list.end(); - for (; cur != end; ++cur) { - c->Message(Chat::White, " %s", *cur); + + auto rule_category_count = 0; + auto rule_category_number = 1; + + for (const auto& e : l) { + c->Message( + Chat::White, + fmt::format( + "Rule Category {} | {}", + rule_category_number, + e + ).c_str() + ); + + rule_category_count++; + rule_category_number++; } - } - else if (sep->argnum == 2) { - const char *catfilt = nullptr; + + c->Message( + Chat::White, + fmt::format( + "There {} {} available Rule Categor{}.", + rule_category_count != 1 ? "are" : "is", + rule_category_count, + rule_category_count != 1 ? "ies" : "y" + ).c_str() + ); + } else if (arguments == 2) { + std::string category_name; if (std::string("all") != sep->arg[2]) { - catfilt = sep->arg[2]; + category_name = sep->arg[2]; } - std::vector rule_list; - if (!RuleManager::Instance()->ListRules(catfilt, rule_list)) { - c->Message(Chat::Red, "Failed to list rules!"); + + std::vector l; + if (!RuleManager::Instance()->ListRules(category_name, l)) { + c->Message(Chat::White, "Failed to list rules!"); return; } - c->Message(Chat::White, "Rules in category %s:", sep->arg[2]); - std::vector::iterator cur, end; - cur = rule_list.begin(); - end = rule_list.end(); - for (; cur != end; ++cur) { - c->Message(Chat::White, " %s", *cur); + + c->Message( + Chat::White, + fmt::format( + "Rules in {} Category:", + category_name + ).c_str() + ); + + auto rule_count = 0; + auto rule_number = 1; + + for (const auto& e : l) { + c->Message( + Chat::White, + fmt::format( + "Rule {} | {}", + rule_number, + e + ).c_str() + ); + + rule_count++; + rule_number++; } - } - else { - c->Message(Chat::Red, "Invalid argument count, see help."); - } - } - else if (!strcasecmp(sep->arg[1], "values")) { - if (sep->argnum != 2) { - c->Message(Chat::Red, "Invalid argument count, see help."); + + c->Message( + Chat::White, + fmt::format( + "There {} {} available Rule{} in the {} Category.", + rule_count != 1 ? "are" : "is", + rule_count, + rule_count != 1 ? "s" : "", + category_name + ).c_str() + ); + } else { + SendRuleSubCommands(c); return; } - else { - const char *catfilt = nullptr; + } else if (is_values) { + if (arguments == 2) { + std::string category_name; if (std::string("all") != sep->arg[2]) { - catfilt = sep->arg[2]; + category_name = sep->arg[2]; } - std::vector rule_list; - if (!RuleManager::Instance()->ListRules(catfilt, rule_list)) { - c->Message(Chat::Red, "Failed to list rules!"); + + std::vector l; + if (!RuleManager::Instance()->ListRules(category_name, l)) { + c->Message(Chat::White, "Failed to list rules!"); return; } - c->Message(Chat::White, "Rules & values in category %s:", sep->arg[2]); - std::vector::iterator cur, end; - cur = rule_list.begin(); - end = rule_list.end(); - for (std::string tmp_value; cur != end; ++cur) { - if (RuleManager::Instance()->GetRule(*cur, tmp_value)) { - c->Message(Chat::White, " %s - %s", *cur, tmp_value.c_str()); + + c->Message( + Chat::White, + fmt::format( + "Rule Values in {} Category:", + category_name + ).c_str() + ); + + auto rule_count = 0; + auto rule_number = 1; + std::string rule_value; + + for (const auto& e : l) { + if (RuleManager::Instance()->GetRule(e, rule_value)) { + c->Message( + Chat::White, + fmt::format( + "Rule {} | Name: {} Value: {}", + rule_number, + e, + rule_value + ).c_str() + ); + + rule_count++; + rule_number++; } } - } - } - else { - c->Message(Chat::Yellow, "Invalid action specified. use '#rules help' for help"); + c->Message( + Chat::White, + fmt::format( + "There {} {} available Rule{} in the {} Category.", + rule_count != 1 ? "are" : "is", + rule_count, + rule_count != 1 ? "s" : "", + category_name + ).c_str() + ); + } else { + SendRuleSubCommands(c); + return; + } } } - +void SendRuleSubCommands(Client *c) +{ + c->Message(Chat::White, "Usage: #rule listsets - List available rule sets"); + c->Message(Chat::White, "Usage: #rule current - gives the name of the ruleset currently running in this zone"); + c->Message(Chat::White, "Usage: #rule reload - Reload the selected ruleset in this zone"); + c->Message(Chat::White, "Usage: #rule switch [Ruleset Name] - Change the selected ruleset and load it"); + c->Message( + Chat::White, + "Usage: #rule load [Ruleset Name] - Load a ruleset in just this zone without changing the selected set" + ); + c->Message(Chat::White, "Usage: #rule store [Ruleset Name] - Store the running ruleset as the specified name"); + c->Message(Chat::White, "Usage: #rule reset - Reset all rules to their default values"); + c->Message(Chat::White, "Usage: #rule get [Rule] - Get the specified rule's local value"); + c->Message( + Chat::White, + "Usage: #rule set [Rule) [Value] - Set the specified rule to the specified value locally only" + ); + c->Message( + Chat::White, + "Usage: #rule setdb [Rule] [Value] - Set the specified rule to the specified value locally and in the DB" + ); + c->Message( + Chat::White, + "Usage: #rule list [Category Name] - List all rules in the specified category (or all categiries if omitted)" + ); + c->Message( + Chat::White, + "Usage: #rule values [Category Name] - List the value of all rules in the specified category" + ); + return; +} diff --git a/zone/zone.cpp b/zone/zone.cpp index dcf4bf123..8a2d6422c 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -59,6 +59,7 @@ #include "zone_reload.h" #include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/repositories/content_flags_repository.h" +#include "../common/repositories/rule_sets_repository.h" #include "../common/repositories/zone_points_repository.h" #include "../common/serverinfo.h" @@ -1113,7 +1114,7 @@ bool Zone::Init(bool is_static) { } // if that fails, try the file name, then load defaults if (RuleManager::Instance()->GetActiveRulesetID() != default_ruleset) { - std::string r_name = RuleManager::Instance()->GetRulesetName(&database, default_ruleset); + std::string r_name = RuleSetsRepository::GetRuleSetName(database, default_ruleset); if (r_name.size() > 0) { RuleManager::Instance()->LoadRules(&database, r_name.c_str(), false); }