diff --git a/changelog.txt b/changelog.txt index 84d38df4a..0a9304c64 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 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 [move_delay] diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 97585670c..c07869ec8 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -18,6 +18,7 @@ #include #include +#include #if defined(_MSC_VER) && _MSC_VER >= 1800 #include @@ -1469,6 +1470,37 @@ bool SharedDatabase::GetCommandSettings(std::map> &injected, const std::vector &orphaned) +{ + bool return_value = true; + + if (injected.size()) { + + std::string query = fmt::format( + "REPLACE INTO `command_settings`(`command`, `access`) VALUES {}", + implode(",", string_string("(", ")"), join_pair(string_string(), ",", string_string("'", "'"), injected)) + ); + + if (!QueryDatabase(query).Success()) { + return_value = false; + } + } + + if (orphaned.size()) { + + std::string query = fmt::format( + "DELETE FROM `command_settings` WHERE `command` IN ({})", + implode(",", string_string("'", "'"), orphaned) + ); + + if (!QueryDatabase(query).Success()) { + return_value = false; + } + } + + return return_value; +} + bool SharedDatabase::LoadSkillCaps(const std::string &prefix) { skill_caps_mmf.reset(nullptr); diff --git a/common/shareddb.h b/common/shareddb.h index f6b1879df..1fae1eb7e 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -71,6 +71,7 @@ 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>> &command_settings); + bool UpdateCommandSettings(const std::vector> &injected, const std::vector &orphaned); uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID); void SetMailKey(int CharID, int IPAddress, int MailKey); std::string GetMailKey(int CharID, bool key_only = false); diff --git a/common/string_util.cpp b/common/string_util.cpp index ec53663cc..a3c775777 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -16,6 +16,7 @@ #include "string_util.h" #include +#include #ifdef _WINDOWS #include @@ -144,6 +145,53 @@ std::string implode(std::string glue, std::vector src) return final_output; } +std::string implode(std::string glue, string_string encapsulation, std::vector src) +{ + if (src.empty()) { + return {}; + } + + std::ostringstream output; + std::vector::iterator src_iter; + + for (src_iter = src.begin(); src_iter != src.end(); src_iter++) { + output << encapsulation.first << *src_iter << encapsulation.second << glue; + } + + std::string final_output = output.str(); + final_output.resize(output.str().size() - glue.size()); + + return final_output; +} + +std::vector join_pair(string_string outer_encap, std::string joiner, string_string inner_encap, std::vector> src) +{ + if (src.empty()) { + return {}; + } + + std::vector output; + + for (auto src_iter : src) { + output.push_back( + fmt::format( + "{}{}{}{}{}{}{}{}{}", + outer_encap.first, + inner_encap.first, + src_iter.first, + inner_encap.second, + joiner, + inner_encap.first, + src_iter.second, + inner_encap.second, + outer_encap.second + ) + ); + } + + return output; +} + std::string EscapeString(const std::string &s) { std::string ret; @@ -514,4 +562,4 @@ bool isAlphaNumeric(const char *text) } return true; -} \ No newline at end of file +} diff --git a/common/string_util.h b/common/string_util.h index 23a8af05b..6b0ac8bbc 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -31,6 +31,10 @@ std::vector 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 src); +typedef std::pair string_string; +std::string implode(std::string glue, string_string encapsulation, std::vector src); +// wanted to make 'join_pair' a template function..this will do for now +std::vector join_pair(string_string outer_encap, std::string joiner, string_string inner_encap, std::vector> src); std::vector SplitString(const std::string &s, char delim); std::string EscapeString(const char *src, size_t sz); std::string EscapeString(const std::string &s); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 31270cc0f..dfbb59502 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1426,23 +1426,51 @@ int bot_command_init(void) std::map>> bot_command_settings; database.botdb.LoadBotCommandSettings(bot_command_settings); + std::vector> injected_bot_command_settings; + std::vector orphaned_bot_command_settings; + 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()); + + injected_bot_command_settings.push_back(std::pair(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()) + 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()) { continue; + } for (auto alias_iter : bot_command_settings_iter->second.second) { - if (alias_iter.empty()) + 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()); continue; @@ -1455,6 +1483,24 @@ int bot_command_init(void) } } + 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 (injected_bot_command_settings.size() || orphaned_bot_command_settings.size()) { + database.botdb.UpdateBotCommandSettings(injected_bot_command_settings, orphaned_bot_command_settings); + } + bot_command_dispatch = bot_command_real_dispatch; BCSpells::Load(); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 29d0f37e9..b87ff2c00 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -18,6 +18,8 @@ #ifdef BOTS +#include + #include "../common/global_define.h" #include "../common/rulesys.h" #include "../common/string_util.h" @@ -52,6 +54,37 @@ bool BotDatabase::LoadBotCommandSettings(std::map> &injected, const std::vector &orphaned) +{ + bool return_value = true; + + if (injected.size()) { + + query = fmt::format( + "REPLACE INTO `bot_command_settings`(`bot_command`, `access`) VALUES {}", + implode(",", string_string("(", ")"), join_pair(string_string(), ",", string_string("'", "'"), injected)) + ); + + if (!database.QueryDatabase(query).Success()) { + return_value = false; + } + } + + if (orphaned.size()) { + + query = fmt::format( + "DELETE FROM `bot_command_settings` WHERE `bot_command` IN ({})", + implode(",", string_string("'", "'"), orphaned) + ); + + if (!database.QueryDatabase(query).Success()) { + return_value = false; + } + } + + return return_value; +} + bool BotDatabase::LoadBotSpellCastingChances() { query = diff --git a/zone/bot_database.h b/zone/bot_database.h index 0c18eade5..2031a575d 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -43,6 +43,7 @@ class BotDatabase { public: bool LoadBotCommandSettings(std::map>> &bot_command_settings); + bool UpdateBotCommandSettings(const std::vector> &injected, const std::vector &orphaned); bool LoadBotSpellCastingChances(); diff --git a/zone/command.cpp b/zone/command.cpp index b979716c8..45ed0cbba 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -453,36 +453,91 @@ int command_init(void) std::map>> command_settings; database.GetCommandSettings(command_settings); + std::vector> injected_command_settings; + std::vector orphaned_command_settings; + std::map 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()); + + injected_command_settings.push_back(std::pair(iter_cl->first, iter_cl->second->access)); + Log(Logs::General, + Logs::Status, + "New Command '%s' found... Adding to `command_settings` table with access '%u'...", + iter_cl->first.c_str(), + iter_cl->second->access + ); + + 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() + ); + } + 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()) + 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()) { continue; + } - for (auto iter_aka = iter_cs->second.second.begin(); iter_aka != iter_cs->second.second.end(); - ++iter_aka) { - if (iter_aka->empty()) + 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()); + Log(Logs::General, + Logs::Commands, + "command_init(): Warning: Alias '%s' already exists as a command - skipping!", + iter_aka->c_str() + ); + continue; } commandlist[*iter_aka] = iter_cl->second; commandaliases[*iter_aka] = iter_cl->first; - Log(Logs::General, Logs::Commands, "command_init(): - Alias '%s' added to command '%s'.", iter_aka->c_str(), commandaliases[*iter_aka].c_str()); + Log(Logs::General, + Logs::Commands, + "command_init(): - Alias '%s' added to command '%s'.", + iter_aka->c_str(), + commandaliases[*iter_aka].c_str() + ); } } + 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 (injected_command_settings.size() || orphaned_command_settings.size()) { + database.UpdateCommandSettings(injected_command_settings, orphaned_command_settings); + } + command_dispatch = command_realdispatch; return commandcount;