Merge pull request #894 from EQEmu/io_work

Io work
This commit is contained in:
Uleat 2019-09-09 23:57:43 -04:00 committed by GitHub
commit 7911225960
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1364 additions and 724 deletions

View File

@ -1,5 +1,27 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 9/04/2019 ==
Uleat: Added code to restore rule notes to their original values
- The code is inactive by default
- Change rule 'World:RestoreRuleNotes' to 'true' to enable this feature
- Rule will not exist until the server is run for the first time after updating
== 9/02/2019 ==
Uleat: Added code to inject new rules into the 'default' ruleset and remove orphaned rules from all rulesets
- New rules are only added using the 'default' ruleset - Other rulesets will need to be added manually or through in-game updates
-- Rule notes are now loaded into the system's hard-coded entries and will now propagate properly into database updates
- Defunct rules will have their orhpaned entries removed from the `rule_values` table for the all rulesets
Note: If you would like to add these rules before starting your server so that you can modify them, start world.exe
manually and wait for the console messages to finish. It should take 5-10 seconds, or so. The world log should contain
a list of the added and removed entries, IF the `file` field of the 'Status' logging category is set to 1 or higher.
(Don't forget to manually stop the process after the update is complete.)
== 8/30/2019 ==
Uleat: Added code to inject new commands and remove orphaned commands from both command systems
- New commands are added with their status (`access`) set to the server default value - no aliases are defined
- Defunct commands will have their orhpaned entries removed from the command settings table for each system
== 8/16/2019 ==
Akkadius: Simplified the use of roamboxes and improved the AI for roambox pathing https://i.imgur.com/z33u7y9.gif
Akkadius: Implemented command #roambox set <box_size> [move_delay]

View File

@ -21,6 +21,7 @@
#include "string_util.h"
#include <cstdlib>
#include <cstring>
#include <fmt/format.h>
/*
Commands:
@ -45,14 +46,14 @@ const char *RuleManager::s_categoryNames[_CatCount+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) \
{ #cat ":" #rule, Category__##cat, IntRule, Int__##rule },
#define RULE_INT(cat, rule, default_value, notes) \
{ #cat ":" #rule, Category__##cat, IntRule, Int__##rule, notes },
#include "ruletypes.h"
#define RULE_REAL(cat, rule, default_value) \
{ #cat ":" #rule, Category__##cat, RealRule, Real__##rule },
#define RULE_REAL(cat, rule, default_value, notes) \
{ #cat ":" #rule, Category__##cat, RealRule, Real__##rule, notes },
#include "ruletypes.h"
#define RULE_BOOL(cat, rule, default_value) \
{ #cat ":" #rule, Category__##cat, BoolRule, Bool__##rule },
#define RULE_BOOL(cat, rule, default_value, notes) \
{ #cat ":" #rule, Category__##cat, BoolRule, Bool__##rule, notes },
#include "ruletypes.h"
{ "Invalid Rule", _CatCount, IntRule }
};
@ -178,11 +179,11 @@ void RuleManager::ResetRules(bool reload) {
}
Log(Logs::Detail, Logs::Rules, "Resetting running rules to default values");
#define RULE_INT(cat, rule, default_value) \
#define RULE_INT(cat, rule, default_value, notes) \
m_RuleIntValues[ Int__##rule ] = default_value;
#define RULE_REAL(cat, rule, default_value) \
#define RULE_REAL(cat, rule, default_value, notes) \
m_RuleRealValues[ Real__##rule ] = default_value;
#define RULE_BOOL(cat, rule, default_value) \
#define RULE_BOOL(cat, rule, default_value, notes) \
m_RuleBoolValues[ Bool__##rule ] = default_value;
#include "ruletypes.h"
@ -214,19 +215,101 @@ bool RuleManager::_FindRule(const char *rule_name, RuleType &type_into, uint16 &
//assumes index is valid!
const char *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);
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("InvalidRule??");
return(s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount].name); // no need to create a string when one already exists...
}
//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;
}
//should never happen
return(s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount].notes);
}
bool RuleManager::LoadRules(Database *database, const char *ruleset_name, bool reload) {
int ruleset_id = this->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);
}
m_activeRuleset = ruleset_id;
m_activeName = ruleset_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());
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()
);
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]);
}
}
}
Log(Logs::Detail, Logs::Rules, "Processing rule set '%s' (%d) load...", ruleset_name, ruleset_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()) {
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]);
}
}
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) {
@ -257,56 +340,6 @@ void RuleManager::SaveRules(Database *database, const char *ruleset_name) {
}
}
bool RuleManager::LoadRules(Database *database, const char *ruleset_name, bool reload) {
int ruleset_id = this->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);
}
Log(Logs::Detail, Logs::Rules, "Loading rule set '%s' (%d)", ruleset_name, ruleset_id);
m_activeRuleset = ruleset_id;
m_activeName = ruleset_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());
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());
return (false);
}
Log(Logs::Detail, Logs::Rules, "Loading rule set '%s' (%d)", 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]);
}
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())
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]);
return true;
}
void RuleManager::_SaveRule(Database *database, RuleType type, uint16 index) {
char value_string[100];
@ -328,17 +361,248 @@ void RuleManager::_SaveRule(Database *database, RuleType type, uint16 index) {
}
std::string query = StringFormat(
"REPLACE INTO rule_values "
"(ruleset_id, rule_name, rule_value) "
" VALUES(%d, '%s', '%s')",
"REPLACE INTO `rule_values`"
"(`ruleset_id`, `rule_name`, `rule_value`, `notes`)"
" VALUES('%d', '%s', '%s', '%s')",
m_activeRuleset,
_GetRuleName(type, index),
value_string
value_string,
EscapeString(_GetRuleNotes(type, index)).c_str()
);
database->QueryDatabase(query);
}
bool RuleManager::UpdateInjectedRules(Database *db, const char *ruleset_name, bool quiet_update)
{
std::vector<std::string> database_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;
if (ruleset_name == nullptr) {
return false;
}
int ruleset_id = GetRulesetID(db, ruleset_name);
if (ruleset_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()) {
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) {
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;
}
}
// 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()) {
injected_rule_entries.push_back(
std::tuple<int, std::string, std::string, std::string>(
ruleset_id, // `ruleset_id`
rd_iter.first, // `rule_name`
rd_iter.second.first, // `rule_value`
EscapeString(*rd_iter.second.second) // `notes`
)
);
if (!quiet_update) {
Log(Logs::General,
Logs::Status,
"New Rule '%s' found... Adding to `rule_values` table with ruleset '%s' (%i) and rule value '%s'...",
rd_iter.first.c_str(),
ruleset_name,
ruleset_id,
rd_iter.second.first.c_str()
);
}
}
}
if (injected_rule_entries.size()) {
std::string query(
fmt::format(
"REPLACE INTO `rule_values`(`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES {}",
implode(
",",
std::pair<char, char>('(', ')'),
join_tuple(",", std::pair<char, char>('\'', '\''), injected_rule_entries)
)
)
);
if (!db->QueryDatabase(query).Success()) {
return false;
}
Log(Logs::General,
Logs::Status,
"%u New Rule%s Added to ruleset '%s' (%i)",
injected_rule_entries.size(),
(injected_rule_entries.size() == 1 ? "" : "s"),
ruleset_name,
ruleset_id
);
}
return true;
}
bool RuleManager::UpdateOrphanedRules(Database *db, bool quiet_update)
{
std::vector<std::string> rule_data;
std::vector<std::string> 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()) {
return false;
}
// build rule data entries
for (const auto &ri_iter : s_RuleInfo) {
if (strcasecmp(ri_iter.name, "Invalid Rule") == 0) {
continue;
}
rule_data.push_back(ri_iter.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]));
if (!quiet_update) {
Log(Logs::General,
Logs::Status,
"Rule '%s' no longer exists... Deleting orphaned entry from `rule_values` table...",
row[0]
);
}
}
}
if (orphaned_rule_entries.size()) {
std::string query (
fmt::format(
"DELETE FROM `rule_values` WHERE `rule_name` IN ({})",
implode(",", std::pair<char, char>('\'', '\''), orphaned_rule_entries)
)
);
if (!db->QueryDatabase(query).Success()) {
return false;
}
Log(Logs::General,
Logs::Status,
"%u Orphaned Rule%s Deleted from 'All Rulesets' (-1)",
orphaned_rule_entries.size(),
(orphaned_rule_entries.size() == 1 ? "" : "s")
);
}
return true;
}
bool RuleManager::RestoreRuleNotes(Database *db)
{
std::string query("SELECT `ruleset_id`, `rule_name`, IFNULL(`notes`, '\\0')`notes` FROM `rule_values`");
auto results = db->QueryDatabase(query);
if (!results.Success()) {
return false;
}
int update_count = 0;
for (auto row = results.begin(); row != results.end(); ++row) {
const auto &rule = [&row]() {
for (const auto &rule_iter : s_RuleInfo) {
if (strcasecmp(rule_iter.name, row[1]) == 0) {
return rule_iter;
}
}
return s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount];
}();
if (strcasecmp(rule.name, row[1]) != 0) {
continue;
}
if (rule.notes.compare(row[2]) == 0) {
continue;
}
std::string query(
fmt::format(
"UPDATE `rule_values` SET `notes` = '{}' WHERE `ruleset_id` = '{}' AND `rule_name` = '{}'",
EscapeString(rule.notes),
row[0],
row[1]
)
);
if (!db->QueryDatabase(query).Success()) {
continue;
}
++update_count;
}
if (update_count > 0) {
Log(Logs::General, Logs::Status, "%u Rule Note%s Restored", update_count, (update_count == 1 ? "" : "s"));
}
}
int RuleManager::GetRulesetID(Database *database, const char *ruleset_name) {

View File

@ -49,21 +49,21 @@ class RuleManager {
public:
//generate our rule enums:
typedef enum {
#define RULE_INT(cat, rule, default_value) \
#define RULE_INT(cat, rule, default_value, notes) \
Int__##rule,
#include "ruletypes.h"
_IntRuleCount
} IntType;
typedef enum {
#define RULE_REAL(cat, rule, default_value) \
#define RULE_REAL(cat, rule, default_value, notes) \
Real__##rule,
#include "ruletypes.h"
_RealRuleCount
} RealType;
typedef enum {
#define RULE_BOOL(cat, rule, default_value) \
#define RULE_BOOL(cat, rule, default_value, notes) \
Bool__##rule,
#include "ruletypes.h"
_BoolRuleCount
@ -97,6 +97,9 @@ public:
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<const char *> &into);
@ -113,6 +116,9 @@ public:
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 UpdateOrphanedRules(Database *db, bool quiet_update = false);
bool RestoreRuleNotes(Database *db);
private:
RuleManager();
@ -137,15 +143,17 @@ private:
static bool _FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into);
static const char *_GetRuleName(RuleType type, uint16 index);
static const std::string &_GetRuleNotes(RuleType type, uint16 index);
static int _FindOrCreateRuleset(Database *db, const char *ruleset);
void _SaveRule(Database *db, RuleType type, uint16 index);
static const char *s_categoryNames[];
typedef struct {
const char *name;
CategoryType category;
RuleType type;
uint16 rule_index; //index into its 'type' array
const std::string notes;
} RuleInfo;
static const RuleInfo s_RuleInfo[];

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@
#include <iostream>
#include <cstring>
#include <fmt/format.h>
#if defined(_MSC_VER) && _MSC_VER >= 1800
#include <algorithm>
@ -1469,6 +1470,58 @@ bool SharedDatabase::GetCommandSettings(std::map<std::string, std::pair<uint8, s
return true;
}
bool SharedDatabase::UpdateInjectedCommandSettings(const std::vector<std::pair<std::string, uint8>> &injected)
{
if (injected.size()) {
std::string query = fmt::format(
"REPLACE INTO `command_settings`(`command`, `access`) VALUES {}",
implode(
",",
std::pair<char, char>('(', ')'),
join_pair(",", std::pair<char, char>('\'', '\''), injected)
)
);
if (!QueryDatabase(query).Success()) {
return false;
}
Log(Logs::General,
Logs::Status,
"%u New Command%s Added",
injected.size(),
(injected.size() == 1 ? "" : "s")
);
}
return true;
}
bool SharedDatabase::UpdateOrphanedCommandSettings(const std::vector<std::string> &orphaned)
{
if (orphaned.size()) {
std::string query = fmt::format(
"DELETE FROM `command_settings` WHERE `command` IN ({})",
implode(",", std::pair<char, char>('\'', '\''), orphaned)
);
if (!QueryDatabase(query).Success()) {
return false;
}
Log(Logs::General,
Logs::Status,
"%u Orphaned Command%s Deleted",
orphaned.size(),
(orphaned.size() == 1 ? "" : "s")
);
}
return true;
}
bool SharedDatabase::LoadSkillCaps(const std::string &prefix) {
skill_caps_mmf.reset(nullptr);

View File

@ -71,6 +71,8 @@ class SharedDatabase : public Database
void LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message);
void SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message);
bool GetCommandSettings(std::map<std::string, std::pair<uint8, std::vector<std::string>>> &command_settings);
bool UpdateInjectedCommandSettings(const std::vector<std::pair<std::string, uint8>> &injected);
bool UpdateOrphanedCommandSettings(const std::vector<std::string> &orphaned);
uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID);
void SetMailKey(int CharID, int IPAddress, int MailKey);
std::string GetMailKey(int CharID, bool key_only = false);

View File

@ -144,6 +144,7 @@ std::string implode(std::string glue, std::vector<std::string> src)
return final_output;
}
std::string EscapeString(const std::string &s) {
std::string ret;
@ -514,4 +515,4 @@ bool isAlphaNumeric(const char *text)
}
return true;
}
}

View File

@ -20,6 +20,12 @@
#include <string.h>
#include <vector>
#include <cstdarg>
#include <tuple>
#ifndef _WIN32
// this doesn't appear to affect linux-based systems..need feedback for _WIN64
#include <fmt/format.h>
#endif
#include "types.h"
@ -31,6 +37,93 @@ std::vector<std::string> split(std::string str_to_split, char delimiter);
const std::string StringFormat(const char* format, ...);
const std::string vStringFormat(const char* format, va_list args);
std::string implode(std::string glue, std::vector<std::string> src);
template <typename T>
std::string implode(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
{
if (src.empty()) {
return {};
}
std::ostringstream oss;
for (const T &src_iter : src) {
oss << encapsulation.first << src_iter << encapsulation.second << glue;
}
std::string output(oss.str());
output.resize(output.size() - glue.size());
return output;
}
// _WIN32 builds require that #include<fmt/format.h> be included in whatever code file the invocation is made from (no header files)
template <typename T1, typename T2>
std::vector<std::string> join_pair(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<std::pair<T1, T2>> &src)
{
if (src.empty()) {
return {};
}
std::vector<std::string> output;
for (const std::pair<T1, T2> &src_iter : src) {
output.push_back(
fmt::format(
"{}{}{}{}{}{}{}",
encapsulation.first,
src_iter.first,
encapsulation.second,
glue,
encapsulation.first,
src_iter.second,
encapsulation.second
)
);
}
return output;
}
// _WIN32 builds require that #include<fmt/format.h> be included in whatever code file the invocation is made from (no header files)
template <typename T1, typename T2, typename T3, typename T4>
std::vector<std::string> join_tuple(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<std::tuple<T1, T2, T3, T4>> &src)
{
if (src.empty()) {
return {};
}
std::vector<std::string> output;
for (const std::tuple<T1, T2, T3, T4> &src_iter : src) {
output.push_back(
fmt::format(
"{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
encapsulation.first,
std::get<0>(src_iter),
encapsulation.second,
glue,
encapsulation.first,
std::get<1>(src_iter),
encapsulation.second,
glue,
encapsulation.first,
std::get<2>(src_iter),
encapsulation.second,
glue,
encapsulation.first,
std::get<3>(src_iter),
encapsulation.second
)
);
}
return output;
}
std::vector<std::string> SplitString(const std::string &s, char delim);
std::string EscapeString(const char *src, size_t sz);
std::string EscapeString(const std::string &s);

View File

@ -332,16 +332,28 @@ int main(int argc, char** argv) {
Log(Logs::General, Logs::World_Server, "Error: Could not load skill cap data. But ignoring");
Log(Logs::General, Logs::World_Server, "Loading guilds..");
guild_mgr.LoadGuilds();
//rules:
{
if (!RuleManager::Instance()->UpdateOrphanedRules(&database)) {
Log(Logs::General, Logs::World_Server, "Failed to process 'Orphaned Rules' update operation.");
}
if (!RuleManager::Instance()->UpdateInjectedRules(&database, "default")) {
Log(Logs::General, Logs::World_Server, "Failed to process 'Injected Rules' for ruleset 'default' update operation.");
}
std::string tmp;
if (database.GetVariable("RuleSet", tmp)) {
Log(Logs::General, Logs::World_Server, "Loading rule set '%s'", tmp.c_str());
if (!RuleManager::Instance()->LoadRules(&database, tmp.c_str(), false)) {
Log(Logs::General, Logs::World_Server, "Failed to load ruleset '%s', falling back to defaults.", tmp.c_str());
}
}
else {
if (!RuleManager::Instance()->LoadRules(&database, "default", false)) {
Log(Logs::General, Logs::World_Server, "No rule set configured, using default rules");
}
@ -350,17 +362,19 @@ int main(int argc, char** argv) {
}
}
EQEmu::InitializeDynamicLookups();
Log(Logs::General, Logs::World_Server, "Initialized dynamic dictionary entries");
if (!RuleManager::Instance()->RestoreRuleNotes(&database)) {
Log(Logs::General, Logs::World_Server, "Failed to process 'Restore Rule Notes' update operation.");
}
}
EQEmu::InitializeDynamicLookups();
Log(Logs::General, Logs::World_Server, "Initialized dynamic dictionary entries");
if (RuleB(World, ClearTempMerchantlist)) {
Log(Logs::General, Logs::World_Server, "Clearing temporary merchant lists..");
database.ClearMerchantTemp();
}
RuleManager::Instance()->SaveRules(&database);
Log(Logs::General, Logs::World_Server, "Loading EQ time of day..");
TimeOfDay_Struct eqTime;
time_t realtime;

View File

@ -1429,35 +1429,99 @@ int bot_command_init(void)
std::map<std::string, std::pair<uint8, std::vector<std::string>>> bot_command_settings;
database.botdb.LoadBotCommandSettings(bot_command_settings);
std::vector<std::pair<std::string, uint8>> injected_bot_command_settings;
std::vector<std::string> orphaned_bot_command_settings;
for (auto bcs_iter : bot_command_settings) {
auto bcl_iter = bot_command_list.find(bcs_iter.first);
if (bcl_iter == bot_command_list.end()) {
orphaned_bot_command_settings.push_back(bcs_iter.first);
Log(Logs::General,
Logs::Status,
"Bot Command '%s' no longer exists... Deleting orphaned entry from `bot_command_settings` table...",
bcs_iter.first.c_str()
);
}
}
if (orphaned_bot_command_settings.size()) {
if (!database.botdb.UpdateOrphanedBotCommandSettings(orphaned_bot_command_settings)) {
Log(Logs::General, Logs::Zone_Server, "Failed to process 'Orphaned Bot Commands' update operation.");
}
}
auto working_bcl = bot_command_list;
for (auto working_bcl_iter : working_bcl) {
auto bot_command_settings_iter = bot_command_settings.find(working_bcl_iter.first);
if (bot_command_settings_iter == bot_command_settings.end()) {
if (working_bcl_iter.second->access == 0)
Log(Logs::General, Logs::Commands, "bot_command_init(): Warning: Bot Command '%s' defaulting to access level 0!", working_bcl_iter.first.c_str());
auto bcs_iter = bot_command_settings.find(working_bcl_iter.first);
if (bcs_iter == bot_command_settings.end()) {
injected_bot_command_settings.push_back(std::pair<std::string, uint8>(working_bcl_iter.first, working_bcl_iter.second->access));
Log(Logs::General,
Logs::Status,
"New Bot Command '%s' found... Adding to `bot_command_settings` table with access '%u'...",
working_bcl_iter.first.c_str(),
working_bcl_iter.second->access
);
if (working_bcl_iter.second->access == 0) {
Log(Logs::General,
Logs::Commands,
"bot_command_init(): Warning: Bot Command '%s' defaulting to access level 0!",
working_bcl_iter.first.c_str()
);
}
continue;
}
working_bcl_iter.second->access = bot_command_settings_iter->second.first;
Log(Logs::General, Logs::Commands, "bot_command_init(): - Bot Command '%s' set to access level %d.", working_bcl_iter.first.c_str(), bot_command_settings_iter->second.first);
if (bot_command_settings_iter->second.second.empty())
working_bcl_iter.second->access = bcs_iter->second.first;
Log(Logs::General,
Logs::Commands,
"bot_command_init(): - Bot Command '%s' set to access level %d.",
working_bcl_iter.first.c_str(),
bcs_iter->second.first
);
if (bcs_iter->second.second.empty()) {
continue;
}
for (auto alias_iter : bot_command_settings_iter->second.second) {
if (alias_iter.empty())
for (auto alias_iter : bcs_iter->second.second) {
if (alias_iter.empty()) {
continue;
}
if (bot_command_list.find(alias_iter) != bot_command_list.end()) {
Log(Logs::General, Logs::Commands, "bot_command_init(): Warning: Alias '%s' already exists as a bot command - skipping!", alias_iter.c_str());
Log(Logs::General,
Logs::Commands,
"bot_command_init(): Warning: Alias '%s' already exists as a bot command - skipping!",
alias_iter.c_str()
);
continue;
}
bot_command_list[alias_iter] = working_bcl_iter.second;
bot_command_aliases[alias_iter] = working_bcl_iter.first;
Log(Logs::General, Logs::Commands, "bot_command_init(): - Alias '%s' added to bot command '%s'.", alias_iter.c_str(), bot_command_aliases[alias_iter].c_str());
Log(Logs::General,
Logs::Commands,
"bot_command_init(): - Alias '%s' added to bot command '%s'.",
alias_iter.c_str(),
bot_command_aliases[alias_iter].c_str()
);
}
}
if (injected_bot_command_settings.size()) {
if (!database.botdb.UpdateInjectedBotCommandSettings(injected_bot_command_settings)) {
Log(Logs::General, Logs::Zone_Server, "Failed to process 'Injected Bot Commands' update operation.");
}
}
bot_command_dispatch = bot_command_real_dispatch;
BCSpells::Load();

View File

@ -18,6 +18,8 @@
#ifdef BOTS
#include <fmt/format.h>
#include "../common/global_define.h"
#include "../common/rulesys.h"
#include "../common/string_util.h"
@ -54,6 +56,58 @@ bool BotDatabase::LoadBotCommandSettings(std::map<std::string, std::pair<uint8,
return true;
}
bool BotDatabase::UpdateInjectedBotCommandSettings(const std::vector<std::pair<std::string, uint8>> &injected)
{
if (injected.size()) {
query = fmt::format(
"REPLACE INTO `bot_command_settings`(`bot_command`, `access`) VALUES {}",
implode(
",",
std::pair<char, char>('(', ')'),
join_pair(",", std::pair<char, char>('\'', '\''), injected)
)
);
if (!database.QueryDatabase(query).Success()) {
return false;
}
Log(Logs::General,
Logs::Status,
"%u New Bot Command%s Added",
injected.size(),
(injected.size() == 1 ? "" : "s")
);
}
return true;
}
bool BotDatabase::UpdateOrphanedBotCommandSettings(const std::vector<std::string> &orphaned)
{
if (orphaned.size()) {
query = fmt::format(
"DELETE FROM `bot_command_settings` WHERE `bot_command` IN ({})",
implode(",", std::pair<char, char>('\'', '\''), orphaned)
);
if (!database.QueryDatabase(query).Success()) {
return false;
}
Log(Logs::General,
Logs::Status,
"%u Orphaned Bot Command%s Deleted",
orphaned.size(),
(orphaned.size() == 1 ? "" : "s")
);
}
return true;
}
bool BotDatabase::LoadBotSpellCastingChances()
{
query =

View File

@ -43,6 +43,8 @@ class BotDatabase
{
public:
bool LoadBotCommandSettings(std::map<std::string, std::pair<uint8, std::vector<std::string>>> &bot_command_settings);
bool UpdateInjectedBotCommandSettings(const std::vector<std::pair<std::string, uint8>> &injected);
bool UpdateOrphanedBotCommandSettings(const std::vector<std::string> &orphaned);
bool LoadBotSpellCastingChances();

View File

@ -454,33 +454,96 @@ int command_init(void)
std::map<std::string, std::pair<uint8, std::vector<std::string>>> command_settings;
database.GetCommandSettings(command_settings);
std::map<std::string, CommandRecord *> working_cl = commandlist;
for (auto iter_cl = working_cl.begin(); iter_cl != working_cl.end(); ++iter_cl) {
auto iter_cs = command_settings.find(iter_cl->first);
if (iter_cs == command_settings.end()) {
if (iter_cl->second->access == 0)
Log(Logs::General, Logs::Commands, "command_init(): Warning: Command '%s' defaulting to access level 0!", iter_cl->first.c_str());
std::vector<std::pair<std::string, uint8>> injected_command_settings;
std::vector<std::string> orphaned_command_settings;
for (auto cs_iter : command_settings) {
auto cl_iter = commandlist.find(cs_iter.first);
if (cl_iter == commandlist.end()) {
orphaned_command_settings.push_back(cs_iter.first);
Log(Logs::General,
Logs::Status,
"Command '%s' no longer exists... Deleting orphaned entry from `command_settings` table...",
cs_iter.first.c_str()
);
}
}
if (orphaned_command_settings.size()) {
if (!database.UpdateOrphanedCommandSettings(orphaned_command_settings)) {
Log(Logs::General, Logs::Zone_Server, "Failed to process 'Orphaned Commands' update operation.");
}
}
auto working_cl = commandlist;
for (auto working_cl_iter : working_cl) {
auto cs_iter = command_settings.find(working_cl_iter.first);
if (cs_iter == command_settings.end()) {
injected_command_settings.push_back(std::pair<std::string, uint8>(working_cl_iter.first, working_cl_iter.second->access));
Log(Logs::General,
Logs::Status,
"New Command '%s' found... Adding to `command_settings` table with access '%u'...",
working_cl_iter.first.c_str(),
working_cl_iter.second->access
);
if (working_cl_iter.second->access == 0) {
Log(Logs::General,
Logs::Commands,
"command_init(): Warning: Command '%s' defaulting to access level 0!",
working_cl_iter.first.c_str()
);
}
continue;
}
iter_cl->second->access = iter_cs->second.first;
Log(Logs::General, Logs::Commands, "command_init(): - Command '%s' set to access level %d.", iter_cl->first.c_str(), iter_cs->second.first);
if (iter_cs->second.second.empty())
working_cl_iter.second->access = cs_iter->second.first;
Log(Logs::General,
Logs::Commands,
"command_init(): - Command '%s' set to access level %d.",
working_cl_iter.first.c_str(),
cs_iter->second.first
);
if (cs_iter->second.second.empty()) {
continue;
}
for (auto iter_aka = iter_cs->second.second.begin(); iter_aka != iter_cs->second.second.end();
++iter_aka) {
if (iter_aka->empty())
continue;
if (commandlist.find(*iter_aka) != commandlist.end()) {
Log(Logs::General, Logs::Commands, "command_init(): Warning: Alias '%s' already exists as a command - skipping!", iter_aka->c_str());
for (auto alias_iter : cs_iter->second.second) {
if (alias_iter.empty()) {
continue;
}
commandlist[*iter_aka] = iter_cl->second;
commandaliases[*iter_aka] = iter_cl->first;
if (commandlist.find(alias_iter) != commandlist.end()) {
Log(Logs::General,
Logs::Commands,
"command_init(): Warning: Alias '%s' already exists as a command - skipping!",
alias_iter.c_str()
);
continue;
}
Log(Logs::General, Logs::Commands, "command_init(): - Alias '%s' added to command '%s'.", iter_aka->c_str(), commandaliases[*iter_aka].c_str());
commandlist[alias_iter] = working_cl_iter.second;
commandaliases[alias_iter] = working_cl_iter.first;
Log(Logs::General,
Logs::Commands,
"command_init(): - Alias '%s' added to command '%s'.",
alias_iter.c_str(),
commandaliases[alias_iter].c_str()
);
}
}
if (injected_command_settings.size()) {
if (!database.UpdateInjectedCommandSettings(injected_command_settings)) {
Log(Logs::General, Logs::Zone_Server, "Failed to process 'Injected Commands' update operation.");
}
}

View File

@ -2327,17 +2327,17 @@ luabind::scope lua_register_rules_const() {
return luabind::class_<Rule>("Rule")
.enum_("constants")
[
#define RULE_INT(cat, rule, default_value) \
#define RULE_INT(cat, rule, default_value, notes) \
luabind::value(#rule, RuleManager::Int__##rule),
#include "../common/ruletypes.h"
luabind::value("_IntRuleCount", RuleManager::_IntRuleCount),
#undef RULE_INT
#define RULE_REAL(cat, rule, default_value) \
#define RULE_REAL(cat, rule, default_value, notes) \
luabind::value(#rule, RuleManager::Real__##rule),
#include "../common/ruletypes.h"
luabind::value("_RealRuleCount", RuleManager::_RealRuleCount),
#undef RULE_REAL
#define RULE_BOOL(cat, rule, default_value) \
#define RULE_BOOL(cat, rule, default_value, notes) \
luabind::value(#rule, RuleManager::Bool__##rule),
#include "../common/ruletypes.h"
luabind::value("_BoolRuleCount", RuleManager::_BoolRuleCount)