From 15dde4778a1697dd178012120f61743f858bfd70 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 2 Apr 2020 11:52:27 -0400 Subject: [PATCH 01/28] Update aggro.cpp --- zone/aggro.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 87b98d599..dad9aacc4 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -237,6 +237,11 @@ bool Mob::CheckWillAggro(Mob *mob) { if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn() || mob->CastToClient()->bZoning) return false; } + + // We don't want to aggro clients outside of water if we're water only. + if (mob->IsClient() && mob->CastToClient()->GetLastRegion() != RegionTypeWater && IsUnderwaterOnly()) { + return false; + } /** * Pets shouldn't scan for aggro From 803c3aabe434ee22aacbffe035aeaf40d4d75871 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 2 Apr 2020 11:54:19 -0400 Subject: [PATCH 02/28] Update client.cpp --- zone/client.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zone/client.cpp b/zone/client.cpp index 8c4b42957..a270d6579 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8756,6 +8756,11 @@ void Client::CheckRegionTypeChanges() // still same region, do nothing if (last_region_type == new_region) return; + + // If we got out of water clear any water aggro for water only npcs + if (last_region_type == RegionTypeWater) { + entity_list.ClearWaterAggro(this); + } // region type changed last_region_type = new_region; From a898a1d07b9833fbe261f2cc9a1eafc48c107d62 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 2 Apr 2020 11:55:05 -0400 Subject: [PATCH 03/28] Update aggro.cpp --- zone/aggro.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index dad9aacc4..b568d11af 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -32,6 +32,7 @@ #endif #include "map.h" +#include "water_map.h" extern Zone* zone; //#define LOSDEBUG 6 From 42f959329d8190cd33070a5001869cc373771364 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 2 Apr 2020 11:56:06 -0400 Subject: [PATCH 04/28] Update client.h --- zone/client.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/client.h b/zone/client.h index 58b0d9af8..edc3f18c0 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1293,6 +1293,8 @@ public: void SendHPUpdateMarquee(); void CheckRegionTypeChanges(); + + WaterRegionType GetLastRegion() { return last_region_type; } int32 CalcATK(); From ef0b29dc8e43ce9f98f1385b907df87587dc38bb Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 2 Apr 2020 12:03:45 -0400 Subject: [PATCH 05/28] Update entity.cpp --- zone/entity.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/zone/entity.cpp b/zone/entity.cpp index b13fb5649..0e93be722 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3294,6 +3294,36 @@ void EntityList::ClearAggro(Mob* targ) } } +//removes "targ" from all hate lists of mobs that are water only. +//water only mobs that are fleed from to another region are healed as per live +void EntityList::ClearWaterAggro(Mob* targ) +{ + Client *c = nullptr; + if (targ->IsClient()) + c = targ->CastToClient(); + auto it = npc_list.begin(); + while (it != npc_list.end()) { + if (it->second->IsUnderwaterOnly()) { + if (it->second->CheckAggro(targ)) { + if (c) + c->RemoveXTarget(it->second, false); + it->second->RemoveFromHateList(targ); + } + if (c && it->second->IsOnFeignMemory(c)) { + it->second->RemoveFromFeignMemory(c); //just in case we feigned + c->RemoveXTarget(it->second, false); + } + if (!it->second->GetHateTop()) { + // target fled the water and no other targets. + // Heal NPC as on live + it->second->Heal(); + } + } + ++it; + } +} + + void EntityList::ClearFeignAggro(Mob *targ) { auto it = npc_list.begin(); From abeb93f1e60b802b46c41c98874eda0a16e97532 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 2 Apr 2020 12:04:16 -0400 Subject: [PATCH 06/28] Update entity.h --- zone/entity.h | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/entity.h b/zone/entity.h index 505f34963..3c182be29 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -447,6 +447,7 @@ public: void Process(); void ClearAggro(Mob* targ); + void ClearWaterAggro(Mob* targ); void ClearFeignAggro(Mob* targ); void ClearZoneFeignAggro(Client* targ); void AggroZone(Mob* who, uint32 hate = 0); From 561433902e28a039b3e7d604c4c1049f30d04530 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 2 Apr 2020 13:08:05 -0400 Subject: [PATCH 07/28] Removed heal per @mackal --- zone/entity.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 0e93be722..e74136be7 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3295,7 +3295,6 @@ void EntityList::ClearAggro(Mob* targ) } //removes "targ" from all hate lists of mobs that are water only. -//water only mobs that are fleed from to another region are healed as per live void EntityList::ClearWaterAggro(Mob* targ) { Client *c = nullptr; @@ -3313,11 +3312,6 @@ void EntityList::ClearWaterAggro(Mob* targ) it->second->RemoveFromFeignMemory(c); //just in case we feigned c->RemoveXTarget(it->second, false); } - if (!it->second->GetHateTop()) { - // target fled the water and no other targets. - // Heal NPC as on live - it->second->Heal(); - } } ++it; } From 03ca345b37250692112a8dff8e1113410665343a Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 Apr 2020 01:07:59 -0400 Subject: [PATCH 08/28] Add getcurrencyitemid(currency_id) to Perl/Lua. --- zone/embparser_api.cpp | 18 ++++++++++++++++++ zone/lua_general.cpp | 5 +++++ zone/questmgr.cpp | 13 +++++++++++++ zone/questmgr.h | 1 + 4 files changed, 37 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index dfb23ddc8..0bde28561 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3265,6 +3265,23 @@ XS(XS__getcharidbyname) { XSRETURN(1); } +XS(XS__getcurrencyitemid); +XS(XS__getcurrencyitemid) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getcurrencyitemid(int currency_id)"); + dXSTARG; + + int RETVAL; + int currency_id = (int) SvUV(ST(0)); + + RETVAL = quest_manager.getcurrencyitemid(currency_id); + + XSprePUSH; + PUSHi((IV)RETVAL); + XSRETURN(1); +} + XS(XS__getguildnamebyid); XS(XS__getguildnamebyid) { dXSARGS; @@ -4112,6 +4129,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file); newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file); newXS(strcpy(buf, "getcharnamebyid"), XS__getcharnamebyid, file); + newXS(strcpy(buf, "getcurrencyitemid"), XS__getcurrencyitemid, file); newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getguildidbycharid"), XS__getguildidbycharid, file); newXS(strcpy(buf, "getgroupidbycharid"), XS__getgroupidbycharid, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 848348fbf..cc320ab63 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -878,6 +878,10 @@ uint32 lua_get_char_id_by_name(const char* name) { return quest_manager.getcharidbyname(name); } +int lua_get_currency_item_id(int currency_id) { + return quest_manager.getcurrencyitemid(currency_id); +} + const char *lua_get_guild_name_by_id(uint32 guild_id) { return quest_manager.getguildnamebyid(guild_id); } @@ -1773,6 +1777,7 @@ luabind::scope lua_register_general() { luabind::def("delete_data", (bool(*)(std::string))&lua_delete_data), luabind::def("get_char_name_by_id", &lua_get_char_name_by_id), luabind::def("get_char_id_by_name", (uint32(*)(const char*))&lua_get_char_id_by_name), + luabind::def("get_currency_item_id", &lua_get_currency_item_id), luabind::def("get_guild_name_by_id", &lua_get_guild_name_by_id), luabind::def("get_guild_id_by_char_id", &lua_get_guild_id_by_char_id), luabind::def("get_group_id_by_char_id", &lua_get_group_id_by_char_id), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 0a61361c6..7560678e1 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2939,6 +2939,19 @@ uint32 QuestManager::getcharidbyname(const char* name) { return database.GetCharacterID(name); } +int QuestManager::getcurrencyitemid(int currency_id) { + if (currency_id > 0) { + auto iter = zone->AlternateCurrencies.begin(); + while (iter != zone->AlternateCurrencies.end()) { + if (currency_id == (*iter).id) { + return (*iter).item_id; + } + ++iter; + } + } + return 0; +} + const char* QuestManager::getguildnamebyid(int guild_id) { if (guild_id > 0) return guild_mgr.GetGuildName(guild_id); diff --git a/zone/questmgr.h b/zone/questmgr.h index a3c7eab3d..a4743b2ad 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -257,6 +257,7 @@ public: std::string saylink(char *saylink_text, bool silent, const char *link_name); const char* getcharnamebyid(uint32 char_id); uint32 getcharidbyname(const char* name); + int getcurrencyitemid(int currency_id); const char* getguildnamebyid(int guild_id); int getguildidbycharid(uint32 char_id); int getgroupidbycharid(uint32 char_id); From c2c6282cc77bbe693b0d38f212ac65c94268742d Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 Apr 2020 02:26:58 -0400 Subject: [PATCH 09/28] Add getclassname(class_id, level) to Perl/Lua. --- zone/embparser_api.cpp | 21 +++++++++++++++++++++ zone/lua_general.cpp | 10 ++++++++++ zone/questmgr.cpp | 4 ++++ zone/questmgr.h | 1 + 4 files changed, 36 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index dfb23ddc8..839928b89 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3265,6 +3265,26 @@ XS(XS__getcharidbyname) { XSRETURN(1); } +XS(XS__getclassname); +XS(XS__getclassname) { + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: quest::getclassname(uint8 class_id, [uint8 level = 0])"); + dXSTARG; + + std::string RETVAL; + uint8 class_id = (int) SvUV(ST(0)); + uint8 level = 0; + if (items > 1) + level = (int) SvUV(ST(1)); + + RETVAL = quest_manager.getclassname(class_id, level); + sv_setpv(TARG, RETVAL.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + XS(XS__getguildnamebyid); XS(XS__getguildnamebyid) { dXSARGS; @@ -4107,6 +4127,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "forcedoorclose"), XS__forcedoorclose, file); newXS(strcpy(buf, "forcedooropen"), XS__forcedooropen, file); newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file); + newXS(strcpy(buf, "getclassname"), XS__getclassname, file); newXS(strcpy(buf, "getinventoryslotid"), XS__getinventoryslotid, file); newXS(strcpy(buf, "getitemname"), XS__getitemname, file); newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 848348fbf..00dbb1993 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -878,6 +878,14 @@ uint32 lua_get_char_id_by_name(const char* name) { return quest_manager.getcharidbyname(name); } +std::string lua_get_class_name(uint8 class_id) { + return quest_manager.getclassname(class_id); +} + +std::string lua_get_class_name(uint8 class_id, uint8 level) { + return quest_manager.getclassname(class_id, level); +} + const char *lua_get_guild_name_by_id(uint32 guild_id) { return quest_manager.getguildnamebyid(guild_id); } @@ -1773,6 +1781,8 @@ luabind::scope lua_register_general() { luabind::def("delete_data", (bool(*)(std::string))&lua_delete_data), luabind::def("get_char_name_by_id", &lua_get_char_name_by_id), luabind::def("get_char_id_by_name", (uint32(*)(const char*))&lua_get_char_id_by_name), + luabind::def("get_class_name", (std::string(*)(uint8))&lua_get_class_name), + luabind::def("get_class_name", (std::string(*)(uint8,uint8))&lua_get_class_name), luabind::def("get_guild_name_by_id", &lua_get_guild_name_by_id), luabind::def("get_guild_id_by_char_id", &lua_get_guild_id_by_char_id), luabind::def("get_group_id_by_char_id", &lua_get_group_id_by_char_id), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 0a61361c6..1a00b7f93 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2939,6 +2939,10 @@ uint32 QuestManager::getcharidbyname(const char* name) { return database.GetCharacterID(name); } +std::string QuestManager::getclassname(uint8 class_id, uint8 level) { + return GetClassIDName(class_id, level); +} + const char* QuestManager::getguildnamebyid(int guild_id) { if (guild_id > 0) return guild_mgr.GetGuildName(guild_id); diff --git a/zone/questmgr.h b/zone/questmgr.h index a3c7eab3d..581ee4f0b 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -257,6 +257,7 @@ public: std::string saylink(char *saylink_text, bool silent, const char *link_name); const char* getcharnamebyid(uint32 char_id); uint32 getcharidbyname(const char* name); + std::string getclassname(uint8 class_id, uint8 level = 0); const char* getguildnamebyid(int guild_id); int getguildidbycharid(uint32 char_id); int getgroupidbycharid(uint32 char_id); From b5575133cd08bc0854e3c3d584b248dac773d9d0 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 6 Apr 2020 08:10:15 -0400 Subject: [PATCH 10/28] Put in braces in my new function as well as the source function. The entire file has implied braces... I'd change them all but fear making a mistake. --- zone/entity.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index e74136be7..b3805ad63 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3277,13 +3277,15 @@ void EntityList::Evade(Mob *who) void EntityList::ClearAggro(Mob* targ) { Client *c = nullptr; - if (targ->IsClient()) + if (targ->IsClient()) { c = targ->CastToClient(); + } auto it = npc_list.begin(); while (it != npc_list.end()) { if (it->second->CheckAggro(targ)) { - if (c) + if (c) { c->RemoveXTarget(it->second, false); + } it->second->RemoveFromHateList(targ); } if (c && it->second->IsOnFeignMemory(c)) { @@ -3298,14 +3300,16 @@ void EntityList::ClearAggro(Mob* targ) void EntityList::ClearWaterAggro(Mob* targ) { Client *c = nullptr; - if (targ->IsClient()) + if (targ->IsClient()) { c = targ->CastToClient(); + } auto it = npc_list.begin(); while (it != npc_list.end()) { if (it->second->IsUnderwaterOnly()) { if (it->second->CheckAggro(targ)) { - if (c) + if (c) { c->RemoveXTarget(it->second, false); + } it->second->RemoveFromHateList(targ); } if (c && it->second->IsOnFeignMemory(c)) { From 973fd376e58eae48c8c2532038ad91b0f081906a Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 6 Apr 2020 08:23:57 -0400 Subject: [PATCH 11/28] Propose change in location of Stun check as possible resolution to crash --- zone/npc.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index b7dc3272a..39b1b046b 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -694,11 +694,6 @@ void NPC::RemoveCash() { bool NPC::Process() { - if (IsStunned() && stunned_timer.Check()) { - Mob::UnStun(); - this->spun_timer.Disable(); - } - if (p_depop) { Mob* owner = entity_list.GetMob(this->ownerid); @@ -711,6 +706,11 @@ bool NPC::Process() } return false; } + + if (IsStunned() && stunned_timer.Check()) { + Mob::UnStun(); + this->spun_timer.Disable(); + } SpellProcess(); From 40ef4c799b61e3b608823982b7076fce7f9efed1 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 Apr 2020 16:22:08 -0400 Subject: [PATCH 12/28] Update embparser_api.cpp --- zone/embparser_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 4daebd009..bad973bfe 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3327,7 +3327,7 @@ XS(XS__getcurrencyitemid) { XSprePUSH; PUSHi((IV)RETVAL); - XSRETURN(1); + XSRETURN(1); } XS(XS__getcurrencyid); From f8735cf9ba19b4d90a04384886fd1c35a7381525 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 Apr 2020 18:06:18 -0400 Subject: [PATCH 13/28] Update lua_general.cpp --- zone/lua_general.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index a45bffb90..d047238de 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -895,7 +895,7 @@ std::string lua_get_class_name(uint8 class_id, uint8 level) { } int lua_get_currency_id(uint32 item_id) { - return quest_manager.getcurrencyid(item_id) + return quest_manager.getcurrencyid(item_id); } int lua_get_currency_item_id(int currency_id) { From cbd1f42a085220b2b729d69a7f9f9212cda89e48 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 Apr 2020 18:57:13 -0400 Subject: [PATCH 14/28] Merge conflict fixes... --- zone/embparser_api.cpp | 2 +- zone/questmgr.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index e3e5a5664..c65cd204c 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3358,7 +3358,7 @@ XS(XS__getcurrencyid) { dXSTARG; int RETVAL; - uint32 item_id = (int) SvUV(ST(0));; + uint32 item_id = (int) SvUV(ST(0)); RETVAL = quest_manager.getcurrencyid(item_id); XSprePUSH; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 52ef22621..ba6a5340c 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2986,6 +2986,7 @@ int QuestManager::getcurrencyitemid(int currency_id) { } ++iter; } + } return 0; } From 1728923bbb6a1af1f1c8104983225f73226a0c7e Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 8 Apr 2020 16:00:27 -0500 Subject: [PATCH 15/28] Revert "Merge pull request #1007 from EQEmu/feature/improved-name-generator" This reverts commit 3c71e2c91df68421ab32d7f14094f067014cb70b, reversing changes made to 80d160175450a7e74500f5c773f9e1d72ecb907d. --- common/CMakeLists.txt | 2 - common/namegenerator/namegen.cpp | 522 ------------------------------- common/namegenerator/namegen.h | 269 ---------------- world/client.cpp | 81 ++++- 4 files changed, 73 insertions(+), 801 deletions(-) delete mode 100644 common/namegenerator/namegen.cpp delete mode 100644 common/namegenerator/namegen.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 57f000ccb..e312c202d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -77,7 +77,6 @@ SET(common_sources unix.cpp platform.cpp json/jsoncpp.cpp - namegenerator/namegen.cpp net/console_server.cpp net/console_server_connection.cpp net/crc32.cpp @@ -222,7 +221,6 @@ SET(common_headers event/timer.h json/json.h json/json-forwards.h - namegenerator/namegen.h net/console_server.h net/console_server_connection.h net/crc32.h diff --git a/common/namegenerator/namegen.cpp b/common/namegenerator/namegen.cpp deleted file mode 100644 index 3ef880ea4..000000000 --- a/common/namegenerator/namegen.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/** - * - * @file A fantasy name generator library. - * @version 1.0.1 - * @license Public Domain - * @author German M. Bravo (Kronuz) - * - */ - -#include "namegen.h" - -#include // for move, reverse -#include // for rng seed -#include // for size_t, mbsrtowcs, wcsrtombs -#include // for towupper -#include // for make_unique -#include // for mt19937, uniform_real_distribution -#include // for invalid_argument, out_of_range - - -using namespace NameGen; - - -static std::mt19937 rng(std::chrono::high_resolution_clock::now().time_since_epoch().count()); - - -// https://isocpp.org/wiki/faq/ctors#static-init-order -// Avoid the "static initialization order fiasco" -const std::unordered_map>& Generator::SymbolMap() -{ - static auto* const symbols = new std::unordered_map>({ - { - "s", { - "ach", "ack", "ad", "age", "ald", "ale", "an", "ang", "ar", "ard", - "as", "ash", "at", "ath", "augh", "aw", "ban", "bel", "bur", "cer", - "cha", "che", "dan", "dar", "del", "den", "dra", "dyn", "ech", "eld", - "elm", "em", "en", "end", "eng", "enth", "er", "ess", "est", "et", - "gar", "gha", "hat", "hin", "hon", "ia", "ight", "ild", "im", "ina", - "ine", "ing", "ir", "is", "iss", "it", "kal", "kel", "kim", "kin", - "ler", "lor", "lye", "mor", "mos", "nal", "ny", "nys", "old", "om", - "on", "or", "orm", "os", "ough", "per", "pol", "qua", "que", "rad", - "rak", "ran", "ray", "ril", "ris", "rod", "roth", "ryn", "sam", - "say", "ser", "shy", "skel", "sul", "tai", "tan", "tas", "ther", - "tia", "tin", "ton", "tor", "tur", "um", "und", "unt", "urn", "usk", - "ust", "ver", "ves", "vor", "war", "wor", "yer" - } - }, - { - "v", { - "a", "e", "i", "o", "u", "y" - } - }, - { - "V", { - "a", "e", "i", "o", "u", "y", "ae", "ai", "au", "ay", "ea", "ee", - "ei", "eu", "ey", "ia", "ie", "oe", "oi", "oo", "ou", "ui" - } - }, - { - "c", { - "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", - "s", "t", "v", "w", "x", "y", "z" - } - }, - { - "B", { - "b", "bl", "br", "c", "ch", "chr", "cl", "cr", "d", "dr", "f", "g", - "h", "j", "k", "l", "ll", "m", "n", "p", "ph", "qu", "r", "rh", "s", - "sch", "sh", "sl", "sm", "sn", "st", "str", "sw", "t", "th", "thr", - "tr", "v", "w", "wh", "y", "z", "zh" - } - }, - { - "C", { - "b", "c", "ch", "ck", "d", "f", "g", "gh", "h", "k", "l", "ld", "ll", - "lt", "m", "n", "nd", "nn", "nt", "p", "ph", "q", "r", "rd", "rr", - "rt", "s", "sh", "ss", "st", "t", "th", "v", "w", "y", "z" - } - }, - { - "i", { - "air", "ankle", "ball", "beef", "bone", "bum", "bumble", "bump", - "cheese", "clod", "clot", "clown", "corn", "dip", "dolt", "doof", - "dork", "dumb", "face", "finger", "foot", "fumble", "goof", - "grumble", "head", "knock", "knocker", "knuckle", "loaf", "lump", - "lunk", "meat", "muck", "munch", "nit", "numb", "pin", "puff", - "skull", "snark", "sneeze", "thimble", "twerp", "twit", "wad", - "wimp", "wipe" - } - }, - { - "m", { - "baby", "booble", "bunker", "cuddle", "cuddly", "cutie", "doodle", - "foofie", "gooble", "honey", "kissie", "lover", "lovey", "moofie", - "mooglie", "moopie", "moopsie", "nookum", "poochie", "poof", - "poofie", "pookie", "schmoopie", "schnoogle", "schnookie", - "schnookum", "smooch", "smoochie", "smoosh", "snoogle", "snoogy", - "snookie", "snookum", "snuggy", "sweetie", "woogle", "woogy", - "wookie", "wookum", "wuddle", "wuddly", "wuggy", "wunny" - } - }, - { - "M", { - "boo", "bunch", "bunny", "cake", "cakes", "cute", "darling", - "dumpling", "dumplings", "face", "foof", "goo", "head", "kin", - "kins", "lips", "love", "mush", "pie", "poo", "pooh", "pook", "pums" - } - }, - { - "D", { - "b", "bl", "br", "cl", "d", "f", "fl", "fr", "g", "gh", "gl", "gr", - "h", "j", "k", "kl", "m", "n", "p", "th", "w" - } - }, - { - "d", { - "elch", "idiot", "ob", "og", "ok", "olph", "olt", "omph", "ong", - "onk", "oo", "oob", "oof", "oog", "ook", "ooz", "org", "ork", "orm", - "oron", "ub", "uck", "ug", "ulf", "ult", "um", "umb", "ump", "umph", - "un", "unb", "ung", "unk", "unph", "unt", "uzz" - } - } - }); - - return *symbols; -} - -Generator::Generator() -{ -} - - -Generator::Generator(std::vector>&& generators_) : - generators(std::move(generators_)) -{ -} - - -size_t Generator::combinations() -{ - size_t total = 1; - for (auto& g : generators) { - total *= g->combinations(); - } - return total; -} - - -size_t Generator::min() -{ - size_t final = 0; - for (auto& g : generators) { - final += g->min(); - } - return final; -} - - -size_t Generator::max() -{ - size_t final = 0; - for (auto& g : generators) { - final += g->max(); - } - return final; -} - - -std::string Generator::toString() { - std::string str; - for (auto& g : generators) { - str.append(g->toString()); - } - return str; -} - - -void Generator::add(std::unique_ptr&& g) -{ - generators.push_back(std::move(g)); -} - - -Random::Random() -{ -} - -Random::Random(std::vector>&& generators_) : - Generator(std::move(generators_)) -{ -} - -size_t Random::combinations() -{ - size_t total = 0; - for (auto& g : generators) { - total += g->combinations(); - } - return total ? total : 1; -} - -size_t Random::min() -{ - size_t final = -1; - for (auto& g : generators) { - size_t current = g->min(); - if (current < final) { - final = current; - } - } - return final; -} - -size_t Random::max() -{ - size_t final = 0; - for (auto& g : generators) { - size_t current = g->max(); - if (current > final) { - final = current; - } - } - return final; -} - - -std::string Random::toString() -{ - if (!generators.size()) { - return ""; - } - std::uniform_real_distribution distribution(0, generators.size() - 1); - int rnd = distribution(rng) + 0.5; - return generators[rnd]->toString(); -} - - -Sequence::Sequence() -{ -} - -Sequence::Sequence(std::vector>&& generators_) : - Generator(std::move(generators_)) -{ -} - -Literal::Literal(const std::string &value_) : - value(value_) -{ -} - -size_t Literal::combinations() -{ - return 1; -} - -size_t Literal::min() -{ - return value.size(); -} - -size_t Literal::max() -{ - return value.size(); -} - -std::string Literal::toString() -{ - return value; -} - -Reverser::Reverser(std::unique_ptr&& g) -{ - add(std::move(g)); -} - - -std::string Reverser::toString() -{ - std::wstring str = towstring(Generator::toString()); - std::reverse(str.begin(), str.end()); - return tostring(str); -} - -Capitalizer::Capitalizer(std::unique_ptr&& g) -{ - add(std::move(g)); -} - -std::string Capitalizer::toString() -{ - std::wstring str = towstring(Generator::toString()); - str[0] = std::towupper(str[0]); - return tostring(str); -} - - -Collapser::Collapser(std::unique_ptr&& g) -{ - add(std::move(g)); -} - -std::string Collapser::toString() -{ - std::wstring str = towstring(Generator::toString()); - std::wstring out; - int cnt = 0; - wchar_t pch = L'\0'; - for (auto ch : str) { - if (ch == pch) { - cnt++; - } else { - cnt = 0; - } - int mch = 2; - switch(ch) { - case 'a': - case 'h': - case 'i': - case 'j': - case 'q': - case 'u': - case 'v': - case 'w': - case 'x': - case 'y': - mch = 1; - } - if (cnt < mch) { - out.push_back(ch); - } - pch = ch; - } - return tostring(out); -} - - -Generator::Generator(const std::string &pattern, bool collapse_triples) { - std::unique_ptr last; - - std::stack> stack; - std::unique_ptr top = std::unique_ptr(); - - for (auto c : pattern) { - switch (c) { - case '<': - stack.push(std::move(top)); - top = std::unique_ptr(); - break; - case '(': - stack.push(std::move(top)); - top = std::unique_ptr(); - break; - case '>': - case ')': - if (stack.size() == 0) { - throw std::invalid_argument("Unbalanced brackets"); - } else if (c == '>' && top->type != group_types::symbol) { - throw std::invalid_argument("Unexpected '>' in pattern"); - } else if (c == ')' && top->type != group_types::literal) { - throw std::invalid_argument("Unexpected ')' in pattern"); - } - last = top->produce(); - top = std::move(stack.top()); - stack.pop(); - top->add(std::move(last)); - break; - case '|': - top->split(); - break; - case '!': - if (top->type == group_types::symbol) { - top->wrap(wrappers::capitalizer); - } else { - top->add(c); - } - break; - case '~': - if (top->type == group_types::symbol) { - top->wrap(wrappers::reverser); - } else { - top->add(c); - } - break; - default: - top->add(c); - break; - } - } - - if (stack.size() != 0) { - throw std::invalid_argument("Missing closing bracket"); - } - - std::unique_ptr g = top->produce(); - if (collapse_triples) { - g = std::unique_ptr(new Collapser(std::move(g))); - } - add(std::move(g)); -} - - -Generator::Group::Group(group_types_t type_) : - type(type_) -{ -} - -void Generator::Group::add(std::unique_ptr&& g) -{ - while (!wrappers.empty()) { - switch (wrappers.top()) { - case reverser: - g = std::unique_ptr(new Reverser(std::move(g))); - break; - case capitalizer: - g = std::unique_ptr(new Capitalizer(std::move(g))); - break; - } - wrappers.pop(); - } - if (set.size() == 0) { - set.push_back(std::unique_ptr()); - } - set.back()->add(std::move(g)); -} - -void Generator::Group::add(char c) -{ - std::string value(1, c); - std::unique_ptr g = std::unique_ptr(); - g->add(std::unique_ptr(new Literal(value))); - Group::add(std::move(g)); -} - -std::unique_ptr Generator::Group::produce() -{ - switch (set.size()) { - case 0: - return std::unique_ptr(new Literal("")); - case 1: - return std::move(*set.begin()); - default: - return std::unique_ptr(new Random(std::move(set))); - } -} - -void Generator::Group::split() -{ - if (set.size() == 0) { - set.push_back(std::unique_ptr()); - } - set.push_back(std::unique_ptr()); -} - -void Generator::Group::wrap(wrappers_t type) -{ - wrappers.push(type); -} - -Generator::GroupSymbol::GroupSymbol() : - Group(group_types::symbol) -{ -} - -void Generator::GroupSymbol::add(char c) -{ - std::string value(1, c); - std::unique_ptr g = std::unique_ptr(); - try { - static const auto& symbols = SymbolMap(); - for (const auto& s : symbols.at(value)) { - g->add(std::unique_ptr(new Literal(s))); - } - } catch (const std::out_of_range&) { - g->add(std::unique_ptr(new Literal(value))); - } - Group::add(std::move(g)); -} - -Generator::GroupLiteral::GroupLiteral() : - Group(group_types::literal) -{ -} - -std::wstring towstring(const std::string & s) -{ - const char *cs = s.c_str(); - const size_t wn = std::mbsrtowcs(nullptr, &cs, 0, nullptr); - - if (wn == static_cast(-1)) { - return L""; - } - - std::vector buf(wn); - cs = s.c_str(); - const size_t wn_again = std::mbsrtowcs(buf.data(), &cs, wn, nullptr); - - if (wn_again == static_cast(-1)) { - return L""; - } - - return std::wstring(buf.data(), wn); -} - -std::string tostring(const std::wstring & s) -{ - const wchar_t *cs = s.c_str(); - const size_t wn = std::wcsrtombs(nullptr, &cs, 0, nullptr); - - if (wn == static_cast(-1)) { - return ""; - } - - std::vector buf(wn); - const size_t wn_again = std::wcsrtombs(buf.data(), &cs, wn, nullptr); - - if (wn_again == static_cast(-1)) { - return ""; - } - - return std::string(buf.data(), wn); -} diff --git a/common/namegenerator/namegen.h b/common/namegenerator/namegen.h deleted file mode 100644 index 4ddcf1bf5..000000000 --- a/common/namegenerator/namegen.h +++ /dev/null @@ -1,269 +0,0 @@ -/** - * - * @file A fantasy name generator library. - * @version 1.0.1 - * @license Public Domain - * @author German M. Bravo (Kronuz) - * - * This library is designed after the RinkWorks Fantasy Name Generator. - * @see http://www.rinkworks.com/namegen/ - * - * @example - * NameGen::Generator generator("sV'i"); - * generator.toString(); // Returns a new name each call with produce() - * // => "entheu'loaf" - * - * ## Pattern Syntax - * - * The compile() function creates a name generator based on an input - * pattern. The letters s, v, V, c, B, C, i, m, M, D, and d represent - * different types of random replacements. Everything else is produced - * literally. - * - * s - generic syllable - * v - vowel - * V - vowel or vowel combination - * c - consonant - * B - consonant or consonant combination suitable for beginning a word - * C - consonant or consonant combination suitable anywhere in a word - * i - insult - * m - mushy name - * M - mushy name ending - * D - consonant suited for a stupid person's name - * d - syllable suited for a stupid person's name (begins with a vowel) - * - * All characters between parenthesis () are produced literally. For - * example, the pattern "s(dim)", produces a random generic syllable - * followed by "dim". - * - * Characters between angle brackets <> produce patterns from the table - * above. Imagine the entire pattern is wrapped in one of these. - * - * In both types of groupings, a vertical bar | denotes a random - * choice. Empty groups are allowed. For example, "(foo|bar)" produces - * either "foo" or "bar". The pattern "" produces a constant, - * vowel, or nothing at all. - * - * An exclamation point ! means to capitalize the component that - * follows it. For example, "!(foo)" will produce "Foo" and "v!s" will - * produce a lowercase vowel followed by a capitalized syllable, like - * "eRod". - * - * A tilde ~ means to reverse the letters of the component that - * follows it. For example, "~(foo)" will produce "oof". To reverse an - * entire template, wrap it in brackets. For example, to reverse - * "sV'i" as a whole use "~". The template "~sV'i" will only - * reverse the initial syllable. - * - * ## Internals - * - * A name generator is anything with a toString() method, including, - * importantly, strings themselves. The generator constructors - * (Random, Sequence) perform additional optimizations when *not* used - * with the `new` keyword: they may pass through a provided generator, - * combine provided generators, or even return a simple string. - * - * New pattern symbols added to Generator.symbols will automatically - * be used by the compiler. - */ - -#pragma once - -#include // for size_t -#include // for wstring -#include // for unique_ptr -#include // for stack -#include // for string -#include // for unordered_map -#include // for vector - - -namespace NameGen { - -// Middle Earth -#define MIDDLE_EARTH "(bil|bal|ban|hil|ham|hal|hol|hob|wil|me|or|ol|od|gor|for|fos|tol|ar|fin|ere|leo|vi|bi|bren|thor)(|go|orbis|apol|adur|mos|ri|i|na|ole|n)(|tur|axia|and|bo|gil|bin|bras|las|mac|grim|wise|l|lo|fo|co|ra|via|da|ne|ta|y|wen|thiel|phin|dir|dor|tor|rod|on|rdo|dis)" - -// Japanese Names (Constrained) -#define JAPANESE_NAMES_CONSTRAINED "(aka|aki|bashi|gawa|kawa|furu|fuku|fuji|hana|hara|haru|hashi|hira|hon|hoshi|ichi|iwa|kami|kawa|ki|kita|kuchi|kuro|marui|matsu|miya|mori|moto|mura|nabe|naka|nishi|no|da|ta|o|oo|oka|saka|saki|sawa|shita|shima|i|suzu|taka|take|to|toku|toyo|ue|wa|wara|wata|yama|yoshi|kei|ko|zawa|zen|sen|ao|gin|kin|ken|shiro|zaki|yuki|asa)(||||||||||bashi|gawa|kawa|furu|fuku|fuji|hana|hara|haru|hashi|hira|hon|hoshi|chi|wa|ka|kami|kawa|ki|kita|kuchi|kuro|marui|matsu|miya|mori|moto|mura|nabe|naka|nishi|no|da|ta|o|oo|oka|saka|saki|sawa|shita|shima|suzu|taka|take|to|toku|toyo|ue|wa|wara|wata|yama|yoshi|kei|ko|zawa|zen|sen|ao|gin|kin|ken|shiro|zaki|yuki|sa)" - -// Japanese Names (Diverse) -#define JAPANESE_NAMES_DIVERSE "(a|i|u|e|o|||||)(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)))(|||n)" - -// Chinese Names -#define CHINESE_NAMES "(zh|x|q|sh|h)(ao|ian|uo|ou|ia)(|(l|w|c|p|b|m)(ao|ian|uo|ou|ia)(|n)|-(l|w|c|p|b|m)(ao|ian|uo|ou|ia)(|(d|j|q|l)(a|ai|iu|ao|i)))" - -// Greek Names -#define GREEK_NAMES "(tia)|s(os)|Bc(ios)|Bv(ios|os)>" - -// Hawaiian Names (1) -#define HAWAIIAN_NAMES_1 "((h|k|l|m|n|p|w|')|)(a|e|i|o|u)((h|k|l|m|n|p|w|')|)(a|e|i|o|u)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)" - -// Hawaiian Names (2) -#define HAWAIIAN_NAMES_2 "((h|k|l|m|n|p|w|)(a|e|i|o|u|a'|e'|i'|o'|u'|ae|ai|ao|au|oi|ou|eu|ei)(k|l|m|n|p|)|)(h|k|l|m|n|p|w|)(a|e|i|o|u|a'|e'|i'|o'|u'|ae|ai|ao|au|oi|ou|eu|ei)(k|l|m|n|p|)" - -// Old Latin Place Names -#define OLD_LATIN_PLACE_NAMES "sv(nia|lia|cia|sia)" - -// Dragons (Pern) -#define DRAGONS_PERN "<|>>(th)" - -// Dragon Riders -#define DRAGON_RIDERS "c'" - -// Pokemon -#define POKEMON "v(mon|chu|zard|rtle)" - -// Fantasy (Vowels, R, etc.) -#define FANTASY_VOWELS_R "(|(|s|h|ty|ph|r))(i|ae|ya|ae|eu|ia|i|eo|ai|a)(lo|la|sri|da|dai|the|sty|lae|due|li|lly|ri|na|ral|sur|rith)(|(su|nu|sti|llo|ria|))(|(n|ra|p|m|lis|cal|deu|dil|suir|phos|ru|dru|rin|raap|rgue))" - -// Fantasy (S, A, etc.) -#define FANTASY_S_A "(cham|chan|jisk|lis|frich|isk|lass|mind|sond|sund|ass|chad|lirt|und|mar|lis|il|)(jask|ast|ista|adar|irra|im|ossa|assa|osia|ilsa|)(|(an|ya|la|sta|sda|sya|st|nya))" - -// Fantasy (H, L, etc.) -#define FANTASY_H_L "(ch|ch't|sh|cal|val|ell|har|shar|shal|rel|laen|ral|jh't|alr|ch|ch't|av)(|(is|al|ow|ish|ul|el|ar|iel))(aren|aeish|aith|even|adur|ulash|alith|atar|aia|erin|aera|ael|ira|iel|ahur|ishul)" - -// Fantasy (N, L, etc.) -#define FANTASY_N_L "(ethr|qil|mal|er|eal|far|fil|fir|ing|ind|il|lam|quel|quar|quan|qar|pal|mal|yar|um|ard|enn|ey)(|(|on|us|un|ar|as|en|ir|ur|at|ol|al|an))(uard|wen|arn|on|il|ie|on|iel|rion|rian|an|ista|rion|rian|cil|mol|yon)" - -// Fantasy (K, N, etc.) -#define FANTASY_K_N "(taith|kach|chak|kank|kjar|rak|kan|kaj|tach|rskal|kjol|jok|jor|jad|kot|kon|knir|kror|kol|tul|rhaok|rhak|krol|jan|kag|ryr)(|in|or|an|ar|och|un|mar|yk|ja|arn|ir|ros|ror)(|(mund|ard|arn|karr|chim|kos|rir|arl|kni|var|an|in|ir|a|i|as))" - -// Fantasy (J, G, Z, etc.) -#define FANTASY_J_G_Z "(aj|ch|etz|etzl|tz|kal|gahn|kab|aj|izl|ts|jaj|lan|kach|chaj|qaq|jol|ix|az|biq|nam)(|(|aw|al|yes|il|ay|en|tom||oj|im|ol|aj|an|as))(aj|am|al|aqa|ende|elja|ich|ak|ix|in|ak|al|il|ek|ij|os|al|im)" - -// Fantasy (K, J, Y, etc.) -#define FANTASY_K_J_Y "(yi|shu|a|be|na|chi|cha|cho|ksa|yi|shu)(th|dd|jj|sh|rr|mk|n|rk|y|jj|th)(us|ash|eni|akra|nai|ral|ect|are|el|urru|aja|al|uz|ict|arja|ichi|ural|iru|aki|esh)" - -// Fantasy (S, E, etc.) -#define FANTASY_S_E "(syth|sith|srr|sen|yth|ssen|then|fen|ssth|kel|syn|est|bess|inth|nen|tin|cor|sv|iss|ith|sen|slar|ssil|sthen|svis|s|ss|s|ss)(|(tys|eus|yn|of|es|en|ath|elth|al|ell|ka|ith|yrrl|is|isl|yr|ast|iy))(us|yn|en|ens|ra|rg|le|en|ith|ast|zon|in|yn|ys)" - - - class Generator - { - typedef enum wrappers { - capitalizer, - reverser - } wrappers_t; - - typedef enum group_types { - symbol, - literal - } group_types_t; - - - class Group { - std::stack wrappers; - std::vector> set; - - public: - group_types_t type; - - Group(group_types_t type_); - virtual ~Group() { } - - std::unique_ptr produce(); - void split(); - void wrap(wrappers_t type); - void add(std::unique_ptr&& g); - - virtual void add(char c); - }; - - - class GroupSymbol : public Group { - public: - GroupSymbol(); - void add(char c); - }; - - - class GroupLiteral : public Group { - public: - GroupLiteral(); - }; - - protected: - std::vector> generators; - - public: - static const std::unordered_map>& SymbolMap(); - - Generator(); - Generator(const std::string& pattern, bool collapse_triples=true); - Generator(std::vector>&& generators_); - - virtual ~Generator() = default; - - virtual size_t combinations(); - virtual size_t min(); - virtual size_t max(); - virtual std::string toString(); - - void add(std::unique_ptr&& g); - }; - - - class Random : public Generator - { - public: - Random(); - Random(std::vector>&& generators_); - - size_t combinations(); - size_t min(); - size_t max(); - std::string toString(); - }; - - - class Sequence : public Generator - { - public: - Sequence(); - Sequence(std::vector>&& generators_); - }; - - - class Literal : public Generator - { - std::string value; - - public: - Literal(const std::string& value_); - - size_t combinations(); - size_t min(); - size_t max(); - std::string toString(); - }; - - - class Reverser : public Generator { - public: - Reverser(std::unique_ptr&& g); - - std::string toString(); - }; - - - class Capitalizer : public Generator - { - public: - Capitalizer(std::unique_ptr&& g); - - std::string toString(); - }; - - - class Collapser : public Generator - { - public: - Collapser(std::unique_ptr&& g); - - std::string toString(); - }; - -} - -std::wstring towstring(const std::string& s); -std::string tostring(const std::wstring& s); \ No newline at end of file diff --git a/world/client.cpp b/world/client.cpp index c4d1c6c70..2d4b65e71 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -46,7 +46,6 @@ #include "clientlist.h" #include "wguild_mgr.h" #include "sof_char_create_data.h" -#include "../common/namegenerator/namegen.h" #include #include @@ -534,14 +533,80 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) return true; } -bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) -{ - NameGen::Generator generator("!Bvss"); - std::string random_name = generator.toString(); +bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) { + // creates up to a 10 char name + char vowels[18]="aeiouyaeiouaeioe"; + char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd"; + char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; + int rndnum=emu_random.Int(0, 75),n=1; + bool dlc=false; + bool vwl=false; + bool dbl=false; + if (rndnum>63) + { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel + rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" + rndname[0]=paircons[rndnum]; + rndname[1]=paircons[rndnum+1]; + n=2; + } + else if (rndnum>16) + { + rndnum-=17; + rndname[0]=cons[rndnum]; + } + else + { + rndname[0]=vowels[rndnum]; + vwl=true; + } + int namlen=emu_random.Int(5, 10); + for (int i=n;i46) + { // pick a cons pair + if (i>namlen-3) // last 2 chars in name? + { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" + rndnum=emu_random.Int(0, 7)*2; + } + else + { // pick any from the set + rndnum=(rndnum-47)*2; + } + rndname[i]=paircons[rndnum]; + rndname[i+1]=paircons[rndnum+1]; + dlc=true; // flag keeps second letter from being doubled below + i+=1; + } + else + { // select a single cons + rndname[i]=cons[rndnum]; + } + } + else + { // select a vowel + rndname[i]=vowels[emu_random.Int(0, 16)]; + } + vwl=!vwl; + if (!dbl && !dlc) + { // one chance at double letters in name + if (!emu_random.Int(0, i+9)) // chances decrease towards end of name + { + rndname[i+1]=rndname[i]; + dbl=true; + i+=1; + } + } + } - auto *ngs = (NameGeneration_Struct *) app->pBuffer; - memset(ngs->name, 0, 64); - strcpy(ngs->name, random_name.c_str()); + rndname[0]=toupper(rndname[0]); + NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; + memset(ngs->name,0,64); + strcpy(ngs->name,rndname); QueuePacket(app); return true; From 88ff56b2f2df92678fe04f1381b7d561a03cec62 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Apr 2020 01:43:00 -0500 Subject: [PATCH 16/28] Add client->SendToGuildHall - have instances properly cycle out IDs --- common/database.h | 10 +- common/database_instances.cpp | 137 ++++++++++++++-------- common/ruletypes.h | 7 +- common/timer.cpp | 12 +- zone/client.cpp | 44 ++++++- zone/client.h | 11 +- zone/client_packet.cpp | 215 ++++++++++++++++++---------------- zone/lua_client.cpp | 6 + zone/lua_client.h | 1 + zone/perl_client.cpp | 26 +++- zone/zone.cpp | 24 +++- zone/zone.h | 4 + 12 files changed, 323 insertions(+), 174 deletions(-) diff --git a/common/database.h b/common/database.h index cf85acf9c..96897e4ea 100644 --- a/common/database.h +++ b/common/database.h @@ -159,7 +159,7 @@ public: uint16 GetInstanceID(const char* zone, uint32 charid, int16 version); uint16 GetInstanceID(uint32 zone, uint32 charid, int16 version); uint16 GetInstanceVersion(uint16 instance_id); - uint32 GetTimeRemainingInstance(uint16 instance_id, bool &is_perma); + uint32 GetTimeRemainingInstance(uint16 instance_id, bool is_perma = false); uint32 VersionFromInstanceID(uint16 instance_id); uint32 ZoneIDFromInstanceID(uint16 instance_id); @@ -196,19 +196,19 @@ public: void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus); void SetAgreementFlag(uint32 acctid); - + int GetIPExemption(std::string account_ip); int GetInstanceID(uint32 char_id, uint32 zone_id); /* Groups */ - + char* GetGroupLeaderForLogin(const char* name,char* leaderbuf); char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr); - + uint32 GetGroupID(const char* name); - + void ClearGroup(uint32 gid = 0); void ClearGroupLeader(uint32 gid = 0); void SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc = false); diff --git a/common/database_instances.cpp b/common/database_instances.cpp index 548a84bdb..3359dc7ce 100644 --- a/common/database_instances.cpp +++ b/common/database_instances.cpp @@ -97,42 +97,53 @@ bool Database::CheckInstanceExists(uint16 instance_id) { bool Database::CheckInstanceExpired(uint16 instance_id) { - int32 start_time = 0; - int32 duration = 0; + int32 start_time = 0; + int32 duration = 0; uint32 never_expires = 0; - std::string query = StringFormat("SELECT start_time, duration, never_expires FROM instance_list WHERE id=%u", instance_id); + std::string query = StringFormat( + "SELECT start_time, duration, never_expires FROM instance_list WHERE id=%u", + instance_id + ); + auto results = QueryDatabase(query); - if (!results.Success()) + if (!results.Success()) { return true; + } - if (results.RowCount() == 0) + if (results.RowCount() == 0) { return true; + } auto row = results.begin(); - start_time = atoi(row[0]); - duration = atoi(row[1]); + start_time = atoi(row[0]); + duration = atoi(row[1]); never_expires = atoi(row[2]); - if (never_expires == 1) + if (never_expires == 1) { return false; + } - timeval tv; + timeval tv{}; gettimeofday(&tv, nullptr); - if ((start_time + duration) <= tv.tv_sec) - return true; + return (start_time + duration) <= tv.tv_sec; - return false; } bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration) { - std::string query = StringFormat("INSERT INTO instance_list (id, zone, version, start_time, duration)" - " values(%lu, %lu, %lu, UNIX_TIMESTAMP(), %lu)", - (unsigned long)instance_id, (unsigned long)zone_id, (unsigned long)version, (unsigned long)duration); + std::string query = StringFormat( + "INSERT INTO instance_list (id, zone, version, start_time, duration)" + " values (%u, %u, %u, UNIX_TIMESTAMP(), %u)", + instance_id, + zone_id, + version, + duration + ); + auto results = QueryDatabase(query); return results.Success(); @@ -140,66 +151,79 @@ bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version bool Database::GetUnusedInstanceID(uint16 &instance_id) { - uint32 count = RuleI(Zone, ReservedInstances); - uint32 max = 65535; + uint32 max_reserved_instance_id = RuleI(Instances, ReservedInstances); + uint32 max = 32000; + + std::string query = StringFormat( + "SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u", + max_reserved_instance_id, + max_reserved_instance_id + ); + + if (RuleB(Instances, RecycleInstanceIds)) { + query = ( + SQL( + SELECT i.id + 1 AS next_available + FROM instance_list i + LEFT JOIN instance_list i2 ON i2.id = i.id + 1 + WHERE i2.id IS NULL + ORDER BY i.id + LIMIT 0, 1; + + ) + ); + } - std::string query = StringFormat("SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u", count, count); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { instance_id = 0; return false; } - if (results.RowCount() == 0) - { - instance_id = 0; - return false; + if (results.RowCount() == 0) { + instance_id = max_reserved_instance_id; + return true; } auto row = results.begin(); - if (atoi(row[0]) <= max) - { + if (atoi(row[0]) <= max) { instance_id = atoi(row[0]); + return true; } - query = StringFormat("SELECT id FROM instance_list where id > %u ORDER BY id", count); + query = StringFormat("SELECT id FROM instance_list where id > %u ORDER BY id", max_reserved_instance_id); results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { instance_id = 0; return false; } - if (results.RowCount() == 0) - { + if (results.RowCount() == 0) { instance_id = 0; return false; } - count++; - for (auto row = results.begin(); row != results.end(); ++row) - { - if (count < atoi(row[0])) - { - instance_id = count; + max_reserved_instance_id++; + for (auto row = results.begin(); row != results.end(); ++row) { + if (max_reserved_instance_id < atoi(row[0])) { + instance_id = max_reserved_instance_id; return true; } - if (count > max) - { + if (max_reserved_instance_id > max) { instance_id = 0; return false; } - count++; + max_reserved_instance_id++; } - instance_id = count; + instance_id = max_reserved_instance_id; + return true; } @@ -357,7 +381,7 @@ uint16 Database::GetInstanceVersion(uint16 instance_id) { return atoi(row[0]); } -uint32 Database::GetTimeRemainingInstance(uint16 instance_id, bool &is_perma) +uint32 Database::GetTimeRemainingInstance(uint16 instance_id, bool is_perma) { uint32 start_time = 0; uint32 duration = 0; @@ -548,17 +572,36 @@ void Database::GetCharactersInInstance(uint16 instance_id, std::list &ch void Database::PurgeExpiredInstances() { - std::string query("SELECT id FROM instance_list where (start_time+duration) <= UNIX_TIMESTAMP() and never_expires = 0"); + + /** + * Delay purging by a day so that we can continue using adjacent free instance id's + * from the table without risking the chance we immediately re-allocate a zone that freshly expired but + * has not been fully de-allocated + */ + std::string query = + SQL( + SELECT + id + FROM + instance_list + where + (start_time + duration) <= (UNIX_TIMESTAMP() + 86400) + and never_expires = 0 + ); + auto results = QueryDatabase(query); - if (!results.Success()) + if (!results.Success()) { return; + } - if (results.RowCount() == 0) + if (results.RowCount() == 0) { return; + } - for (auto row = results.begin(); row != results.end(); ++row) + for (auto row = results.begin(); row != results.end(); ++row) { DeleteInstance(atoi(row[0])); + } } void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration) @@ -566,4 +609,4 @@ void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration) std::string query = StringFormat("UPDATE `instance_list` SET start_time=UNIX_TIMESTAMP(), " "duration=%u WHERE id=%u", new_duration, instance_id); auto results = QueryDatabase(query); -} \ No newline at end of file +} diff --git a/common/ruletypes.h b/common/ruletypes.h index a4c1ef8a2..2f5b9f246 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -270,7 +270,6 @@ RULE_INT(Zone, PEQZoneDebuff1, 4454, "First debuff casted by #peqzone Default is RULE_INT(Zone, PEQZoneDebuff2, 2209, "Second debuff casted by #peqzone Default is Tendrils of Apathy") RULE_BOOL(Zone, UsePEQZoneDebuffs, true, "Will determine if #peqzone will debuff players or not when used") RULE_REAL(Zone, HotZoneBonus, 0.75, "") -RULE_INT(Zone, ReservedInstances, 30, "Will reserve this many instance ids for globals... probably not a good idea to change this while a server is running") RULE_INT(Zone, EbonCrystalItemID, 40902, "") RULE_INT(Zone, RadiantCrystalItemID, 40903, "") RULE_BOOL(Zone, LevelBasedEXPMods, false, "Allows you to use the level_exp_mods table in consideration to your players EXP hits") @@ -775,6 +774,12 @@ RULE_BOOL(HotReload, QuestsRepopWhenPlayersNotInCombat, true, "When a hot reload RULE_BOOL(HotReload, QuestsResetTimersWithReload, true, "When a hot reload is triggered, quest timers will be reset") RULE_CATEGORY_END() +RULE_CATEGORY(Instances) +RULE_INT(Instances, ReservedInstances, 30, "Will reserve this many instance ids for globals... probably not a good idea to change this while a server is running") +RULE_BOOL(Instances, RecycleInstanceIds, true, "Will recycle free instance ids instead of gradually running out at 32k") +RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires") +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/common/timer.cpp b/common/timer.cpp index 120e3dea7..f4d931764 100644 --- a/common/timer.cpp +++ b/common/timer.cpp @@ -129,13 +129,17 @@ void Timer::SetTimer(uint32 set_timer_time) { } } -uint32 Timer::GetRemainingTime() const { +uint32 Timer::GetRemainingTime() const +{ if (enabled) { - if (current_time - start_time > timer_time) + if (current_time - start_time > timer_time) { return 0; - else + } + else { return (start_time + timer_time) - current_time; - } else { + } + } + else { return 0xFFFFFFFF; } } diff --git a/zone/client.cpp b/zone/client.cpp index a270d6579..c75573612 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1918,7 +1918,7 @@ void Client::CheckManaEndUpdate() { else if (group) { group->SendEndurancePacketFrom(this); } - + auto endurance_packet = new EQApplicationPacket(OP_EnduranceUpdate, sizeof(EnduranceUpdate_Struct)); EnduranceUpdate_Struct* endurance_update = (EnduranceUpdate_Struct*)endurance_packet->pBuffer; endurance_update->cur_end = GetEndurance(); @@ -8756,7 +8756,7 @@ void Client::CheckRegionTypeChanges() // still same region, do nothing if (last_region_type == new_region) return; - + // If we got out of water clear any water aggro for water only npcs if (last_region_type == RegionTypeWater) { entity_list.ClearWaterAggro(this); @@ -9203,7 +9203,7 @@ void Client::SetSecondaryWeaponOrnamentation(uint32 model_id) secondary_item->SetOrnamentationIDFile(model_id); SendItemPacket(EQEmu::invslot::slotSecondary, secondary_item, ItemPacketTrade); WearChange(EQEmu::textures::weaponSecondary, static_cast(model_id), 0); - + Message(Chat::Yellow, "Your secondary weapon appearance has been modified"); } } @@ -9292,3 +9292,41 @@ void Client::SetBotOption(BotOwnerOption boo, bool flag) { } #endif + +void Client::SendToGuildHall() +{ + std::string zone_short_name = "guildhall"; + uint32 zone_id = database.GetZoneID(zone_short_name.c_str()); + if (zone_id == 0) { + return; + } + + uint32 expiration_time = (RuleI(Instances, GuildHallExpirationDays) * 86400); + uint16 instance_id = 0; + std::string guild_hall_instance_key = fmt::format("guild-hall-instance-{}", GuildID()); + std::string instance_data = DataBucket::GetData(guild_hall_instance_key); + if (!instance_data.empty() && std::stoi(instance_data) > 0) { + instance_id = std::stoi(instance_data); + } + + if (instance_id <= 0) { + if (!database.GetUnusedInstanceID(instance_id)) { + Message(Chat::Red, "Server was unable to find a free instance id."); + return; + } + + if (!database.CreateInstance(instance_id, zone_id, 0, expiration_time)) { + Message(Chat::Red, "Server was unable to create a new instance."); + return; + } + + DataBucket::SetData( + guild_hall_instance_key, + std::to_string(instance_id), + std::to_string(expiration_time) + ); + } + + AssignToInstance(instance_id); + MovePC(345, instance_id, -1.00, -1.00, 3.34, 0, 1); +} diff --git a/zone/client.h b/zone/client.h index edc3f18c0..0986a31f5 100644 --- a/zone/client.h +++ b/zone/client.h @@ -633,6 +633,7 @@ public: void MovePC(uint32 zoneID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); void MovePC(float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); void MovePC(uint32 zoneID, uint32 instanceID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); + void SendToGuildHall(); void AssignToInstance(uint16 instance_id); void RemoveFromInstance(uint16 instance_id); void WhoAll(); @@ -691,7 +692,7 @@ public: int GetClientMaxLevel() const { return client_max_level; } void SetClientMaxLevel(int max_level) { client_max_level = max_level; } - + void CheckManaEndUpdate(); void SendManaUpdate(); void SendEnduranceUpdate(); @@ -1293,7 +1294,7 @@ public: void SendHPUpdateMarquee(); void CheckRegionTypeChanges(); - + WaterRegionType GetLastRegion() { return last_region_type; } int32 CalcATK(); @@ -1635,9 +1636,9 @@ private: bool InterrogateInventory_error(int16 head, int16 index, const EQEmu::ItemInstance* inst, const EQEmu::ItemInstance* parent, int depth); int client_max_level; - + #ifdef BOTS - + public: enum BotOwnerOption : size_t { booDeathMarquee, @@ -1654,7 +1655,7 @@ public: bool GetBotOption(BotOwnerOption boo) const; void SetBotOption(BotOwnerOption boo, bool flag = true); - + bool GetBotPulling() { return m_bot_pulling; } void SetBotPulling(bool flag = true) { m_bot_pulling = flag; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 1caf2b4b3..779a4e352 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -599,7 +599,7 @@ void Client::CompleteConnect() if (group) group->SendHPManaEndPacketsTo(this); } - + //bulk raid send in here eventually @@ -818,35 +818,46 @@ void Client::CompleteConnect() database.QueryDatabase( StringFormat( "UPDATE `character_data` SET `last_login` = UNIX_TIMESTAMP() WHERE id = %u", - this->CharacterID() + CharacterID() ) ); } - if (zone) { - if (zone->GetInstanceTimer()) { - uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); - uint32 day = (ttime / 86400000); - uint32 hour = (ttime / 3600000) % 24; - uint32 minute = (ttime / 60000) % 60; - uint32 second = (ttime / 1000) % 60; - if (day) { - Message(Chat::Yellow, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); - } - else if (hour) { - Message(Chat::Yellow, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); - } - else if (minute) { - Message(Chat::Yellow, "%s(%u) will expire in %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), minute, second); - } - else { - Message(Chat::Yellow, "%s(%u) will expire in in %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), second); - } + if (zone && zone->GetInstanceID() > 0) { + + uint32 remaining_time_seconds = zone->GetInstanceTimeRemaining(); + uint32 day = (remaining_time_seconds / 86400); + uint32 hour = (remaining_time_seconds / 3600) % 24; + uint32 minute = (remaining_time_seconds / 60) % 60; + uint32 second = (remaining_time_seconds / 1) % 60; + + LogInfo("Remaining time seconds [{}]", remaining_time_seconds); + + if (day) { + Message( + Chat::Yellow, "%s (%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second + ); } + else if (hour) { + Message( + Chat::Yellow, "%s (%u) will expire in %u hours, %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), hour, minute, second + ); + } + else if (minute) { + Message( + Chat::Yellow, "%s (%u) will expire in %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), minute, second + ); + } + else { + Message( + Chat::Yellow, "%s (%u) will expire in in %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), second + ); + } + } SendRewards(); @@ -1237,7 +1248,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) database.ClearOldRecastTimestamps(cid); /* Clear out our old recast timestamps to keep the DB clean */ // set to full support in case they're a gm with items in disabled expansion slots..but, have their gm flag off... // item loss will occur when they use the 'empty' slots, if this is not done - m_inv.SetGMInventory(true); + m_inv.SetGMInventory(true); loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ @@ -1341,7 +1352,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) client_max_level = GetCharMaxLevelFromBucket(); } SetClientMaxLevel(client_max_level); - + // we know our class now, so we might have to fix our consume timer! if (class_ == MONK) consume_food_timer.SetTimer(CONSUMPTION_MNK_TIMER); @@ -2840,7 +2851,7 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) // rogue simply won't apply at all, no skill check done. uint16 poison_skill = GetSkill(EQEmu::skills::SkillApplyPoison); - + if (ChanceRoll < (.75 + poison_skill / 1000)) { ApplyPoisonSuccessResult = 1; AddProcToWeapon(poison->Proc.Effect, false, (GetDEX() / 100) + 103, POISON_PROC); @@ -3917,7 +3928,7 @@ void Client::Handle_OP_Bug(const EQApplicationPacket *app) Message(0, "Bug reporting is disabled on this server."); return; } - + if (app->size != sizeof(BugReport_Struct)) { printf("Wrong size of BugReport_Struct got %d expected %zu!\n", app->size, sizeof(BugReport_Struct)); } @@ -4017,7 +4028,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) //Message(Chat::Red, "You cant cast right now, you arent in control of yourself!"); return; } - + // Hack for broken RoF2 which allows casting after a zoned IVU/IVA if (invisible_undead || invisible_animals) { BuffFadeByEffect(SE_InvisVsAnimals); @@ -4370,9 +4381,9 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { sizeof(PlayerPositionUpdateClient_Struct), app->size); return; } - + PlayerPositionUpdateClient_Struct *ppu = (PlayerPositionUpdateClient_Struct *) app->pBuffer; - + /* Boat handling */ if (ppu->spawn_id != GetID()) { /* If player is controlling boat */ @@ -4382,16 +4393,16 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { controlling_boat_id = 0; return; } - + auto boat_delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, EQ10toFloat(ppu->delta_heading)); boat->SetDelta(boat_delta); - + auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct *ppus = (PlayerPositionUpdateServer_Struct *) outapp->pBuffer; boat->MakeSpawnUpdate(ppus); entity_list.QueueCloseClients(boat, outapp, true, 300, this, false); safe_delete(outapp); - + /* Update the boat's position on the server, without sending an update */ boat->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading), false); return; @@ -4406,9 +4417,9 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { if (cmob != nullptr) { cmob->SetPosition(ppu->x_pos, ppu->y_pos, ppu->z_pos); cmob->SetHeading(EQ12toFloat(ppu->heading)); - mMovementManager->SendCommandToClients(cmob, 0.0, 0.0, 0.0, + mMovementManager->SendCommandToClients(cmob, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny, nullptr, this); - cmob->CastToNPC()->SaveGuardSpot(glm::vec4(ppu->x_pos, + cmob->CastToNPC()->SaveGuardSpot(glm::vec4(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading))); } } @@ -4426,7 +4437,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { // From this point forward, we need to use a new set of variables for client // position. If the client is in a boat, we need to add the boat pos and // the client offset together. - + float cx = ppu->x_pos; float cy = ppu->y_pos; float cz = ppu->z_pos; @@ -4451,45 +4462,45 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { /* Check to see if PPU should trigger an update to the rewind position. */ float rewind_x_diff = 0; float rewind_y_diff = 0; - + rewind_x_diff = cx - m_RewindLocation.x; rewind_x_diff *= rewind_x_diff; rewind_y_diff = cy - m_RewindLocation.y; rewind_y_diff *= rewind_y_diff; - - /* + + /* We only need to store updated values if the player has moved. If the player has moved more than units for x or y, then we'll store his pre-PPU x and y for /rewind, in case he gets stuck. */ - + if ((rewind_x_diff > 750) || (rewind_y_diff > 750)) m_RewindLocation = glm::vec3(m_Position); - + /* If the PPU was a large jump, such as a cross zone gate or Call of Hero, just update rewind coordinates to the new ppu coordinates. This will prevent exploitation. */ - + if ((rewind_x_diff > 5000) || (rewind_y_diff > 5000)) m_RewindLocation = glm::vec3(cx, cy, cz); - + if (proximity_timer.Check()) { entity_list.ProcessMove(this, glm::vec3(cx, cy, cz)); if (RuleB(TaskSystem, EnableTaskSystem) && RuleB(TaskSystem, EnableTaskProximity)) ProcessTaskProximities(cx, cy, cz); - + m_Proximity = glm::vec3(cx, cy, cz); } - + /* Update internal state */ m_Delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, EQ10toFloat(ppu->delta_heading)); - + if (IsTracking() && ((m_Position.x != cx) || (m_Position.y != cy))) { if (zone->random.Real(0, 100) < 70)//should be good CheckIncreaseSkill(EQEmu::skills::SkillTracking, nullptr, -20); } - + /* Break Hide if moving without sneaking and set rewind timer if moved */ if (cy != m_Position.y || cx != m_Position.x) { if ((hidden || improved_hidden) && !sneaking) { @@ -4508,7 +4519,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { } rewind_timer.Start(30000, true); } - + /* Handle client aggro scanning timers NPCs */ is_client_moving = (cy == m_Position.y && cx == m_Position.x) ? false : true; @@ -4572,55 +4583,55 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { } int32 new_animation = ppu->animation; - + /* Update internal server position from what the client has sent */ m_Position.x = cx; m_Position.y = cy; m_Position.z = cz; - + /* Visual Debugging */ if (RuleB(Character, OPClientUpdateVisualDebug)) { LogDebug("ClientUpdate: ppu x: [{}] y: [{}] z: [{}] h: [{}]", cx, cy, cz, new_heading); this->SendAppearanceEffect(78, 0, 0, 0, 0); this->SendAppearanceEffect(41, 0, 0, 0, 0); } - + /* Only feed real time updates when client is moving */ if (is_client_moving || new_heading != m_Position.w || new_animation != animation) { - + animation = ppu->animation; m_Position.w = new_heading; - + /* Broadcast update to other clients */ auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct *position_update = (PlayerPositionUpdateServer_Struct *) outapp->pBuffer; - + MakeSpawnUpdate(position_update); - + if (gm_hide_me) { entity_list.QueueClientsStatus(this, outapp, true, Admin(), 255); } else { entity_list.QueueCloseClients(this, outapp, true, RuleI(Range, ClientPositionUpdates), nullptr, true); } - - + + /* Always send position updates to group - send when beyond normal ClientPositionUpdate range */ Group *group = this->GetGroup(); Raid *raid = this->GetRaid(); - + if (raid) { raid->QueueClients(this, outapp, true, true, (RuleI(Range, ClientPositionUpdates) * -1)); } else if (group) { group->QueueClients(this, outapp, true, true, (RuleI(Range, ClientPositionUpdates) * -1)); } - + safe_delete(outapp); } - + if (zone->watermap) { if (zone->watermap->InLiquid(glm::vec3(m_Position))) { CheckIncreaseSkill(EQEmu::skills::SkillSwimming, nullptr, -17); - + // Dismount horses when entering water if (GetHorseId() && RuleB(Character, DismountWater)) { SetHorseId(0); @@ -5757,23 +5768,23 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) printf("Error in FindPersonRequest_Struct. Expected size of: %zu, but got: %i\n", sizeof(FindPersonRequest_Struct), app->size); else { FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; - + std::vector points; Mob* target = entity_list.GetMob(t->npc_id); - + if (target == nullptr) { //empty length packet == not found. EQApplicationPacket outapp(OP_FindPersonReply, 0); QueuePacket(&outapp); return; } - + if (!RuleB(Pathing, Find) && RuleB(Bazaar, EnableWarpToTrader) && target->IsClient() && (target->CastToClient()->Trader || target->CastToClient()->Buyer)) { Message(Chat::Yellow, "Moving you to Trader %s", target->GetName()); MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ(), 0.0f); } - + if (!RuleB(Pathing, Find) || !zone->pathing) { //fill in the path array... @@ -5796,40 +5807,40 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) { glm::vec3 Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); glm::vec3 End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION); - + bool partial = false; bool stuck = false; auto pathlist = zone->pathing->FindRoute(Start, End, partial, stuck); - + if (pathlist.empty() || partial) { EQApplicationPacket outapp(OP_FindPersonReply, 0); QueuePacket(&outapp); return; } - + // Live appears to send the points in this order: // Final destination. // Current Position. // rest of the points. FindPerson_Point p; - + int PointNumber = 0; - + bool LeadsToTeleporter = false; - + auto v = pathlist.back(); - + p.x = v.pos.x; p.y = v.pos.y; p.z = v.pos.z; points.push_back(p); - + p.x = GetX(); p.y = GetY(); p.z = GetZ(); points.push_back(p); - + for (auto Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) { if ((*Iterator).teleport) // Teleporter @@ -5837,7 +5848,7 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) LeadsToTeleporter = true; break; } - + glm::vec3 v = (*Iterator).pos; p.x = v.x; p.y = v.y; @@ -5845,17 +5856,17 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) points.push_back(p); ++PointNumber; } - + if (!LeadsToTeleporter) { p.x = target->GetX(); p.y = target->GetY(); p.z = target->GetZ(); - + points.push_back(p); } } - + SendPathPacket(points); } } @@ -11098,14 +11109,14 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) { case RaidCommandInviteIntoExisting: case RaidCommandInvite: { - + Client *player_to_invite = entity_list.GetClientByName(raid_command_packet->player_name); if (!player_to_invite) break; Group *player_to_invite_group = player_to_invite->GetGroup(); - + if (player_to_invite->HasRaid()) { Message(Chat::Red, "%s is already in a raid.", player_to_invite->GetName()); break; @@ -11120,7 +11131,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) Message(Chat::Red, "You can only invite an ungrouped player or group leader to join your raid."); break; } - + /* Send out invite to the client */ auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); RaidGeneral_Struct *raid_command = (RaidGeneral_Struct*)outapp->pBuffer; @@ -11132,7 +11143,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid_command->action = 20; player_to_invite->QueuePacket(outapp); - + safe_delete(outapp); break; @@ -11228,7 +11239,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } if (player_invited_group->IsLeader(player_invited_group->members[x])) { Client *c = nullptr; - + if (player_invited_group->members[x]->IsClient()) c = player_invited_group->members[x]->CastToClient(); else @@ -11238,24 +11249,24 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, raid_free_group_id, true, true, true); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } } else { Client *c = nullptr; - + if (player_invited_group->members[x]->IsClient()) c = player_invited_group->members[x]->CastToClient(); else continue; - + raid->SendRaidCreate(c); raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, raid_free_group_id); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } @@ -11289,12 +11300,12 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) c = group->members[x]->CastToClient(); else continue; - + raid->SendRaidCreate(c); raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, raid_free_group_id, false, true); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } @@ -11302,17 +11313,17 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) else { Client *c = nullptr; - + if (group->members[x]->IsClient()) c = group->members[x]->CastToClient(); else continue; - + raid->SendRaidCreate(c); raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, raid_free_group_id); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } @@ -11329,7 +11340,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) if (player_invited_group) { raid = new Raid(player_accepting_invite); - + entity_list.AddRaid(raid); raid->SetRaidDetails(); Client *addClientig = nullptr; @@ -11353,7 +11364,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, 0, true, true, true); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } @@ -11478,7 +11489,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid->SetGroupLeader(raid_command_packet->leader_name, false); /* We were the leader of our old group */ - if (old_group < 12) { + if (old_group < 12) { /* Assign new group leader if we can */ for (int x = 0; x < MAX_RAID_MEMBERS; x++) { if (raid->members[x].GroupNumber == old_group) { @@ -11507,7 +11518,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) strn0cpy(raid_command_packet->playername, raid->members[x].membername, 64); worldserver.SendPacket(pack); - + safe_delete(pack); } break; @@ -11553,7 +11564,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid->SetGroupLeader(raid_command_packet->leader_name, false); for (int x = 0; x < MAX_RAID_MEMBERS; x++) { if (raid->members[x].GroupNumber == oldgrp && strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, raid_command_packet->leader_name) != 0){ - + raid->SetGroupLeader(raid->members[x].membername); raid->UpdateGroupAAs(oldgrp); @@ -11576,7 +11587,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) strn0cpy(raid_command->playername, raid->members[x].membername, 64); raid_command->zoneid = zone->GetZoneID(); raid_command->instance_id = zone->GetInstanceID(); - + worldserver.SendPacket(pack); safe_delete(pack); } @@ -11591,14 +11602,14 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) else { auto pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct* raid_command = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - + raid_command->rid = raid->GetID(); raid_command->zoneid = zone->GetZoneID(); raid_command->instance_id = zone->GetInstanceID(); strn0cpy(raid_command->playername, raid_command_packet->leader_name, 64); worldserver.SendPacket(pack); - + safe_delete(pack); } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 9d0873360..955130922 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -90,6 +90,11 @@ void Lua_Client::SetPVP(bool v) { self->SetPVP(v); } +void Lua_Client::SendToGuildHall() { + Lua_Safe_Call_Void(); + self->SendToGuildHall(); +} + bool Lua_Client::GetPVP() { Lua_Safe_Call_Bool(); return self->GetPVP(); @@ -1584,6 +1589,7 @@ luabind::scope lua_register_client() { .def("Disconnect", (void(Lua_Client::*)(void))&Lua_Client::Disconnect) .def("IsLD", (bool(Lua_Client::*)(void))&Lua_Client::IsLD) .def("WorldKick", (void(Lua_Client::*)(void))&Lua_Client::WorldKick) + .def("SendToGuildHall", (void(Lua_Client::*)(void))&Lua_Client::SendToGuildHall) .def("GetAnon", (bool(Lua_Client::*)(void))&Lua_Client::GetAnon) .def("Duck", (void(Lua_Client::*)(void))&Lua_Client::Duck) .def("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand) diff --git a/zone/lua_client.h b/zone/lua_client.h index aef0378cb..b0eb719e8 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -39,6 +39,7 @@ public: void Disconnect(); bool IsLD(); void WorldKick(); + void SendToGuildHall(); bool GetAnon(); void Duck(); void Stand(); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 2390dea3a..e46b5e8d4 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -245,6 +245,27 @@ XS(XS_Client_WorldKick) { XSRETURN_EMPTY; } +XS(XS_Client_SendToGuildHall); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendToGuildHall) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::SendToGuildHall(THIS)"); + { + Client *THIS; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV *) SvRV(ST(0))); + THIS = INT2PTR(Client *, tmp); + } else + Perl_croak(aTHX_ "THIS is not of type Client"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + THIS->SendToGuildHall(); + } + XSRETURN_EMPTY; +} + XS(XS_Client_GetAnon); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_GetAnon) { dXSARGS; @@ -2439,7 +2460,7 @@ XS(XS_Client_MemmedCount) { RETVAL = THIS->MemmedCount(); XSprePUSH; - PUSHu((UV) RETVAL); + PUSHu((UV) RETVAL); } XSRETURN(1); } @@ -4786,7 +4807,7 @@ XS(XS_Client_AddLevelBasedExp) { if (items > 2) max_level = (uint8) SvUV(ST(2)); - + if (items > 3) ignore_mods = (bool) SvTRUE(ST(3)); @@ -6564,6 +6585,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SendSound"), XS_Client_SendSound, file, "$"); newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$"); newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$"); + newXSproto(strcpy(buf, "SendToGuildHall"), XS_Client_SendToGuildHall, file, "$"); newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$"); newXSproto(strcpy(buf, "SendZoneFlagInfo"), XS_Client_SendZoneFlagInfo, file, "$$"); newXSproto(strcpy(buf, "SetAAPoints"), XS_Client_SetAAPoints, file, "$$"); diff --git a/zone/zone.cpp b/zone/zone.cpp index 16cdc0af8..9e50d0b8c 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -140,7 +140,7 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { if(iInstanceID != 0) { auto pack = new ServerPacket(ServerOP_AdventureZoneData, sizeof(uint16)); - *((uint16*)pack->pBuffer) = iInstanceID; + *((uint16*)pack->pBuffer) = iInstanceID; worldserver.SendPacket(pack); delete pack; } @@ -491,7 +491,7 @@ void Zone::LoadNewMerchantData(uint32 merchantid) { void Zone::GetMerchantDataForZoneLoad() { LogInfo("Loading Merchant Lists"); - std::string query = StringFormat( + std::string query = StringFormat( "SELECT " "DISTINCT ml.merchantid, " "ml.slot, " @@ -816,7 +816,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) { LogDebug("Graveyard ID is [{}]", graveyard_id()); bool GraveYardLoaded = database.GetZoneGraveyard(graveyard_id(), &pgraveyard_zoneid, &m_Graveyard.x, &m_Graveyard.y, &m_Graveyard.z, &m_Graveyard.w); - + if (GraveYardLoaded) { LogDebug("Loaded a graveyard for zone [{}]: graveyard zoneid is [{}] at [{}]", short_name, graveyard_zoneid(), to_string(m_Graveyard).c_str()); } @@ -907,7 +907,7 @@ Zone::~Zone() { //Modified for timezones. bool Zone::Init(bool iStaticZone) { SetStaticZone(iStaticZone); - + //load the zone config file. if (!LoadZoneCFG(zone->GetShortName(), zone->GetInstanceVersion())) // try loading the zone name... LoadZoneCFG(zone->GetFileName(), zone->GetInstanceVersion()); // if that fails, try the file name, then load defaults @@ -1018,6 +1018,10 @@ bool Zone::Init(bool iStaticZone) { petition_list.ClearPetitions(); petition_list.ReadDatabase(); + if (zone->GetInstanceID() > 0) { + zone->SetInstanceTimeRemaining(database.GetTimeRemainingInstance(zone->GetInstanceID())); + } + LogInfo("Loading timezone data"); zone->zone_time.setEQTimeZone(database.GetZoneTZ(zoneid, GetInstanceVersion())); @@ -1089,7 +1093,7 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_id) if (instance_id != 0) { safe_delete_array(map_name); - if(!database.GetZoneCFG(database.GetZoneID(filename), 0, &newzone_data, can_bind, can_combat, can_levitate, + if(!database.GetZoneCFG(database.GetZoneID(filename), 0, &newzone_data, can_bind, can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, allow_mercs, max_movement_update_range, zone_type, default_ruleset, &map_name)) { LogError("Error loading the Zone Config"); @@ -2455,3 +2459,13 @@ void Zone::SetQuestHotReloadQueued(bool in_quest_hot_reload_queued) { quest_hot_reload_queued = in_quest_hot_reload_queued; } + +uint32 Zone::GetInstanceTimeRemaining() const +{ + return instance_time_remaining; +} + +void Zone::SetInstanceTimeRemaining(uint32 instance_time_remaining) +{ + Zone::instance_time_remaining = instance_time_remaining; +} diff --git a/zone/zone.h b/zone/zone.h index 1243e18c2..fc7fa8bf7 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -278,6 +278,9 @@ public: ZonePoint *GetClosestZonePoint(const glm::vec3 &location, uint32 to, Client *client, float max_distance = 40000.0f); ZonePoint *GetClosestZonePointWithoutZone(float x, float y, float z, Client *client, float max_distance = 40000.0f); + uint32 GetInstanceTimeRemaining() const; + void SetInstanceTimeRemaining(uint32 instance_time_remaining); + /** * GMSay Callback for LogSys * @@ -361,6 +364,7 @@ private: uint8 zone_type; uint16 instanceversion; uint32 instanceid; + uint32 instance_time_remaining; uint32 pgraveyard_id, pgraveyard_zoneid; uint32 pMaxClients; uint32 zoneid; From d89b2f11b5fc334a064f542b4fb0b71a6746ce0d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Apr 2020 02:03:30 -0500 Subject: [PATCH 17/28] Get time remaining directly from database for now, this needs to be cached better at the zone state level, I'll refactor this when repositories are merged in --- zone/client_packet.cpp | 2 +- zone/zone.cpp | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 779a4e352..542b52288 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -825,7 +825,7 @@ void Client::CompleteConnect() if (zone && zone->GetInstanceID() > 0) { - uint32 remaining_time_seconds = zone->GetInstanceTimeRemaining(); + uint32 remaining_time_seconds = database.GetTimeRemainingInstance(zone->GetInstanceID()); uint32 day = (remaining_time_seconds / 86400); uint32 hour = (remaining_time_seconds / 3600) % 24; uint32 minute = (remaining_time_seconds / 60) % 60; diff --git a/zone/zone.cpp b/zone/zone.cpp index 9e50d0b8c..55de2b291 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1018,10 +1018,6 @@ bool Zone::Init(bool iStaticZone) { petition_list.ClearPetitions(); petition_list.ReadDatabase(); - if (zone->GetInstanceID() > 0) { - zone->SetInstanceTimeRemaining(database.GetTimeRemainingInstance(zone->GetInstanceID())); - } - LogInfo("Loading timezone data"); zone->zone_time.setEQTimeZone(database.GetZoneTZ(zoneid, GetInstanceVersion())); From bd8e94ff174d9a34c566c10fd249868fdd681a37 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Apr 2020 02:20:56 -0500 Subject: [PATCH 18/28] Use version 1 for GH --- zone/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index c75573612..4a7a66954 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9315,7 +9315,7 @@ void Client::SendToGuildHall() return; } - if (!database.CreateInstance(instance_id, zone_id, 0, expiration_time)) { + if (!database.CreateInstance(instance_id, zone_id, 1, expiration_time)) { Message(Chat::Red, "Server was unable to create a new instance."); return; } From d1349e5ac9debf1f9cd5df700f3e28b142fcff56 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Apr 2020 02:30:46 -0500 Subject: [PATCH 19/28] Oops, math --- common/database_instances.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/database_instances.cpp b/common/database_instances.cpp index 3359dc7ce..d6a12c750 100644 --- a/common/database_instances.cpp +++ b/common/database_instances.cpp @@ -585,7 +585,7 @@ void Database::PurgeExpiredInstances() FROM instance_list where - (start_time + duration) <= (UNIX_TIMESTAMP() + 86400) + (start_time + duration) <= (UNIX_TIMESTAMP() - 86400) and never_expires = 0 ); From 499fe153ab4e38e1534a9bf623f4706442080b75 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Apr 2020 02:56:45 -0500 Subject: [PATCH 20/28] Tweak peq dump [skip ci] --- utils/sql/peq-dump/peq-dump.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/sql/peq-dump/peq-dump.sh b/utils/sql/peq-dump/peq-dump.sh index ccb7b8949..1687034fd 100755 --- a/utils/sql/peq-dump/peq-dump.sh +++ b/utils/sql/peq-dump/peq-dump.sh @@ -44,6 +44,7 @@ echo "Generating [create_*] table exports..." bash -c "${world_bin} database:dump --login-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_login.sql" bash -c "${world_bin} database:dump --player-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_player.sql" bash -c "${world_bin} database:dump --state-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_state.sql" +echo 'INSERT INTO `instance_list` VALUES (1,25,1,1,0,0,1),(2,25,2,1,0,0,1),(3,151,1,1,0,0,1),(4,114,1,1,0,0,1),(5,344,1,1,0,0,1),(6,202,0,1,0,0,1);' > "${dump_path}create_tables_state.sql" bash -c "${world_bin} database:dump --query-serv-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_queryserv.sql" # with content @@ -67,4 +68,4 @@ bash -c "cd /tmp/ && rm -rf peq-latest.zip && zip peq-latest.zip peq-dump/* && m echo "Cleaning up..." rm -rf ${dump_path} -echo "Dump located [/tmp/peq-latest.zip]" \ No newline at end of file +echo "Dump located [/tmp/peq-latest.zip]" From 0dc3e5ba35f481f66f1d93e4aea5ff5529d47da1 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Apr 2020 03:20:41 -0500 Subject: [PATCH 21/28] Add jank permanent reference pass back in [skip ci] --- common/database.h | 2 +- common/database_instances.cpp | 2 +- zone/client_packet.cpp | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/common/database.h b/common/database.h index 96897e4ea..962349fa9 100644 --- a/common/database.h +++ b/common/database.h @@ -159,7 +159,7 @@ public: uint16 GetInstanceID(const char* zone, uint32 charid, int16 version); uint16 GetInstanceID(uint32 zone, uint32 charid, int16 version); uint16 GetInstanceVersion(uint16 instance_id); - uint32 GetTimeRemainingInstance(uint16 instance_id, bool is_perma = false); + uint32 GetTimeRemainingInstance(uint16 instance_id, bool &is_perma); uint32 VersionFromInstanceID(uint16 instance_id); uint32 ZoneIDFromInstanceID(uint16 instance_id); diff --git a/common/database_instances.cpp b/common/database_instances.cpp index d6a12c750..350c9849f 100644 --- a/common/database_instances.cpp +++ b/common/database_instances.cpp @@ -381,7 +381,7 @@ uint16 Database::GetInstanceVersion(uint16 instance_id) { return atoi(row[0]); } -uint32 Database::GetTimeRemainingInstance(uint16 instance_id, bool is_perma) +uint32 Database::GetTimeRemainingInstance(uint16 instance_id, bool &is_perma) { uint32 start_time = 0; uint32 duration = 0; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 542b52288..1267386eb 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -823,16 +823,15 @@ void Client::CompleteConnect() ); } - if (zone && zone->GetInstanceID() > 0) { + if (zone && zone->GetInstanceTimer()) { - uint32 remaining_time_seconds = database.GetTimeRemainingInstance(zone->GetInstanceID()); + bool is_permanent = false; + uint32 remaining_time_seconds = database.GetTimeRemainingInstance(zone->GetInstanceID(), is_permanent); uint32 day = (remaining_time_seconds / 86400); uint32 hour = (remaining_time_seconds / 3600) % 24; uint32 minute = (remaining_time_seconds / 60) % 60; uint32 second = (remaining_time_seconds / 1) % 60; - LogInfo("Remaining time seconds [{}]", remaining_time_seconds); - if (day) { Message( Chat::Yellow, "%s (%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", From b5b473f25d5fa00642710122ab351258b310ed47 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Apr 2020 03:21:33 -0500 Subject: [PATCH 22/28] Update peq dump [skip ci] --- utils/sql/peq-dump/peq-dump.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/sql/peq-dump/peq-dump.sh b/utils/sql/peq-dump/peq-dump.sh index 1687034fd..086cd3357 100755 --- a/utils/sql/peq-dump/peq-dump.sh +++ b/utils/sql/peq-dump/peq-dump.sh @@ -44,7 +44,7 @@ echo "Generating [create_*] table exports..." bash -c "${world_bin} database:dump --login-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_login.sql" bash -c "${world_bin} database:dump --player-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_player.sql" bash -c "${world_bin} database:dump --state-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_state.sql" -echo 'INSERT INTO `instance_list` VALUES (1,25,1,1,0,0,1),(2,25,2,1,0,0,1),(3,151,1,1,0,0,1),(4,114,1,1,0,0,1),(5,344,1,1,0,0,1),(6,202,0,1,0,0,1);' > "${dump_path}create_tables_state.sql" +echo 'REPLACE INTO `instance_list` VALUES (1,25,1,1,0,0,1),(2,25,2,1,0,0,1),(3,151,1,1,0,0,1),(4,114,1,1,0,0,1),(5,344,1,1,0,0,1),(6,202,0,1,0,0,1);' > "${dump_path}create_tables_state.sql" bash -c "${world_bin} database:dump --query-serv-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_queryserv.sql" # with content From b41f2dac663e42fa25bbe78aa945d46738db4028 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Apr 2020 03:26:09 -0500 Subject: [PATCH 23/28] Clamp value for max reserved instance id [skip ci] --- common/database_instances.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/database_instances.cpp b/common/database_instances.cpp index 350c9849f..10a2ce032 100644 --- a/common/database_instances.cpp +++ b/common/database_instances.cpp @@ -194,6 +194,11 @@ bool Database::GetUnusedInstanceID(uint16 &instance_id) return true; } + if (instance_id < max_reserved_instance_id) { + instance_id = max_reserved_instance_id; + return true; + } + query = StringFormat("SELECT id FROM instance_list where id > %u ORDER BY id", max_reserved_instance_id); results = QueryDatabase(query); From a031c20e7ec865a96aa6f31e6aeefe0ab733cc00 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 11 Apr 2020 16:44:19 -0500 Subject: [PATCH 24/28] Update peq-dump.sh [skip ci] --- utils/sql/peq-dump/peq-dump.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/sql/peq-dump/peq-dump.sh b/utils/sql/peq-dump/peq-dump.sh index 086cd3357..7eaffdc6a 100755 --- a/utils/sql/peq-dump/peq-dump.sh +++ b/utils/sql/peq-dump/peq-dump.sh @@ -44,7 +44,7 @@ echo "Generating [create_*] table exports..." bash -c "${world_bin} database:dump --login-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_login.sql" bash -c "${world_bin} database:dump --player-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_player.sql" bash -c "${world_bin} database:dump --state-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_state.sql" -echo 'REPLACE INTO `instance_list` VALUES (1,25,1,1,0,0,1),(2,25,2,1,0,0,1),(3,151,1,1,0,0,1),(4,114,1,1,0,0,1),(5,344,1,1,0,0,1),(6,202,0,1,0,0,1);' > "${dump_path}create_tables_state.sql" +echo 'REPLACE INTO `instance_list` VALUES (1,25,1,1,0,0,1),(2,25,2,1,0,0,1),(3,151,1,1,0,0,1),(4,114,1,1,0,0,1),(5,344,1,1,0,0,1),(6,202,0,1,0,0,1);' >> "${dump_path}create_tables_state.sql" bash -c "${world_bin} database:dump --query-serv-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_queryserv.sql" # with content From d29c0fc3321dbe9bf763f303a37be13e879d8338 Mon Sep 17 00:00:00 2001 From: TurmoilToad Date: Mon, 13 Apr 2020 10:01:22 -0400 Subject: [PATCH 25/28] Update README.md Updated wiki link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 31f88be38..1b08d54be 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ forum, although pull requests will be much quicker and easier on all parties. ## Resources - [EQEmulator Forums](http://www.eqemulator.org/forums) -- [EQEmulator Wiki](https://github.com/EQEmu/Server/wiki) +- [EQEmulator Wiki](https://eqemu.gitbook.io/) ## Related Repositories * [ProjectEQ Quests](https://github.com/ProjectEQ/projecteqquests) From 16cfad1966cacd63e0d0ab93be9ec48762312d69 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 14 Apr 2020 23:28:43 -0500 Subject: [PATCH 26/28] UCS / Raid / Zone Fixes (#1033) * Cache EntityList::GetRaidByClient * Adjustments [skip ci] * Update entity [skip ci] * More cleanup [skip ci] * More tweaks [skip ci] * Cleanup [skip ci] * Fix bugs with UCS reconnection on crash / exit, not adding soft deleted characters, put main loop on UV lib * Reduce log spam that should be debugging; send keepalives to clients so that they properly prune from the connection list * Shutdown the eventloop to properly shutdown the zone versus calling a hard exit --- ucs/chatchannel.cpp | 29 ++++++++++++++----------- ucs/clientlist.cpp | 51 ++++++++++++++++++++++++++++++-------------- ucs/clientlist.h | 4 +++- ucs/database.cpp | 6 +++--- ucs/ucs.cpp | 24 ++++++++++++++------- ucs/worldserver.cpp | 2 +- world/ucs.cpp | 41 +++++++++++++++++++++++++---------- world/ucs.h | 13 ++++++++--- zone/client.cpp | 1 + zone/client.h | 3 +++ zone/entity.cpp | 23 ++++++++++++++------ zone/raids.cpp | 10 +++++---- zone/worldserver.cpp | 6 ++++-- zone/zone.cpp | 2 +- 14 files changed, 146 insertions(+), 69 deletions(-) diff --git a/ucs/chatchannel.cpp b/ucs/chatchannel.cpp index 001f7b1e7..efac898d5 100644 --- a/ucs/chatchannel.cpp +++ b/ucs/chatchannel.cpp @@ -47,8 +47,13 @@ ChatChannel::ChatChannel(std::string inName, std::string inOwner, std::string in Moderated = false; - LogInfo("New ChatChannel created: Name: [[{}]], Owner: [[{}]], Password: [[{}]], MinStatus: [{}]", - Name.c_str(), Owner.c_str(), Password.c_str(), MinimumStatus); + LogDebug( + "New ChatChannel created: Name: [[{}]], Owner: [[{}]], Password: [[{}]], MinStatus: [{}]", + Name.c_str(), + Owner.c_str(), + Password.c_str(), + MinimumStatus + ); } @@ -154,7 +159,7 @@ void ChatChannelList::SendAllChannels(Client *c) { void ChatChannelList::RemoveChannel(ChatChannel *Channel) { - LogInfo("RemoveChannel([{}])", Channel->GetName().c_str()); + LogDebug("RemoveChannel ([{}])", Channel->GetName().c_str()); LinkedListIterator iterator(ChatChannels); @@ -175,7 +180,7 @@ void ChatChannelList::RemoveChannel(ChatChannel *Channel) { void ChatChannelList::RemoveAllChannels() { - LogInfo("RemoveAllChannels"); + LogDebug("RemoveAllChannels"); LinkedListIterator iterator(ChatChannels); @@ -242,7 +247,7 @@ void ChatChannel::AddClient(Client *c) { int AccountStatus = c->GetAccountStatus(); - LogInfo("Adding [{}] to channel [{}]", c->GetName().c_str(), Name.c_str()); + LogDebug("Adding [{}] to channel [{}]", c->GetName().c_str(), Name.c_str()); LinkedListIterator iterator(ClientsInChannel); @@ -267,7 +272,7 @@ bool ChatChannel::RemoveClient(Client *c) { if(!c) return false; - LogInfo("RemoveClient [{}] from channel [{}]", c->GetName().c_str(), GetName().c_str()); + LogDebug("RemoveClient [{}] from channel [{}]", c->GetName().c_str(), GetName().c_str()); bool HideMe = c->GetHideMe(); @@ -304,7 +309,7 @@ bool ChatChannel::RemoveClient(Client *c) { if((Password.length() == 0) || (RuleI(Channels, DeleteTimer) == 0)) return false; - LogInfo("Starting delete timer for empty password protected channel [{}]", Name.c_str()); + LogDebug("Starting delete timer for empty password protected channel [{}]", Name.c_str()); DeleteTimer.Start(RuleI(Channels, DeleteTimer) * 60000); } @@ -402,7 +407,7 @@ void ChatChannel::SendMessageToChannel(std::string Message, Client* Sender) { if(ChannelClient) { - LogInfo("Sending message to [{}] from [{}]", + LogDebug("Sending message to [{}] from [{}]", ChannelClient->GetName().c_str(), Sender->GetName().c_str()); if (cv_messages[static_cast(ChannelClient->GetClientVersion())].length() == 0) { @@ -505,7 +510,7 @@ ChatChannel *ChatChannelList::AddClientToChannel(std::string ChannelName, Client return nullptr; } - LogInfo("AddClient to channel [[{}]] with password [[{}]]", NormalisedName.c_str(), Password.c_str()); + LogDebug("AddClient to channel [[{}]] with password [[{}]]", NormalisedName.c_str(), Password.c_str()); ChatChannel *RequiredChannel = FindChannel(NormalisedName); @@ -581,7 +586,7 @@ void ChatChannelList::Process() { if(CurrentChannel && CurrentChannel->ReadyToDelete()) { - LogInfo("Empty temporary password protected channel [{}] being destroyed", + LogDebug("Empty temporary password protected channel [{}] being destroyed", CurrentChannel->GetName().c_str()); RemoveChannel(CurrentChannel); @@ -597,7 +602,7 @@ void ChatChannel::AddInvitee(const std::string &Invitee) if (!IsInvitee(Invitee)) { Invitees.push_back(Invitee); - LogInfo("Added [{}] as invitee to channel [{}]", Invitee.c_str(), Name.c_str()); + LogDebug("Added [{}] as invitee to channel [{}]", Invitee.c_str(), Name.c_str()); } } @@ -608,7 +613,7 @@ void ChatChannel::RemoveInvitee(std::string Invitee) if(it != std::end(Invitees)) { Invitees.erase(it); - LogInfo("Removed [{}] as invitee to channel [{}]", Invitee.c_str(), Name.c_str()); + LogDebug("Removed [{}] as invitee to channel [{}]", Invitee.c_str(), Name.c_str()); } } diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index 429f28fbf..0b1d43262 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -235,7 +235,7 @@ std::vector ParseRecipients(std::string RecipientString) { static void ProcessMailTo(Client *c, std::string MailMessage) { - LogInfo("MAILTO: From [{}], [{}]", c->MailBoxName().c_str(), MailMessage.c_str()); + LogDebug("MAILTO: From [{}], [{}]", c->MailBoxName().c_str(), MailMessage.c_str()); std::vector Recipients; @@ -304,7 +304,7 @@ static void ProcessMailTo(Client *c, std::string MailMessage) { if (!database.SendMail(Recipient, c->MailBoxName(), Subject, Body, RecipientsString)) { - LogInfo("Failed in SendMail([{}], [{}], [{}], [{}])", Recipient.c_str(), + LogError("Failed in SendMail([{}], [{}], [{}], [{}])", Recipient.c_str(), c->MailBoxName().c_str(), Subject.c_str(), RecipientsString.c_str()); int PacketLength = 10 + Recipient.length() + Subject.length(); @@ -556,6 +556,17 @@ void Client::CloseConnection() { ClientStream->ReleaseFromUse(); } +void Clientlist::CheckForStaleConnectionsAll() +{ + LogDebug("Checking for stale connections"); + + auto it = ClientChatConnections.begin(); + while (it != ClientChatConnections.end()) { + (*it)->SendKeepAlive(); + ++it; + } +} + void Clientlist::CheckForStaleConnections(Client *c) { if (!c) return; @@ -634,10 +645,12 @@ void Clientlist::Process() // std::string::size_type LastPeriod = MailBoxString.find_last_of("."); - if (LastPeriod == std::string::npos) + if (LastPeriod == std::string::npos) { CharacterName = MailBoxString; - else + } + else { CharacterName = MailBoxString.substr(LastPeriod + 1); + } LogInfo("Received login for user [{}] with key [{}]", MailBox, Key); @@ -652,8 +665,9 @@ void Clientlist::Process() database.GetAccountStatus((*it)); - if ((*it)->GetConnectionType() == ConnectionTypeCombined) + if ((*it)->GetConnectionType() == ConnectionTypeCombined) { (*it)->SendFriends(); + } (*it)->SendMailBoxes(); @@ -865,7 +879,9 @@ void Clientlist::CloseAllConnections() { void Client::AddCharacter(int CharID, const char *CharacterName, int Level) { if (!CharacterName) return; - LogInfo("Adding character [{}] with ID [{}] for [{}]", CharacterName, CharID, GetName().c_str()); + + LogDebug("Adding character [{}] with ID [{}] for [{}]", CharacterName, CharID, GetName().c_str()); + CharacterEntry NewCharacter; NewCharacter.CharID = CharID; NewCharacter.Name = CharacterName; @@ -874,6 +890,10 @@ void Client::AddCharacter(int CharID, const char *CharacterName, int Level) { Characters.push_back(NewCharacter); } +void Client::SendKeepAlive() { + QueuePacket(new EQApplicationPacket(OP_SessionReady, 0)); +} + void Client::SendMailBoxes() { int Count = Characters.size(); @@ -930,7 +950,7 @@ void Client::AddToChannelList(ChatChannel *JoinedChannel) { for (int i = 0; i < MAX_JOINED_CHANNELS; i++) if (JoinedChannels[i] == nullptr) { JoinedChannels[i] = JoinedChannel; - LogInfo("Added Channel [{}] to slot [{}] for [{}]", JoinedChannel->GetName().c_str(), i + 1, GetName().c_str()); + LogDebug("Added Channel [{}] to slot [{}] for [{}]", JoinedChannel->GetName().c_str(), i + 1, GetName().c_str()); return; } } @@ -2346,18 +2366,17 @@ void Client::SendFriends() { } } -std::string Client::MailBoxName() { +std::string Client::MailBoxName() +{ + if ((Characters.empty()) || (CurrentMailBox > (Characters.size() - 1))) { + LogDebug("MailBoxName() called with CurrentMailBox set to [{}] and Characters.size() is [{}]", + CurrentMailBox, Characters.size()); - if ((Characters.empty()) || (CurrentMailBox > (Characters.size() - 1))) - { - LogInfo("MailBoxName() called with CurrentMailBox set to [{}] and Characters.size() is [{}]", - CurrentMailBox, Characters.size()); - - return ""; + return std::string(); } - LogInfo("MailBoxName() called with CurrentMailBox set to [{}] and Characters.size() is [{}]", - CurrentMailBox, Characters.size()); + LogDebug("MailBoxName() called with CurrentMailBox set to [{}] and Characters.size() is [{}]", + CurrentMailBox, Characters.size()); return Characters[CurrentMailBox].Name; diff --git a/ucs/clientlist.h b/ucs/clientlist.h index 6021c5c0e..548fb170c 100644 --- a/ucs/clientlist.h +++ b/ucs/clientlist.h @@ -143,7 +143,7 @@ public: void SetConnectionType(char c); ConnectionType GetConnectionType() { return TypeOfConnection; } EQEmu::versions::ClientVersion GetClientVersion() { return ClientVersion_; } - + inline bool IsMailConnection() { return (TypeOfConnection == ConnectionTypeMail) || (TypeOfConnection == ConnectionTypeCombined); } void SendNotification(int MailBoxNumber, std::string From, std::string Subject, int MessageID); void ChangeMailBox(int NewMailBox); @@ -151,6 +151,7 @@ public: void SendFriends(); int GetCharID(); void SendUptime(); + void SendKeepAlive(); private: unsigned int CurrentMailBox; @@ -183,6 +184,7 @@ public: void Process(); void CloseAllConnections(); Client *FindCharacter(std::string CharacterName); + void CheckForStaleConnectionsAll(); void CheckForStaleConnections(Client *c); Client *IsCharacterOnline(std::string CharacterName); void ProcessOPMailCommand(Client *c, std::string CommandString); diff --git a/ucs/database.cpp b/ucs/database.cpp index d756f1ff4..108b17871 100644 --- a/ucs/database.cpp +++ b/ucs/database.cpp @@ -108,7 +108,7 @@ void Database::GetAccountStatus(Client *client) { std::string query = StringFormat( - "SELECT `status`, `hideme`, `karma`, `revoked` FROM `account` WHERE `id` = '%i' LIMIT 1", + "SELECT `status`, `hideme`, `karma`, `revoked` FROM `account` WHERE `id` = %i LIMIT 1", client->GetAccountID() ); @@ -173,7 +173,7 @@ int Database::FindAccount(const char *characterName, Client *client) query = StringFormat( "SELECT `id`, `name`, `level` FROM `character_data` " - "WHERE `account_id` = %i AND `name` != '%s'", + "WHERE `account_id` = %i AND `name` != '%s' AND deleted_at is NULL", accountID, characterName ); @@ -320,7 +320,7 @@ void Database::SendHeaders(Client *client) int unknownField3 = 1; int characterID = FindCharacter(client->MailBoxName().c_str()); - LogInfo("Sendheaders for [{}], CharID is [{}]", client->MailBoxName().c_str(), characterID); + LogDebug("Sendheaders for [{}], CharID is [{}]", client->MailBoxName().c_str(), characterID); if (characterID <= 0) { return; diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index 0f396252b..46bda530c 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -70,17 +70,18 @@ int main() { // Check every minute for unused channels we can delete // Timer ChannelListProcessTimer(60000); + Timer ClientConnectionPruneTimer(60000); Timer InterserverTimer(INTERSERVER_TIMER); // does auto-reconnect LogInfo("Starting EQEmu Universal Chat Server"); - if (!ucsconfig::LoadConfig()) { - LogInfo("Loading server configuration failed"); + if (!ucsconfig::LoadConfig()) { + LogInfo("Loading server configuration failed"); return 1; } - Config = ucsconfig::get(); + Config = ucsconfig::get(); WorldShortName = Config->ShortName; @@ -144,19 +145,26 @@ int main() { worldserver = new WorldServer; - while(RunLoops) { + auto loop_fn = [&](EQ::Timer* t) { Timer::SetCurrentTime(); g_Clientlist->Process(); - if(ChannelListProcessTimer.Check()) + if (ChannelListProcessTimer.Check()) { ChannelList->Process(); + } - EQ::EventLoop::Get().Process(); + if (ClientConnectionPruneTimer.Check()) { + g_Clientlist->CheckForStaleConnectionsAll(); + } - Sleep(5); - } + }; + + EQ::Timer process_timer(loop_fn); + process_timer.Start(32, true); + + EQ::EventLoop::Get().Run(); ChannelList->RemoveAllChannels(); diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp index 72865e416..107624a0d 100644 --- a/ucs/worldserver.cpp +++ b/ucs/worldserver.cpp @@ -61,7 +61,7 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) ServerPacket tpack(opcode, p); ServerPacket *pack = &tpack; - LogInfo("Received Opcode: {:#04x}", opcode); + LogNetcode("Received Opcode: {:#04x}", opcode); switch (opcode) { diff --git a/world/ucs.cpp b/world/ucs.cpp index f5910da80..cde94b8d9 100644 --- a/world/ucs.cpp +++ b/world/ucs.cpp @@ -6,29 +6,38 @@ #include "../common/misc_functions.h" #include "../common/md5.h" #include "../common/packet_dump.h" +#include "../common/event/timer.h" UCSConnection::UCSConnection() { - Stream = 0; + connection = 0; } void UCSConnection::SetConnection(std::shared_ptr inStream) { - if (Stream && Stream->Handle()) - { + if (inStream && connection && connection->Handle()) { LogInfo("Incoming UCS Connection while we were already connected to a UCS"); - Stream->Handle()->Disconnect(); + connection->Handle()->Disconnect(); + } + + connection = inStream; + if (connection) { + connection->OnMessage( + std::bind( + &UCSConnection::ProcessPacket, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); } - Stream = inStream; - if (Stream) { - Stream->OnMessage(std::bind(&UCSConnection::ProcessPacket, this, std::placeholders::_1, std::placeholders::_2)); - } + m_keepalive.reset(new EQ::Timer(5000, true, std::bind(&UCSConnection::OnKeepAlive, this, std::placeholders::_1))); } void UCSConnection::ProcessPacket(uint16 opcode, EQ::Net::Packet &p) { - if (!Stream) + if (!connection) return; ServerPacket tpack(opcode, p); @@ -60,10 +69,10 @@ void UCSConnection::ProcessPacket(uint16 opcode, EQ::Net::Packet &p) void UCSConnection::SendPacket(ServerPacket* pack) { - if (!Stream) + if (!connection) return; - Stream->SendPacket(pack); + connection->SendPacket(pack); } void UCSConnection::SendMessage(const char *From, const char *Message) @@ -78,3 +87,13 @@ void UCSConnection::SendMessage(const char *From, const char *Message) SendPacket(pack); safe_delete(pack); } + +void UCSConnection::OnKeepAlive(EQ::Timer *t) +{ + if (!connection) { + return; + } + + ServerPacket pack(ServerOP_KeepAlive, 0); + connection->SendPacket(&pack); +} diff --git a/world/ucs.h b/world/ucs.h index d2051c0be..c32872ccb 100644 --- a/world/ucs.h +++ b/world/ucs.h @@ -4,6 +4,7 @@ #include "../common/types.h" #include "../common/net/servertalk_server_connection.h" #include "../common/servertalk.h" +#include "../common/event/timer.h" #include class UCSConnection @@ -13,11 +14,17 @@ public: void SetConnection(std::shared_ptr connection); void ProcessPacket(uint16 opcode, EQ::Net::Packet &p); void SendPacket(ServerPacket* pack); - void Disconnect() { if(Stream && Stream->Handle()) Stream->Handle()->Disconnect(); } + void Disconnect() { if(connection && connection->Handle()) connection->Handle()->Disconnect(); } void SendMessage(const char *From, const char *Message); private: - inline std::string GetIP() const { return (Stream && Stream->Handle()) ? Stream->Handle()->RemoteIP() : 0; } - std::shared_ptr Stream; + inline std::string GetIP() const { return (connection && connection->Handle()) ? connection->Handle()->RemoteIP() : 0; } + std::shared_ptr connection; + + /** + * Keepalive + */ + std::unique_ptr m_keepalive; + void OnKeepAlive(EQ::Timer *t); }; #endif /*UCS_H_*/ diff --git a/zone/client.cpp b/zone/client.cpp index 4a7a66954..e730b32ee 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -256,6 +256,7 @@ Client::Client(EQStreamInterface* ieqs) TotalSecondsPlayed = 0; keyring.clear(); bind_sight_target = nullptr; + p_raid_instance = nullptr; mercid = 0; mercSlot = 0; InitializeMercInfo(); diff --git a/zone/client.h b/zone/client.h index 0986a31f5..be842ff50 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1304,6 +1304,8 @@ public: void SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update); glm::vec4 &GetLastPositionBeforeBulkUpdate(); + Raid *p_raid_instance; + protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); @@ -1343,6 +1345,7 @@ protected: char *adv_data; private: + eqFilterMode ClientFilters[_FilterCount]; int32 HandlePacket(const EQApplicationPacket *app); void OPTGB(const EQApplicationPacket *app); diff --git a/zone/entity.cpp b/zone/entity.cpp index b3805ad63..23ff268fc 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -498,7 +498,7 @@ void EntityList::MobProcess() size_t sz = mob_list.size(); #ifdef IDLE_WHEN_EMPTY - if (numclients > 0 || + if (numclients > 0 || mob->GetWanderType() == 4 || mob->GetWanderType() == 6) { // Normal processing, or assuring that spawns that should // path and depop do that. Otherwise all of these type mobs @@ -931,12 +931,12 @@ bool EntityList::MakeDoorSpawnPacket(EQApplicationPacket *app, Client *client) memcpy(new_door.name, door->GetDoorName(), 32); auto position = door->GetPosition(); - + new_door.xPos = position.x; new_door.yPos = position.y; new_door.zPos = position.z; new_door.heading = position.w; - + new_door.incline = door->GetIncline(); new_door.size = door->GetSize(); new_door.doorId = door->GetDoorID(); @@ -1984,17 +1984,26 @@ Raid *EntityList::GetRaidByID(uint32 id) Raid *EntityList::GetRaidByClient(Client* client) { - std::list::iterator iterator; + if (client->p_raid_instance) { + return client->p_raid_instance; + } + std::list::iterator iterator; iterator = raid_list.begin(); while (iterator != raid_list.end()) { - for (int x = 0; x < MAX_RAID_MEMBERS; x++) - if ((*iterator)->members[x].member) - if((*iterator)->members[x].member == client) + for (auto &member : (*iterator)->members) { + if (member.member) { + if (member.member == client) { + client->p_raid_instance = *iterator; return *iterator; + } + } + } + ++iterator; } + return nullptr; } diff --git a/zone/raids.cpp b/zone/raids.cpp index a4e1535d3..428b17843 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -177,6 +177,7 @@ void Raid::RemoveMember(const char *characterName) if(client) { client->SetRaidGrouped(false); client->LeaveRaidXTargets(this); + client->p_raid_instance = nullptr; } auto pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct)); @@ -1078,8 +1079,9 @@ void Raid::SendRaidRemoveAll(const char *who) void Raid::SendRaidDisband(Client *to) { - if(!to) + if (!to) { return; + } auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; @@ -1614,7 +1616,7 @@ void Raid::SendHPManaEndPacketsFrom(Mob *mob) return; uint32 group_id = 0; - + if(mob->IsClient()) group_id = this->GetGroup(mob->CastToClient()); @@ -1622,7 +1624,7 @@ void Raid::SendHPManaEndPacketsFrom(Mob *mob) EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); mob->CreateHPPacket(&hpapp); - + for(int x = 0; x < MAX_RAID_MEMBERS; x++) { if(members[x].member) { if(!mob->IsClient() || ((members[x].member != mob->CastToClient()) && (members[x].GroupNumber == group_id))) { @@ -1633,7 +1635,7 @@ void Raid::SendHPManaEndPacketsFrom(Mob *mob) mana_update->spawn_id = mob->GetID(); mana_update->mana = mob->GetManaPercent(); members[x].member->QueuePacket(&outapp, false); - + outapp.SetOpcode(OP_MobEnduranceUpdate); MobEnduranceUpdate_Struct *endurance_update = (MobEnduranceUpdate_Struct *)outapp.pBuffer; endurance_update->endurance = mob->GetEndurancePercent(); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 87cfb13ef..4b2034067 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1881,9 +1881,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } case ServerOP_UCSServerStatusReply: { - auto ucsss = (UCSServerStatus_Struct*)pack->pBuffer; - if (zone) + auto ucsss = (UCSServerStatus_Struct *) pack->pBuffer; + if (zone) { zone->SetUCSServerAvailable((ucsss->available != 0), ucsss->timestamp); + LogInfo("UCS Server is now [{}]", (ucsss->available == 1 ? "online" : "offline")); + } break; } case ServerOP_CZSetEntityVariableByNPCTypeID: diff --git a/zone/zone.cpp b/zone/zone.cpp index 55de2b291..398c985c6 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -734,7 +734,7 @@ void Zone::Shutdown(bool quiet) if (RuleB(Zone, KillProcessOnDynamicShutdown)) { LogInfo("[KillProcessOnDynamicShutdown] Shutting down"); - std::exit(EXIT_SUCCESS); + EQ::EventLoop::Get().Shutdown(); } } From 15f3697df166295ef3e7aa27fce73a0df7013868 Mon Sep 17 00:00:00 2001 From: Ali Date: Fri, 17 Apr 2020 10:56:43 +0300 Subject: [PATCH 27/28] Fix edge case with NPC pet owners charming PCs * Addresses #1036 * Cleaned up if statement formatting * Using Mob::GetOwnerOrSelf() now, which accounts for the edge case NB: The Mob::SpellOnTarget() and Mob::CommonDamage() methods really should be looked at and spell logic combined somehow. Both have if statements that dodge around the other's conditions to decide which method sends the CombatDamage_Struct packet --- zone/spells.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index be1419caa..d9378a23c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3979,6 +3979,8 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r cd->hit_heading = action->hit_heading; cd->hit_pitch = action->hit_pitch; cd->damage = 0; + + auto spellOwner = GetOwnerOrSelf(); if(!IsEffectInSpell(spell_id, SE_BindAffinity) && !is_damage_or_lifetap_spell){ entity_list.QueueCloseClients( spelltar, /* Sender */ @@ -3989,14 +3991,8 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r true, /* Packet ACK */ (spelltar->IsClient() ? FilterPCSpells : FilterNPCSpells) /* Message Filter Type: (8 or 9) */ ); - } else if (is_damage_or_lifetap_spell && - (IsClient() || - (HasOwner() && - GetOwner()->IsClient() - ) - ) - ) { - (HasOwner() ? GetOwner() : this)->CastToClient()->QueuePacket( + } else if (is_damage_or_lifetap_spell && spellOwner->IsClient()) { + spellOwner->CastToClient()->QueuePacket( message_packet, true, Mob::CLIENT_CONNECTINGALL, From b8e7e5bb0dd0ce85d1ab8965d6886bc4dfb2cf2c Mon Sep 17 00:00:00 2001 From: Ali Date: Fri, 17 Apr 2020 18:46:07 +0300 Subject: [PATCH 28/28] [skip ci] Added a VS ignore for x64 compiles --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b8ec6881f..1db525804 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,5 @@ cmake-build-debug/ libs/ bin/ /Win32 +/x64 /client_files/**/CMakeFiles/