[Commands] Cleanup #rules Command. (#2593)

* [Commands] Cleanup #rules Command.

- Cleanup messages and logic.
- Rewrite all rules logic to use `std::string` and repositories.

* References

* Update rules.cpp

* Strings::Equal and Strings::EqualFold.

* Cleanup.

* Update rulesys.cpp

* Update rulesys.cpp

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
Alex King 2022-12-10 18:08:55 -05:00 committed by GitHub
parent 3872555332
commit b1c4e7c23f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 926 additions and 632 deletions

View File

@ -44,7 +44,53 @@ public:
*/ */
// Custom extended repository methods here // 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<int>(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 #endif //EQEMU_RULE_SETS_REPOSITORY_H

View File

@ -44,6 +44,87 @@ public:
*/ */
// Custom extended repository methods here // Custom extended repository methods here
static std::vector<std::string> GetRuleNames(Database &db, int rule_set_id)
{
std::vector<std::string> 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<std::string> GetGroupedRules(Database &db)
{
std::vector <std::string> 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<std::string>& v)
{
const auto query = fmt::format(
"DELETE FROM {} WHERE rule_name IN ({})",
TableName(),
Strings::ImplodePair(",", std::pair<char, char>('\'', '\''), v)
);
return db.QueryDatabase(query).Success();
}
static bool InjectRules(Database& db, std::vector<std::tuple<int, std::string, std::string, std::string>>& v)
{
const auto query = fmt::format(
"REPLACE INTO {} (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES {}",
TableName(),
Strings::ImplodePair(
",",
std::pair<char, char>('(', ')'),
join_tuple(",", std::pair<char, char>('\'', '\''), 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();
}
}; };

View File

@ -24,462 +24,451 @@
#include <cstring> #include <cstring>
#include <fmt/format.h> #include <fmt/format.h>
/* #include "../common/repositories/rule_sets_repository.h"
Commands: #include "../common/repositories/rule_values_repository.h"
#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/
*/
const char *RuleManager::s_categoryNames[_CatCount+1] = { const char *RuleManager::s_categoryNames[_CatCount + 1] = {
#define RULE_CATEGORY(catname) \ #define RULE_CATEGORY(category_name) \
#catname , #category_name ,
#include "ruletypes.h" #include "ruletypes.h"
"InvalidCategory" "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*/ /* this is done in three steps so we can reliably get to them by index*/
#define RULE_INT(cat, rule, default_value, notes) \ #define RULE_INT(category_name, rule_name, default_value, notes) \
{ #cat ":" #rule, Category__##cat, IntRule, Int__##rule, notes }, { #category_name ":" #rule_name, Category__##category_name, IntRule, Int__##rule_name, notes },
#include "ruletypes.h" #include "ruletypes.h"
#define RULE_REAL(cat, rule, default_value, notes) \ #define RULE_REAL(category_name, rule_name, default_value, notes) \
{ #cat ":" #rule, Category__##cat, RealRule, Real__##rule, notes }, { #category_name ":" #rule_name, Category__##category_name, RealRule, Real__##rule_name, notes },
#include "ruletypes.h" #include "ruletypes.h"
#define RULE_BOOL(cat, rule, default_value, notes) \ #define RULE_BOOL(category_name, rule_name, default_value, notes) \
{ #cat ":" #rule, Category__##cat, BoolRule, Bool__##rule, notes }, { #category_name ":" #rule_name, Category__##category_name, BoolRule, Bool__##rule_name, notes },
#include "ruletypes.h" #include "ruletypes.h"
{ "Invalid Rule", _CatCount, IntRule } { "Invalid Rule", _CatCount, IntRule }
}; };
RuleManager::RuleManager() RuleManager::RuleManager()
: m_activeRuleset(0), : m_activeRuleset(0),
m_activeName("default") m_activeName("default")
{ {
ResetRules(false); ResetRules(false);
} }
RuleManager::CategoryType RuleManager::FindCategory(const char *catname) { RuleManager::CategoryType RuleManager::FindCategory(const std::string &category_name)
int i; {
for (i = 0; i < _CatCount; i++) { for (int i = 0; i < _CatCount; i++) {
if (strcasecmp(catname, s_categoryNames[i]) == 0) if (Strings::Contains(category_name, s_categoryNames[i])) {
return((CategoryType)i); return static_cast<CategoryType>(i);
}
return(InvalidCategory);
}
bool RuleManager::ListRules(const char *catname, std::vector<const char *> &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);
} }
} }
int i;
int rule_count = CountRules(); return InvalidCategory;
for (i = 0; i < rule_count; i++) { }
const RuleInfo &rule = s_RuleInfo[i];
if (catname == nullptr || cat == rule.category) { bool RuleManager::ListRules(const std::string &category_name, std::vector<std::string> &l)
into.push_back(rule.name); {
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<const char *> &into) { for (int i = 0; i < CountRules(); i++) {
int i; const auto& r = s_RuleInfo[i];
for (i = 0; i < _CatCount; i++) { if (category_name.empty() || category_type == r.category) {
into.push_back(s_categoryNames[i]); 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; return true;
} }
bool RuleManager::SetRule(const char *rule_name, const char *rule_value, Database *database, bool db_save, bool reload) { bool RuleManager::ListCategories(std::vector <std::string> &l)
if(rule_name == nullptr || rule_value == nullptr) {
return(false); 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; RuleType type;
uint16 index; uint16 index;
if(!_FindRule(rule_name, type, index)) if (!_FindRule(rule_name, type, index)) {
return(false); return (false);
if (reload) {
if (strcasecmp(rule_name, "World:ExpansionSettings") == 0)
return(false);
if (strcasecmp(rule_name, "World:UseClientBasedExpansionSettings") == 0)
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: 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]); LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleIntValues[index]);
break; break;
case RealRule: case RealRule:
m_RuleRealValues[index] = atof(rule_value); m_RuleRealValues[index] = atof(rule_value.c_str());
LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleRealValues[index]); LogRules("Set rule [{}] to value [{:.2f}]", rule_name, m_RuleRealValues[index]);
break; break;
case BoolRule: case BoolRule:
uint32 val = 0; m_RuleBoolValues[index] = static_cast<uint32>(Strings::ToBool(rule_value));
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;
LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleBoolValues[index] == 1 ? "true" : "false"); LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleBoolValues[index] == 1 ? "true" : "false");
break; break;
} }
if(db_save) if (db_save) {
_SaveRule(database, type, index); _SaveRule(db, type, index);
}
return(true); return true;
} }
void RuleManager::ResetRules(bool reload) { void RuleManager::ResetRules(bool reload) {
std::string expansion1; std::string client_rule;
std::string expansion2; std::string world_rule;
// these rules must not change during server runtime // these rules must not change during server runtime
if (reload) { if (reload) {
GetRule("World:ExpansionSettings", expansion1); GetRule("World:UseClientBasedExpansionSettings", client_rule);
GetRule("World:UseClientBasedExpansionSettings", expansion2); GetRule("World:ExpansionSettings", world_rule);
} }
Log(Logs::Detail, Logs::Rules, "Resetting running rules to default values"); LogRulesDetail("Resetting running rules to default values.");
#define RULE_INT(cat, rule, default_value, notes) \
m_RuleIntValues[ Int__##rule ] = default_value; #define RULE_INT(category_name, rule_name, default_value, notes) \
#define RULE_REAL(cat, rule, default_value, notes) \ m_RuleIntValues[ Int__##rule_name ] = default_value;
m_RuleRealValues[ Real__##rule ] = default_value; #define RULE_REAL(category_name, rule_name, default_value, notes) \
#define RULE_BOOL(cat, rule, default_value, notes) \ m_RuleRealValues[ Real__##rule_name ] = default_value;
m_RuleBoolValues[ Bool__##rule ] = default_value; #define RULE_BOOL(category_name, rule_name, default_value, notes) \
m_RuleBoolValues[ Bool__##rule_name ] = default_value;
#include "ruletypes.h" #include "ruletypes.h"
// restore these rules to their pre-reset values // restore these rules to their pre-reset values
if (reload) { if (reload) {
SetRule("World:ExpansionSettings", expansion1.c_str(), nullptr, false, false); SetRule("World:UseClientBasedExpansionSettings", client_rule);
SetRule("World:UseClientBasedExpansionSettings", expansion2.c_str(), nullptr, false, false); SetRule("World:ExpansionSettings", world_rule);
} }
} }
bool RuleManager::_FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into) { bool RuleManager::_FindRule(const std::string &rule_name, RuleType &type_into, uint16 &index_into) {
if (rule_name == nullptr) if (rule_name.empty()) {
return(false); return false;
}
int i; for (int i = 0; i < CountRules(); i++) {
int rule_count = CountRules(); const auto& r = s_RuleInfo[i];
for (i = 0; i < rule_count; i++) { if (rule_name == r.name) {
const RuleInfo &rule = s_RuleInfo[i]; type_into = r.type;
if (strcmp(rule_name, rule.name) == 0) { index_into = r.rule_index;
type_into = rule.type; return true;
index_into = rule.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! //assumes index is valid!
const char *RuleManager::_GetRuleName(RuleType type, uint16 index) { std::string RuleManager::_GetRuleName(RuleType type, uint16 index) {
switch (type) { switch (type) {
case IntRule: case IntRule:
return(s_RuleInfo[index].name); return s_RuleInfo[index].name;
case RealRule: case RealRule:
return(s_RuleInfo[index+_IntRuleCount].name); return s_RuleInfo[index + _IntRuleCount].name;
case BoolRule: case BoolRule:
return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount].name); return s_RuleInfo[index + _IntRuleCount + _RealRuleCount].name;
default: default:
break; 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! //assumes index is valid!
const std::string &RuleManager::_GetRuleNotes(RuleType type, uint16 index) { const std::string &RuleManager::_GetRuleNotes(RuleType type, uint16 index) {
switch (type) { switch (type) {
case IntRule: case IntRule:
return(s_RuleInfo[index].notes); return s_RuleInfo[index].notes;
case RealRule: case RealRule:
return(s_RuleInfo[index+_IntRuleCount].notes); return s_RuleInfo[index + _IntRuleCount].notes;
case BoolRule: case BoolRule:
return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount].notes); return s_RuleInfo[index + _IntRuleCount + _RealRuleCount].notes;
default: default:
break; 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) { bool RuleManager::LoadRules(Database *db, const std::string &rule_set_name, bool reload) {
const auto rule_set_id = RuleSetsRepository::GetRuleSetID(*db, rule_set_name);
int ruleset_id = GetRulesetID(database, ruleset_name); if (rule_set_id < 0) {
if (ruleset_id < 0) { LogRulesDetail("Failed to find Rule Set {} for load operation. Canceling.", rule_set_name);
Log(Logs::Detail, Logs::Rules, "Failed to find ruleset '%s' for load operation. Canceling.", ruleset_name); return false;
return (false);
} }
m_activeRuleset = ruleset_id; m_activeRuleset = rule_set_id;
m_activeName = ruleset_name; m_activeName = rule_set_name;
/* Load default ruleset values first if we're loading something other than default */ /* 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"; const std::string default_ruleset_name = "default";
int default_ruleset_id = GetRulesetID(database, default_ruleset_name.c_str()); bool is_default = rule_set_name == default_ruleset_name;
if (default_ruleset_id < 0) { if (!is_default) {
Log(Logs::Detail, const auto default_rule_set_id = RuleSetsRepository::GetRuleSetID(*db, default_ruleset_name);
Logs::Rules,
"Failed to find default ruleset '%s' for load operation. Canceling.", if (default_rule_set_id < 0) {
default_ruleset_name.c_str() 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; return false;
} }
for (auto row = results.begin(); row != results.end(); ++row) { LogRulesDetail("Loading Rule Set {} ({}).", default_ruleset_name, default_rule_set_id);
if (!SetRule(row[0], row[1], nullptr, false, reload)) {
Log(Logs::Detail, Logs::Rules, "Unable to interpret rule record for '%s'", row[0]); 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); const auto& l = RuleValuesRepository::GetWhere(
*db,
auto results = database->QueryDatabase(query); fmt::format(
if (!results.Success()) { "ruleset_id = {}",
rule_set_id
)
);
if (l.empty()) {
return false; return false;
} }
for (auto row = results.begin(); row != results.end(); ++row) { for (const auto& e : l) {
if (!SetRule(row[0], row[1], nullptr, false, reload)) { if (!SetRule(e.rule_name, e.rule_value, nullptr, false, reload)) {
Log(Logs::Detail, Logs::Rules, "Unable to interpret rule record for '%s'", row[0]); LogRulesDetail("Unable to interpret Rule record for Rule '{}'.", e.rule_name);
} }
} }
return true; return true;
} }
void RuleManager::SaveRules(Database *database, const char *ruleset_name) { void RuleManager::SaveRules(Database *db, const std::string &rule_set_name) {
if (!rule_set_name.empty()) {
if (ruleset_name != nullptr) { if (m_activeName != rule_set_name) {
//saving to a specific name m_activeRuleset = _FindOrCreateRuleset(db, rule_set_name);
if (m_activeName != ruleset_name) {
//a new name...
m_activeRuleset = _FindOrCreateRuleset(database, ruleset_name);
if (m_activeRuleset == -1) { 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; 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);
} LogRulesDetail("Saving running rules into Rule Set {} ({}).", rule_set_name, m_activeRuleset);
else { } 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 running Rule Set {} ({}).", m_activeName, m_activeRuleset);
} }
int i; int i;
for (i = 0; i < _IntRuleCount; i++) { for (i = 0; i < _IntRuleCount; i++) {
_SaveRule(database, IntRule, i); _SaveRule(db, IntRule, i);
} }
for (i = 0; i < _RealRuleCount; i++) { for (i = 0; i < _RealRuleCount; i++) {
_SaveRule(database, RealRule, i); _SaveRule(db, RealRule, i);
} }
for (i = 0; i < _BoolRuleCount; i++) { for (i = 0; i < _BoolRuleCount; i++) {
_SaveRule(database, BoolRule, i); _SaveRule(db, BoolRule, i);
} }
} }
void RuleManager::_SaveRule(Database *database, RuleType type, uint16 index) { void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
char value_string[100]; const auto rule_name = _GetRuleName(type, index);
if (type == IntRule && strcasecmp(_GetRuleName(type, index), "World:ExpansionSettings") == 0) if (
return; (type == BoolRule && Strings::EqualFold(rule_name, "World:UseClientBasedExpansionSettings")) ||
if (type == BoolRule && strcasecmp(_GetRuleName(type, index), "World:UseClientBasedExpansionSettings") == 0) (type == IntRule && Strings::EqualFold(rule_name, "World:ExpansionSettings"))
) {
return; return;
}
std::string rule_value;
switch (type) { switch (type) {
case IntRule: case IntRule:
sprintf(value_string, "%d", m_RuleIntValues[index]); rule_value = fmt::format("{}", m_RuleIntValues[index]);
break; break;
case RealRule: case RealRule:
sprintf(value_string, "%.13f", m_RuleRealValues[index]); rule_value = fmt::format("{:.13f}", m_RuleRealValues[index]);
break; break;
case BoolRule: case BoolRule:
sprintf(value_string, "%s", m_RuleBoolValues[index] ? "true" : "false"); rule_value = m_RuleBoolValues[index] ? "true" : "false";
break; break;
} }
std::string query = StringFormat( const auto rule_notes = _GetRuleNotes(type, index);
"REPLACE INTO `rule_values`"
"(`ruleset_id`, `rule_name`, `rule_value`, `notes`)" const auto& l = RuleValuesRepository::GetWhere(
" VALUES('%d', '%s', '%s', '%s')", *db,
fmt::format(
"ruleset_id = {} AND rule_name = '{}' LIMIT 1",
m_activeRuleset, m_activeRuleset,
_GetRuleName(type, index), rule_name
value_string, )
Strings::Escape(_GetRuleNotes(type, index)).c_str()
); );
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<std::string> database_data; std::map<std::string, std::pair<std::string, const std::string *>> rule_data;
std::map<std::string, std::pair<std::string, const std::string *>> rule_data;
std::vector<std::tuple<int, std::string, std::string, std::string>> injected_rule_entries; std::vector<std::tuple<int, std::string, std::string, std::string>> injected_rule_entries;
if (ruleset_name == nullptr) { if (rule_set_name.empty()) {
return false; return false;
} }
int ruleset_id = GetRulesetID(db, ruleset_name); const auto rule_set_id = RuleSetsRepository::GetRuleSetID(*db, rule_set_name);
if (ruleset_id < 0) { if (rule_set_id < 0) {
return false; return false;
} }
// load database rule names const auto& v = RuleValuesRepository::GetRuleNames(*db, rule_set_id);
std::string query(StringFormat("SELECT `rule_name` FROM `rule_values` WHERE `ruleset_id` = '%i'", ruleset_id)); if (v.empty()) {
auto results = db->QueryDatabase(query);
if (!results.Success()) {
return false; return false;
} }
// build database data entries
for (auto row : results) {
database_data.push_back(std::string(row[0]));
}
// build rule data entries // build rule data entries
for (const auto &ri_iter : s_RuleInfo) { for (const auto& r : s_RuleInfo) {
if (strcasecmp(ri_iter.name, "Invalid Rule") == 0) { if (Strings::EqualFold(r.name, "Invalid Rule")) {
continue; continue;
} }
char buffer[100]; switch (r.type) {
case IntRule:
switch (ri_iter.type) { rule_data[r.name].first = fmt::format("{}", m_RuleIntValues[r.rule_index]);
case IntRule: rule_data[r.name].second = &r.notes;
sprintf(buffer, "%d", m_RuleIntValues[ri_iter.rule_index]); break;
rule_data[ri_iter.name].first = buffer; case RealRule:
rule_data[ri_iter.name].second = &ri_iter.notes; rule_data[r.name].first = fmt::format("{:.13f}", m_RuleRealValues[r.rule_index]);
break; rule_data[r.name].second = &r.notes;
case RealRule: break;
sprintf(buffer, "%.13f", m_RuleRealValues[ri_iter.rule_index]); case BoolRule:
rule_data[ri_iter.name].first = buffer; rule_data[r.name].first = fmt::format("{}", m_RuleBoolValues[r.rule_index] ? "true" : "false");
rule_data[ri_iter.name].second = &ri_iter.notes; rule_data[r.name].second = &r.notes;
break; break;
case BoolRule: default:
sprintf(buffer, "%s", (m_RuleBoolValues[ri_iter.rule_index] ? "true" : "false")); break;
rule_data[ri_iter.name].first = buffer;
rule_data[ri_iter.name].second = &ri_iter.notes;
break;
default:
break;
} }
} }
// build injected entries // build injected entries
for (const auto &rd_iter : rule_data) { for (const auto &d : rule_data) {
if (std::find(v.begin(), v.end(), d.first) == v.end()) {
const auto &dd_iter = std::find(database_data.begin(), database_data.end(), rd_iter.first);
if (dd_iter == database_data.end()) {
injected_rule_entries.push_back( injected_rule_entries.push_back(
std::tuple<int, std::string, std::string, std::string>( std::tuple<int, std::string, std::string, std::string>(
ruleset_id, // `ruleset_id` rule_set_id,
rd_iter.first, // `rule_name` d.first,
rd_iter.second.first, // `rule_value` d.second.first,
Strings::Escape(*rd_iter.second.second) // `notes` Strings::Escape(*d.second.second)
) )
); );
if (!quiet_update) { if (!quiet_update) {
LogInfo( LogInfo(
"Adding new rule [{}] ruleset [{}] ({}) value [{}]", "Adding new rule [{}] ruleset [{}] ({}) value [{}]",
rd_iter.first.c_str(), d.first,
ruleset_name, rule_set_name,
ruleset_id, rule_set_id,
rd_iter.second.first.c_str() d.second.first
); );
} }
} }
} }
if (injected_rule_entries.size()) { if (injected_rule_entries.size()) {
if (!RuleValuesRepository::InjectRules(*db, injected_rule_entries)) {
std::string query(
fmt::format(
"REPLACE INTO `rule_values`(`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES {}",
Strings::ImplodePair(
",",
std::pair<char, char>('(', ')'),
join_tuple(",", std::pair<char, char>('\'', '\''), injected_rule_entries)
)
)
);
if (!db->QueryDatabase(query).Success()) {
return false; return false;
} }
LogInfo( LogInfo(
"[{}] New rule(s) added to ruleset [{}] [{}]", "[{}] New rule(s) added to ruleset [{}] [{}]",
injected_rule_entries.size(), injected_rule_entries.size(),
ruleset_name, rule_set_name,
ruleset_id rule_set_id
); );
} }
@ -491,50 +480,36 @@ bool RuleManager::UpdateOrphanedRules(Database *db, bool quiet_update)
std::vector<std::string> rule_data; std::vector<std::string> rule_data;
std::vector<std::string> orphaned_rule_entries; std::vector<std::string> orphaned_rule_entries;
// load database rule names const auto& l = RuleValuesRepository::GetGroupedRules(*db);
std::string query("SELECT `rule_name` FROM `rule_values` GROUP BY `rule_name`"); if (l.empty()) {
auto results = db->QueryDatabase(query);
if (!results.Success()) {
return false; return false;
} }
// build rule data entries // build rule data entries
for (const auto &ri_iter : s_RuleInfo) { for (const auto &r : s_RuleInfo) {
if (strcasecmp(ri_iter.name, "Invalid Rule") == 0) { if (Strings::EqualFold(r.name, "Invalid Rule")) {
continue; continue;
} }
rule_data.push_back(ri_iter.name); rule_data.push_back(r.name);
} }
// build orphaned entries for (const auto& e : l) {
for (auto row : results) { const auto &d = std::find(rule_data.begin(), rule_data.end(), e);
if (d == rule_data.end()) {
const auto &rd_iter = std::find(rule_data.begin(), rule_data.end(), row[0]); orphaned_rule_entries.push_back(e);
if (rd_iter == rule_data.end()) {
orphaned_rule_entries.push_back(std::string(row[0]));
if (!quiet_update) { if (!quiet_update) {
LogInfo( LogInfo(
"Rule [{}] no longer exists... Deleting orphaned entry from `rule_values` table...", "Rule [{}] no longer exists... Deleting orphaned entry from `rule_values` table...",
row[0] e
); );
} }
} }
} }
if (orphaned_rule_entries.size()) { if (orphaned_rule_entries.size()) {
if (!RuleValuesRepository::DeleteOrphanedRules(*db, orphaned_rule_entries)) {
std::string query (
fmt::format(
"DELETE FROM `rule_values` WHERE `rule_name` IN ({})",
Strings::ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned_rule_entries)
)
);
if (!db->QueryDatabase(query).Success()) {
return false; return false;
} }
@ -546,139 +521,83 @@ bool RuleManager::UpdateOrphanedRules(Database *db, bool quiet_update)
bool RuleManager::RestoreRuleNotes(Database *db) bool RuleManager::RestoreRuleNotes(Database *db)
{ {
std::string query("SELECT `ruleset_id`, `rule_name`, `notes` FROM `rule_values`"); const auto& l = RuleValuesRepository::All(*db);
if (l.empty()) {
auto results = db->QueryDatabase(query);
if (!results.Success()) {
return false; return false;
} }
int update_count = 0; int update_count = 0;
for (auto row = results.begin(); row != results.end(); ++row) { for (const auto& e : l) {
auto rule = [](const char *rule_name) {
auto rule = [](std::string rule_name) {
for (auto rule_iter : s_RuleInfo) { 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 rule_iter;
} }
} }
return s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount]; return s_RuleInfo[_IntRuleCount + _RealRuleCount + _BoolRuleCount];
}(row[1]); }(e.rule_name);
if (strcasecmp(rule.name, row[1]) != 0) { if (Strings::Contains(rule.name, e.rule_name)) {
continue; continue;
} }
if (row[2] != nullptr && rule.notes.compare(row[2]) == 0) { if (!e.notes.empty() && !rule.notes.compare(e.notes)) {
continue; continue;
} }
std::string query( if (!RuleValuesRepository::UpdateRuleNote(*db, e.ruleset_id, e.rule_name, rule.notes)) {
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()) {
continue; continue;
} }
++update_count; ++update_count;
} }
if (update_count > 0) { if (update_count) {
LogInfo("[{}] Rule Note [{}] Restored", update_count, (update_count == 1 ? "" : "s")); LogInfo("[{}] Rule Note{} Restored", update_count, update_count != 1 ? "s" : "");
} }
return true; 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<uint32>(strlen(ruleset_name)); return RuleSetsRepository::CreateNewRuleSet(*db, rule_set_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]);
} }
int RuleManager::_FindOrCreateRuleset(Database *database, const char *in_ruleset_name) { bool RuleManager::ListRulesets(Database *db, std::map<int, std::string> &m)
{
m[0] = "default";
int ruleset_id = GetRulesetID(database, in_ruleset_name); const auto& l = RuleSetsRepository::All(*db);
if (ruleset_id >= 0) if (l.empty()) {
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<int, std::string> &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())
return false; return false;
}
for (auto row = results.begin(); row != results.end(); ++row) for (const auto& e : l) {
into[atoi(row[0])] = row[1]; m[e.ruleset_id] = e.name;
}
return true; 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 float RuleManager::GetRealRule(RuleManager::RealType t) const
{ {
return (m_RuleRealValues[t]); return m_RuleRealValues[t];
} }
bool RuleManager::GetBoolRule(RuleManager::BoolType t) const bool RuleManager::GetBoolRule(RuleManager::BoolType t) const
{ {
return (m_RuleBoolValues[t] == 1); return m_RuleBoolValues[t] == 1;
} }

View File

@ -29,12 +29,12 @@
//note, these macros assume there is always a RuleManager *rules in scope, //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 //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 //what we do with the zone global and just make it a member of core classes
#define RuleI(cat, rule) \ #define RuleI(category_name, rule_name) \
RuleManager::Instance()->GetIntRule( RuleManager::Int__##rule ) RuleManager::Instance()->GetIntRule( RuleManager::Int__##rule_name )
#define RuleR(cat, rule) \ #define RuleR(category_name, rule_name) \
RuleManager::Instance()->GetRealRule( RuleManager::Real__##rule ) RuleManager::Instance()->GetRealRule( RuleManager::Real__##rule_name )
#define RuleB(cat, rule) \ #define RuleB(category_name, rule_name) \
RuleManager::Instance()->GetBoolRule( RuleManager::Bool__##rule ) RuleManager::Instance()->GetBoolRule( RuleManager::Bool__##rule_name )
#include <vector> #include <vector>
@ -49,91 +49,101 @@ class RuleManager {
public: public:
//generate our rule enums: //generate our rule enums:
typedef enum { typedef enum {
#define RULE_INT(cat, rule, default_value, notes) \ #define RULE_INT(category_name, rule_name, default_value, notes) \
Int__##rule, Int__##rule_name,
#include "ruletypes.h"
#include "ruletypes.h"
_IntRuleCount _IntRuleCount
} IntType; } IntType;
typedef enum { typedef enum {
#define RULE_REAL(cat, rule, default_value, notes) \ #define RULE_REAL(category_name, rule_name, default_value, notes) \
Real__##rule, Real__##rule_name,
#include "ruletypes.h"
#include "ruletypes.h"
_RealRuleCount _RealRuleCount
} RealType; } RealType;
typedef enum { typedef enum {
#define RULE_BOOL(cat, rule, default_value, notes) \ #define RULE_BOOL(category_name, rule_name, default_value, notes) \
Bool__##rule, Bool__##rule_name,
#include "ruletypes.h"
#include "ruletypes.h"
_BoolRuleCount _BoolRuleCount
} BoolType; } BoolType;
typedef enum { typedef enum {
#define RULE_CATEGORY(catname) \ #define RULE_CATEGORY(category_name) \
Category__##catname, Category__##category_name,
#include "ruletypes.h"
#include "ruletypes.h"
_CatCount _CatCount
} CategoryType; } CategoryType;
static RuleManager* Instance() { static RuleManager *Instance()
{
static RuleManager rules; static RuleManager rules;
return &rules; return &rules;
} }
static const IntType InvalidInt = _IntRuleCount; static const IntType InvalidInt = _IntRuleCount;
static const RealType InvalidReal = _RealRuleCount; static const RealType InvalidReal = _RealRuleCount;
static const BoolType InvalidBool = _BoolRuleCount; static const BoolType InvalidBool = _BoolRuleCount;
static const CategoryType InvalidCategory = _CatCount; 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 //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; float GetRealRule(RealType t) const;
bool GetBoolRule(BoolType t) const; bool GetBoolRule(BoolType t) const;
//management routines //management routines
static const char *GetRuleName(IntType t) { return(s_RuleInfo[t].name); } static std::string GetRuleName(IntType t) { return s_RuleInfo[t].name; }
static const char *GetRuleName(RealType t) { return(s_RuleInfo[t+_IntRuleCount].name); } static std::string GetRuleName(RealType t) { return s_RuleInfo[t + _IntRuleCount].name; }
static const char *GetRuleName(BoolType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount].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(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(RealType t) { return s_RuleInfo[t + _IntRuleCount].notes; }
static const std::string &GetRuleNotes(BoolType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount].notes); } static const std::string &GetRuleNotes(BoolType t) { return s_RuleInfo[t + _IntRuleCount + _RealRuleCount].notes; }
static uint32 CountRules() { return(_RulesCount); } static uint32 CountRules() { return _RulesCount; }
static CategoryType FindCategory(const char *catname); static CategoryType FindCategory(const std::string &category_name);
bool ListRules(const char *catname, std::vector<const char *> &into); bool ListRules(const std::string &category_name, std::vector <std::string> &l);
bool ListCategories(std::vector<const char *> &into); bool ListCategories(std::vector <std::string> &l);
bool GetRule(const char *rule_name, std::string &ret_val); bool GetRule(const std::string &rule_name, std::string &rule_value);
bool SetRule(const char *rule_name, const char *rule_value, Database *db = nullptr, bool db_save = false, bool reload = false); 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); } int GetActiveRulesetID() const { return m_activeRuleset; }
const char *GetActiveRuleset() const { return(m_activeName.c_str()); } std::string GetActiveRuleset() const { return m_activeName; }
static int GetRulesetID(Database *db, const char *rulesetname); static bool ListRulesets(Database *db, std::map<int, std::string> &l);
static std::string GetRulesetName(Database *db, int id);
static bool ListRulesets(Database *db, std::map<int, std::string> &into);
void ResetRules(bool reload = false); void ResetRules(bool reload = false);
bool LoadRules(Database *db, const char *ruleset = nullptr, bool reload = false); bool LoadRules(Database *db, const std::string &rule_set_name, bool reload = false);
void SaveRules(Database *db, const char *ruleset = nullptr); void SaveRules(Database *db, const std::string &rule_set_name);
bool UpdateInjectedRules(Database *db, const char *ruleset_name, bool quiet_update = false); bool UpdateInjectedRules(Database *db, const std::string &rule_set_name, bool quiet_update = false);
bool UpdateOrphanedRules(Database *db, bool quiet_update = false); bool UpdateOrphanedRules(Database *db, bool quiet_update = false);
bool RestoreRuleNotes(Database *db); bool RestoreRuleNotes(Database *db);
private: private:
RuleManager(); RuleManager();
RuleManager(const RuleManager&); RuleManager(const RuleManager &);
const RuleManager& operator=(const RuleManager&); const RuleManager &operator=(const RuleManager &);
int m_activeRuleset; int m_activeRuleset;
std::string m_activeName; std::string m_activeName;
#ifdef WIN64
uint32 m_RuleIntValues [_IntRuleCount ]; int m_RuleIntValues[_IntRuleCount];
#else float m_RuleRealValues[_RealRuleCount];
int m_RuleIntValues [_IntRuleCount ]; uint32 m_RuleBoolValues[_BoolRuleCount];
#endif
float m_RuleRealValues[_RealRuleCount];
uint32 m_RuleBoolValues[_BoolRuleCount];
typedef enum { typedef enum {
IntRule, IntRule,
@ -141,23 +151,21 @@ private:
BoolRule BoolRule
} RuleType; } RuleType;
static bool _FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into); static bool _FindRule(const std::string &rule_name, RuleType &type_into, uint16 &index_into);
static const char *_GetRuleName(RuleType type, uint16 index); static std::string _GetRuleName(RuleType type, uint16 index);
static const std::string &_GetRuleNotes(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); void _SaveRule(Database *db, RuleType type, uint16 index);
static const char *s_categoryNames[]; static const char* s_categoryNames[];
typedef struct { typedef struct {
const char *name; std::string name;
CategoryType category; CategoryType category;
RuleType type; RuleType type;
uint16 rule_index; //index into its 'type' array uint16 rule_index;
const std::string notes; const std::string notes;
} RuleInfo; } RuleInfo;
static const RuleInfo s_RuleInfo[]; static const RuleInfo s_RuleInfo[];
}; };
#endif /*RULESYS_H_*/ #endif /*RULESYS_H_*/

View File

@ -721,3 +721,20 @@ uint32 Strings::TimeToSeconds(std::string time_string)
return duration; 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;
}

View File

@ -110,6 +110,8 @@ public:
static std::vector<std::string> Wrap(std::vector<std::string> &src, std::string character); static std::vector<std::string> Wrap(std::vector<std::string> &src, std::string character);
static void FindReplace(std::string &string_subject, const std::string &search_string, const std::string &replace_string); 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 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<typename T> template<typename T>
static std::string static std::string

View File

@ -11837,7 +11837,7 @@ bool Client::SendGMCommand(std::string message, bool ignore_status) {
void Client::RegisterBug(BugReport_Struct* r) { void Client::RegisterBug(BugReport_Struct* r) {
if (!r) { if (!r) {
return; return;
}; }
auto b = BugReportsRepository::NewEntity(); auto b = BugReportsRepository::NewEntity();

View File

@ -32,6 +32,7 @@ void ListModifyNPCStatMap(Client *c);
std::map<std::string, std::string> GetModifyNPCStatMap(); std::map<std::string, std::string> GetModifyNPCStatMap();
std::string GetModifyNPCStatDescription(std::string stat); std::string GetModifyNPCStatDescription(std::string stat);
void SendNPCEditSubCommands(Client *c); void SendNPCEditSubCommands(Client *c);
void SendRuleSubCommands(Client *c);
// Commands // Commands
void command_acceptrules(Client *c, const Seperator *sep); void command_acceptrules(Client *c, const Seperator *sep);

View File

@ -1,232 +1,451 @@
#include "../client.h" #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) void command_rules(Client *c, const Seperator *sep)
{ {
//super-command for managing rules settings auto arguments = sep->argnum;
if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { bool is_help = !strcasecmp(sep->arg[1], "help");
c->Message(Chat::White, "Syntax: #rules [subcommand]."); if (!arguments || is_help) {
c->Message(Chat::White, "-- Rule Set Manipulation --"); SendRuleSubCommands(c);
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");
return; return;
} }
if (!strcasecmp(sep->arg[1], "current")) { bool is_current = !strcasecmp(sep->arg[1], "current");
c->Message( bool is_get = !strcasecmp(sep->arg[1], "get");
Chat::White, "Currently running ruleset '%s' (%d)", RuleManager::Instance()->GetActiveRuleset(), bool is_list = !strcasecmp(sep->arg[1], "list");
RuleManager::Instance()->GetActiveRulesetID()); 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<int, std::string> sets; if (is_current) {
if (!RuleManager::Instance()->ListRulesets(&database, sets)) { c->Message(
c->Message(Chat::Red, "Failed to list rule sets!"); Chat::White,
fmt::format(
"Currently running Rule Set {} ({}).",
RuleManager::Instance()->GetActiveRuleset(),
RuleManager::Instance()->GetActiveRulesetID()
).c_str()
);
} else if (is_list_sets) {
std::map<int, std::string> m;
if (!RuleManager::Instance()->ListRulesets(&database, m)) {
c->Message(Chat::White, "Failed to list Rule Sets!");
return; return;
} }
c->Message(Chat::White, "Avaliable rule sets:"); if (m.empty()) {
std::map<int, std::string>::iterator cur, end; c->Message(Chat::White, "There are no available Rule Sets!");
cur = sets.begin(); return;
end = sets.end();
for (; cur != end; ++cur) {
c->Message(Chat::White, "(%d) %s", cur->first, cur->second.c_str());
} }
}
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); RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true);
c->Message( c->Message(
Chat::White, "The active ruleset (%s (%d)) has been reloaded", RuleManager::Instance()->GetActiveRuleset(), Chat::White,
RuleManager::Instance()->GetActiveRulesetID()); fmt::format(
} "Active Rule Set {} ({}) has been reloaded.",
else if (!strcasecmp(sep->arg[1], "switch")) { RuleManager::Instance()->GetActiveRuleset(),
RuleManager::Instance()->GetActiveRulesetID()
).c_str()
);
} else if (is_switch) {
//make sure this is a valid rule set.. //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) { if (rsid < 0) {
c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); c->Message(
return; Chat::White,
} fmt::format(
if (!database.SetVariable("RuleSet", sep->arg[2])) { "Rule Set '{}' does not exist or is invalid.",
c->Message(Chat::Red, "Failed to update variables table to change selected rule set"); 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; return;
} }
//TODO: we likely want to reload this ruleset everywhere...
RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); RuleManager::Instance()->LoadRules(&database, sep->arg[2], true);
c->Message( c->Message(
Chat::White, 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], sep->arg[2],
rsid rsid
); );
} } else if (is_load) {
else if (!strcasecmp(sep->arg[1], "load")) { const auto rsid = RuleSetsRepository::GetRuleSetID(database, sep->arg[2]);
//make sure this is a valid rule set..
int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]);
if (rsid < 0) { 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; return;
} }
RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); RuleManager::Instance()->LoadRules(&database, sep->arg[2], true);
c->Message(Chat::White, "Loaded ruleset '%s' (%d) locally", sep->arg[2], rsid); c->Message(
} Chat::White,
else if (!strcasecmp(sep->arg[1], "store")) { fmt::format(
if (sep->argnum == 1) { "Loaded Rule Set {} ({}) locally.",
//store current rule set. sep->arg[2],
RuleManager::Instance()->SaveRules(&database); rsid
c->Message(Chat::White, "Rules saved"); ).c_str()
} );
else if (sep->argnum == 2) { } 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]); RuleManager::Instance()->SaveRules(&database, sep->arg[2]);
int prersid = RuleManager::Instance()->GetActiveRulesetID(); const auto prersid = RuleManager::Instance()->GetActiveRulesetID();
int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); const auto rsid = RuleSetsRepository::GetRuleSetID(database, sep->arg[2]);
if (rsid < 0) { if (rsid < 0) {
c->Message(Chat::Red, "Unable to query ruleset ID after store, it most likely failed."); c->Message(Chat::White, "Unable to query Rule Set ID after store.");
} } else {
else { c->Message(
c->Message(Chat::White, "Stored rules as ruleset '%s' (%d)", sep->arg[2], rsid); Chat::White,
fmt::format(
"Stored rules as Rule Set {} ({}).",
sep->arg[2],
rsid
).c_str()
);
if (prersid != rsid) { 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 {
else { SendRuleSubCommands(c);
c->Message(Chat::Red, "Invalid argument count, see help.");
return; return;
} }
} } else if (is_reset) {
else if (!strcasecmp(sep->arg[1], "reset")) {
RuleManager::Instance()->ResetRules(true); RuleManager::Instance()->ResetRules(true);
c->Message(Chat::White, "The running ruleset has been set to defaults"); c->Message(
Chat::White,
} fmt::format(
else if (!strcasecmp(sep->arg[1], "get")) { "Rule Set {} ({}) has been set to defaults.",
if (sep->argnum != 2) { RuleManager::Instance()->GetActiveRuleset(),
c->Message(Chat::Red, "Invalid argument count, see help."); 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; return;
} }
std::string value; } else if (is_set) {
if (!RuleManager::Instance()->GetRule(sep->arg[2], value)) { if (arguments == 3) {
c->Message(Chat::Red, "Unable to find rule %s", sep->arg[2]); if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], nullptr, false, true)) {
} c->Message(
else { Chat::White,
c->Message(Chat::White, "%s - %s", sep->arg[2], value.c_str()); fmt::format(
} "Failed to modify Rule {} to a value of {}.",
sep->arg[2],
} sep->arg[3]
else if (!strcasecmp(sep->arg[1], "set")) { ).c_str()
if (sep->argnum != 3) { );
c->Message(Chat::Red, "Invalid argument count, see help."); } 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; return;
} }
if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], nullptr, false, true)) { } else if (is_set_db) {
c->Message(Chat::Red, "Failed to modify rule"); if (arguments == 3) {
} if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], &database, true, true)) {
else { c->Message(
c->Message(Chat::White, "Rule modified locally."); Chat::White,
} fmt::format(
} "Failed to modify Rule {} to a value of {}.",
else if (!strcasecmp(sep->arg[1], "setdb")) { sep->arg[2],
if (sep->argnum != 3) { sep->arg[3]
c->Message(Chat::Red, "Invalid argument count, see help."); ).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; return;
} }
if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], &database, true, true)) { } else if (is_list) {
c->Message(Chat::Red, "Failed to modify rule"); if (arguments == 1) {
} std::vector<std::string> l;
else { if (!RuleManager::Instance()->ListCategories(l)) {
c->Message(Chat::White, "Rule modified locally and in the database."); c->Message(Chat::White, "Failed to list Rule Categories!");
}
}
else if (!strcasecmp(sep->arg[1], "list")) {
if (sep->argnum == 1) {
std::vector<const char *> rule_list;
if (!RuleManager::Instance()->ListCategories(rule_list)) {
c->Message(Chat::Red, "Failed to list categories!");
return; return;
} }
if (l.empty()) {
c->Message(Chat::White, "There are no Rule Categories to list!");
return;
}
c->Message(Chat::White, "Rule Categories:"); c->Message(Chat::White, "Rule Categories:");
std::vector<const char *>::iterator cur, end;
cur = rule_list.begin(); auto rule_category_count = 0;
end = rule_list.end(); auto rule_category_number = 1;
for (; cur != end; ++cur) {
c->Message(Chat::White, " %s", *cur); 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) { c->Message(
const char *catfilt = nullptr; 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]) { if (std::string("all") != sep->arg[2]) {
catfilt = sep->arg[2]; category_name = sep->arg[2];
} }
std::vector<const char *> rule_list;
if (!RuleManager::Instance()->ListRules(catfilt, rule_list)) { std::vector<std::string> l;
c->Message(Chat::Red, "Failed to list rules!"); if (!RuleManager::Instance()->ListRules(category_name, l)) {
c->Message(Chat::White, "Failed to list rules!");
return; return;
} }
c->Message(Chat::White, "Rules in category %s:", sep->arg[2]);
std::vector<const char *>::iterator cur, end; c->Message(
cur = rule_list.begin(); Chat::White,
end = rule_list.end(); fmt::format(
for (; cur != end; ++cur) { "Rules in {} Category:",
c->Message(Chat::White, " %s", *cur); 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(
c->Message(Chat::Red, "Invalid argument count, see help."); Chat::White,
} fmt::format(
} "There {} {} available Rule{} in the {} Category.",
else if (!strcasecmp(sep->arg[1], "values")) { rule_count != 1 ? "are" : "is",
if (sep->argnum != 2) { rule_count,
c->Message(Chat::Red, "Invalid argument count, see help."); rule_count != 1 ? "s" : "",
category_name
).c_str()
);
} else {
SendRuleSubCommands(c);
return; return;
} }
else { } else if (is_values) {
const char *catfilt = nullptr; if (arguments == 2) {
std::string category_name;
if (std::string("all") != sep->arg[2]) { if (std::string("all") != sep->arg[2]) {
catfilt = sep->arg[2]; category_name = sep->arg[2];
} }
std::vector<const char *> rule_list;
if (!RuleManager::Instance()->ListRules(catfilt, rule_list)) { std::vector<std::string> l;
c->Message(Chat::Red, "Failed to list rules!"); if (!RuleManager::Instance()->ListRules(category_name, l)) {
c->Message(Chat::White, "Failed to list rules!");
return; return;
} }
c->Message(Chat::White, "Rules & values in category %s:", sep->arg[2]);
std::vector<const char *>::iterator cur, end; c->Message(
cur = rule_list.begin(); Chat::White,
end = rule_list.end(); fmt::format(
for (std::string tmp_value; cur != end; ++cur) { "Rule Values in {} Category:",
if (RuleManager::Instance()->GetRule(*cur, tmp_value)) { category_name
c->Message(Chat::White, " %s - %s", *cur, tmp_value.c_str()); ).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++;
} }
} }
}
} c->Message(
else { Chat::White,
c->Message(Chat::Yellow, "Invalid action specified. use '#rules help' for help"); 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;
}

View File

@ -59,6 +59,7 @@
#include "zone_reload.h" #include "zone_reload.h"
#include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/repositories/criteria/content_filter_criteria.h"
#include "../common/repositories/content_flags_repository.h" #include "../common/repositories/content_flags_repository.h"
#include "../common/repositories/rule_sets_repository.h"
#include "../common/repositories/zone_points_repository.h" #include "../common/repositories/zone_points_repository.h"
#include "../common/serverinfo.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 that fails, try the file name, then load defaults
if (RuleManager::Instance()->GetActiveRulesetID() != default_ruleset) { 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) { if (r_name.size() > 0) {
RuleManager::Instance()->LoadRules(&database, r_name.c_str(), false); RuleManager::Instance()->LoadRules(&database, r_name.c_str(), false);
} }