From 3ec500244e558325f4af28cd84feb183e069203b Mon Sep 17 00:00:00 2001 From: kentai Date: Tue, 12 Mar 2019 19:21:30 +1100 Subject: [PATCH 01/60] Added bot commands ^bottitle, ^botsuffix --- changelog.txt | 7 ++++- zone/bot.cpp | 4 ++- zone/bot.h | 7 +++++ zone/bot_command.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++ zone/bot_command.h | 2 ++ zone/bot_database.cpp | 9 ++++-- 6 files changed, 89 insertions(+), 4 deletions(-) diff --git a/changelog.txt b/changelog.txt index 2ea309812..3535cb982 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- - +== 3/12/2019 == +KentaiVZ: Added two new bot commands to make use of the already present database fields. + + - ^bottitle - Gives targeted bot a custom title ( I.E Pathfinder ) + - ^botsuffix - Gives targeted bot a custom suffix ( I.E of The Grand Creation ) + == 3/1/2019 == Noudess: Major faction conversion to use client data. diff --git a/zone/bot.cpp b/zone/bot.cpp index b221620e2..c726aa841 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3259,7 +3259,7 @@ bool Bot::Spawn(Client* botCharacterOwner) { else this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName()); - // Spawn the bot at the bow owner's loc + // Spawn the bot at the bot owner's loc this->m_Position.x = botCharacterOwner->GetX(); this->m_Position.y = botCharacterOwner->GetY(); this->m_Position.z = botCharacterOwner->GetZ(); @@ -3364,6 +3364,8 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.helm = helmtexture; //(GetShowHelm() ? helmtexture : 0); //0xFF; ns->spawn.equip_chest2 = texture; //0xFF; ns->spawn.show_name = true; + strcpy(ns->spawn.title, GetTitle().c_str()); + strcpy(ns->spawn.suffix, GetSuffix().c_str()); const EQEmu::ItemData* item = nullptr; const EQEmu::ItemInstance* inst = nullptr; uint32 spawnedbotid = 0; diff --git a/zone/bot.h b/zone/bot.h index 602b2912d..a70b32cc5 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -272,6 +272,11 @@ public: bool HasOrMayGetAggro(); void SetDefaultBotStance(); + void setTitle(std::string bot_title) { title = bot_title; } + void setSuffix(std::string bot_suffix) { suffix = bot_suffix; } + std::string GetTitle() { return title; } + std::string GetSuffix() { return suffix; } + inline virtual int32 GetMaxStat(); inline virtual int32 GetMaxResist(); inline virtual int32 GetMaxSTR(); @@ -651,6 +656,8 @@ private: uint32 _guildId; uint8 _guildRank; std::string _guildName; + std::string title; + std::string suffix; uint32 _lastZoneId; bool _rangerAutoWeaponSelect; BotRoleType _botRole; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 085a730ea..b0aa9ae29 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1352,6 +1352,8 @@ int bot_command_init(void) bot_command_add("botstopmeleelevel", "Sets the level a caster or spell-casting fighter bot will stop melee combat", 0, bot_subcommand_bot_stop_melee_level) || bot_command_add("botsummon", "Summons bot(s) to your location", 0, bot_subcommand_bot_summon) || bot_command_add("bottattoo", "Changes the Drakkin tattoo of a bot", 0, bot_subcommand_bot_tattoo) || + bot_command_add("bottitle", "For changing the bot title", 0, bot_subcommand_bot_title) || + bot_command_add("botsuffix", "For changing the bot suffix", 0, bot_subcommand_bot_suffix) || bot_command_add("bottogglearcher", "Toggles a archer bot between melee and ranged weapon use", 0, bot_subcommand_bot_toggle_archer) || bot_command_add("bottogglehelm", "Toggles the helm visibility of a bot between shown and hidden", 0, bot_subcommand_bot_toggle_helm) || bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", 0, bot_subcommand_bot_update) || @@ -5540,6 +5542,68 @@ void bot_subcommand_bot_update(Client *c, const Seperator *sep) c->Message(m_action, "Updated %i of your %i spawned bots", bot_count, sbl.size()); } +void bot_subcommand_bot_title(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) { + c->Message(m_fail, "You must specify a [title] to use this command"); + return; + } + auto my_bot = ActionableBots::AsTarget_ByBot(c); + if (!my_bot) { + c->Message(m_fail, "You must a bot that you own to use this command"); + return; + } + + if (strlen(sep->arg[1]) > 31) { + c->Message(13, "Title must be 31 characters or less."); + return; + } + + std::string bot_title = sep->arg[1]; + + my_bot->setTitle(bot_title); + if (!botdb.SaveBot(my_bot)) { + c->Message(m_fail, BotDatabase::fail::SaveBot()); + return; + } + else { + my_bot->CastToClient()->SetAATitle(bot_title.c_str()); + c->Message(m_action, "Bot Saved."); + } +} + +void bot_subcommand_bot_suffix(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) { + c->Message(m_fail, "You must specify a [suffix] to use this command (use underscores for any spaces)"); + return; + } + auto my_bot = ActionableBots::AsTarget_ByBot(c); + if (!my_bot) { + c->Message(m_fail, "You must a bot that you own to use this command"); + return; + } + + if (strlen(sep->arg[1]) > 31) { + c->Message(13, "Suffix must be 31 characters or less."); + return; + } + + std::string bot_suffix = sep->arg[1]; + std::replace(bot_suffix.begin(), bot_suffix.end(), '_', ' '); + bot_suffix = bot_suffix.substr(0, 31); + my_bot->setSuffix(bot_suffix); + + if (!botdb.SaveBot(my_bot)) { + c->Message(m_fail, BotDatabase::fail::SaveBot()); + return; + } + else { + my_bot->CastToClient()->SetTitleSuffix(bot_suffix.c_str()); + c->Message(m_action, "Bot Saved."); + } +} + void bot_subcommand_bot_woad(Client *c, const Seperator *sep) { if (helper_command_alias_fail(c, "bot_subcommand_bot_woad", sep->arg[0], "botwoad")) diff --git a/zone/bot_command.h b/zone/bot_command.h index 603852b38..3cad47523 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -593,6 +593,8 @@ void bot_command_water_breathing(Client *c, const Seperator *sep); // bot subcommands void bot_subcommand_bot_appearance(Client *c, const Seperator *sep); +void bot_subcommand_bot_title(Client *c, const Seperator *sep); +void bot_subcommand_bot_suffix(Client *c, const Seperator *sep); void bot_subcommand_bot_beard_color(Client *c, const Seperator *sep); void bot_subcommand_bot_beard_style(Client *c, const Seperator *sep); void bot_subcommand_bot_camp(Client *c, const Seperator *sep); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index ea5154598..0d4c72a23 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -395,7 +395,8 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) loaded_bot = new Bot(bot_id, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct); if (loaded_bot) { loaded_bot->SetShowHelm((atoi(row[43]) > 0 ? true : false)); - + loaded_bot->setTitle(row[4]); + loaded_bot->setSuffix(row[5]); uint32 bfd = atoi(row[44]); if (bfd < 1) bfd = 1; @@ -604,7 +605,9 @@ bool BotDatabase::SaveBot(Bot* bot_inst) " `corruption` = '%i'," " `show_helm` = '%i'," " `follow_distance` = '%i'," - " `stop_melee_level` = '%u'" + " `stop_melee_level` = '%u'," + " `title` = '%s'," + " `suffix` = '%s'" " WHERE `bot_id` = '%u'", bot_inst->GetBotOwnerCharacterID(), bot_inst->GetBotSpellID(), @@ -647,6 +650,8 @@ bool BotDatabase::SaveBot(Bot* bot_inst) ((bot_inst->GetShowHelm()) ? (1) : (0)), bot_inst->GetFollowDistance(), bot_inst->GetStopMeleeLevel(), + bot_inst->GetTitle().c_str(), + bot_inst->GetSuffix().c_str(), bot_inst->GetBotID() ); auto results = QueryDatabase(query); From b2dd3df1e2017774a6612a9074b6e7a4e8776db7 Mon Sep 17 00:00:00 2001 From: kentai Date: Sat, 16 Mar 2019 17:16:49 +1100 Subject: [PATCH 02/60] Revert "Added bot commands" This reverts commit 3ec500244e558325f4af28cd84feb183e069203b. --- changelog.txt | 7 +---- zone/bot.cpp | 4 +-- zone/bot.h | 7 ----- zone/bot_command.cpp | 64 ------------------------------------------- zone/bot_command.h | 2 -- zone/bot_database.cpp | 9 ++---- 6 files changed, 4 insertions(+), 89 deletions(-) diff --git a/changelog.txt b/changelog.txt index 3535cb982..2ea309812 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,11 +1,6 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- -== 3/12/2019 == -KentaiVZ: Added two new bot commands to make use of the already present database fields. - - - ^bottitle - Gives targeted bot a custom title ( I.E Pathfinder ) - - ^botsuffix - Gives targeted bot a custom suffix ( I.E of The Grand Creation ) - + == 3/1/2019 == Noudess: Major faction conversion to use client data. diff --git a/zone/bot.cpp b/zone/bot.cpp index c726aa841..b221620e2 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3259,7 +3259,7 @@ bool Bot::Spawn(Client* botCharacterOwner) { else this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName()); - // Spawn the bot at the bot owner's loc + // Spawn the bot at the bow owner's loc this->m_Position.x = botCharacterOwner->GetX(); this->m_Position.y = botCharacterOwner->GetY(); this->m_Position.z = botCharacterOwner->GetZ(); @@ -3364,8 +3364,6 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.helm = helmtexture; //(GetShowHelm() ? helmtexture : 0); //0xFF; ns->spawn.equip_chest2 = texture; //0xFF; ns->spawn.show_name = true; - strcpy(ns->spawn.title, GetTitle().c_str()); - strcpy(ns->spawn.suffix, GetSuffix().c_str()); const EQEmu::ItemData* item = nullptr; const EQEmu::ItemInstance* inst = nullptr; uint32 spawnedbotid = 0; diff --git a/zone/bot.h b/zone/bot.h index a70b32cc5..602b2912d 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -272,11 +272,6 @@ public: bool HasOrMayGetAggro(); void SetDefaultBotStance(); - void setTitle(std::string bot_title) { title = bot_title; } - void setSuffix(std::string bot_suffix) { suffix = bot_suffix; } - std::string GetTitle() { return title; } - std::string GetSuffix() { return suffix; } - inline virtual int32 GetMaxStat(); inline virtual int32 GetMaxResist(); inline virtual int32 GetMaxSTR(); @@ -656,8 +651,6 @@ private: uint32 _guildId; uint8 _guildRank; std::string _guildName; - std::string title; - std::string suffix; uint32 _lastZoneId; bool _rangerAutoWeaponSelect; BotRoleType _botRole; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index b0aa9ae29..085a730ea 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1352,8 +1352,6 @@ int bot_command_init(void) bot_command_add("botstopmeleelevel", "Sets the level a caster or spell-casting fighter bot will stop melee combat", 0, bot_subcommand_bot_stop_melee_level) || bot_command_add("botsummon", "Summons bot(s) to your location", 0, bot_subcommand_bot_summon) || bot_command_add("bottattoo", "Changes the Drakkin tattoo of a bot", 0, bot_subcommand_bot_tattoo) || - bot_command_add("bottitle", "For changing the bot title", 0, bot_subcommand_bot_title) || - bot_command_add("botsuffix", "For changing the bot suffix", 0, bot_subcommand_bot_suffix) || bot_command_add("bottogglearcher", "Toggles a archer bot between melee and ranged weapon use", 0, bot_subcommand_bot_toggle_archer) || bot_command_add("bottogglehelm", "Toggles the helm visibility of a bot between shown and hidden", 0, bot_subcommand_bot_toggle_helm) || bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", 0, bot_subcommand_bot_update) || @@ -5542,68 +5540,6 @@ void bot_subcommand_bot_update(Client *c, const Seperator *sep) c->Message(m_action, "Updated %i of your %i spawned bots", bot_count, sbl.size()); } -void bot_subcommand_bot_title(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) { - c->Message(m_fail, "You must specify a [title] to use this command"); - return; - } - auto my_bot = ActionableBots::AsTarget_ByBot(c); - if (!my_bot) { - c->Message(m_fail, "You must a bot that you own to use this command"); - return; - } - - if (strlen(sep->arg[1]) > 31) { - c->Message(13, "Title must be 31 characters or less."); - return; - } - - std::string bot_title = sep->arg[1]; - - my_bot->setTitle(bot_title); - if (!botdb.SaveBot(my_bot)) { - c->Message(m_fail, BotDatabase::fail::SaveBot()); - return; - } - else { - my_bot->CastToClient()->SetAATitle(bot_title.c_str()); - c->Message(m_action, "Bot Saved."); - } -} - -void bot_subcommand_bot_suffix(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) { - c->Message(m_fail, "You must specify a [suffix] to use this command (use underscores for any spaces)"); - return; - } - auto my_bot = ActionableBots::AsTarget_ByBot(c); - if (!my_bot) { - c->Message(m_fail, "You must a bot that you own to use this command"); - return; - } - - if (strlen(sep->arg[1]) > 31) { - c->Message(13, "Suffix must be 31 characters or less."); - return; - } - - std::string bot_suffix = sep->arg[1]; - std::replace(bot_suffix.begin(), bot_suffix.end(), '_', ' '); - bot_suffix = bot_suffix.substr(0, 31); - my_bot->setSuffix(bot_suffix); - - if (!botdb.SaveBot(my_bot)) { - c->Message(m_fail, BotDatabase::fail::SaveBot()); - return; - } - else { - my_bot->CastToClient()->SetTitleSuffix(bot_suffix.c_str()); - c->Message(m_action, "Bot Saved."); - } -} - void bot_subcommand_bot_woad(Client *c, const Seperator *sep) { if (helper_command_alias_fail(c, "bot_subcommand_bot_woad", sep->arg[0], "botwoad")) diff --git a/zone/bot_command.h b/zone/bot_command.h index 3cad47523..603852b38 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -593,8 +593,6 @@ void bot_command_water_breathing(Client *c, const Seperator *sep); // bot subcommands void bot_subcommand_bot_appearance(Client *c, const Seperator *sep); -void bot_subcommand_bot_title(Client *c, const Seperator *sep); -void bot_subcommand_bot_suffix(Client *c, const Seperator *sep); void bot_subcommand_bot_beard_color(Client *c, const Seperator *sep); void bot_subcommand_bot_beard_style(Client *c, const Seperator *sep); void bot_subcommand_bot_camp(Client *c, const Seperator *sep); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 0d4c72a23..ea5154598 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -395,8 +395,7 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) loaded_bot = new Bot(bot_id, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct); if (loaded_bot) { loaded_bot->SetShowHelm((atoi(row[43]) > 0 ? true : false)); - loaded_bot->setTitle(row[4]); - loaded_bot->setSuffix(row[5]); + uint32 bfd = atoi(row[44]); if (bfd < 1) bfd = 1; @@ -605,9 +604,7 @@ bool BotDatabase::SaveBot(Bot* bot_inst) " `corruption` = '%i'," " `show_helm` = '%i'," " `follow_distance` = '%i'," - " `stop_melee_level` = '%u'," - " `title` = '%s'," - " `suffix` = '%s'" + " `stop_melee_level` = '%u'" " WHERE `bot_id` = '%u'", bot_inst->GetBotOwnerCharacterID(), bot_inst->GetBotSpellID(), @@ -650,8 +647,6 @@ bool BotDatabase::SaveBot(Bot* bot_inst) ((bot_inst->GetShowHelm()) ? (1) : (0)), bot_inst->GetFollowDistance(), bot_inst->GetStopMeleeLevel(), - bot_inst->GetTitle().c_str(), - bot_inst->GetSuffix().c_str(), bot_inst->GetBotID() ); auto results = QueryDatabase(query); From 51fb7d8b77066dcc1f9c1f075403b6cef99528b9 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 20 Jan 2020 15:38:07 -0500 Subject: [PATCH 03/60] _GetRunSpeed did not correctly report aa mods for Clients. I actually believe this was some old cut-n-paste error. aa_mod was being set to a total of all (3) caps - like the previous line. --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 71e876938..ea0cf761b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -684,7 +684,7 @@ int Mob::_GetRunSpeed() const { int runspeedcap = RuleI(Character,BaseRunSpeedCap); runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap; - aa_mod = itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap; + aa_mod += aabonuses.BaseMovementSpeed + aabonuses.movementspeed; int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed; int movemod = 0; From 63b8df72b2c9b4ac0cc50d755c3eda571bf37d1c Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sun, 26 Jan 2020 20:53:59 -0500 Subject: [PATCH 04/60] Implement consent for group/raid/guild and add Auto Consent support Refactors consent to be more live accurate Message sent to owner and receiver for each zone a corpse is in Corpses now store consent list instead of clients holding corpse list Consent throttling added Message strings and colors updated Removed reporting invalid consent targets --- common/emu_constants.h | 9 ++++ common/servertalk.h | 3 ++ world/zoneserver.cpp | 119 ++++++++--------------------------------- zone/client.cpp | 46 ++++++++++++++++ zone/client.h | 6 ++- zone/client_packet.cpp | 46 ++++++---------- zone/corpse.cpp | 67 ++++++++++++++++++----- zone/corpse.h | 9 ++++ zone/string_ids.h | 4 ++ zone/worldserver.cpp | 89 +++++++++++++++++------------- 10 files changed, 222 insertions(+), 176 deletions(-) diff --git a/common/emu_constants.h b/common/emu_constants.h index c6491b3b3..945f513b5 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -317,6 +317,15 @@ namespace EQEmu QuestControlGrid = -1 }; + namespace consent { + enum eConsentType : uint8 { + Normal = 0, + Group, + Raid, + Guild + }; + }; // namespace consent + } /*EQEmu*/ #endif /*COMMON_EMU_CONSTANTS_H*/ diff --git a/common/servertalk.h b/common/servertalk.h index b9cb6202e..2557941e7 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -866,10 +866,13 @@ struct SpawnPlayerCorpse_Struct { struct ServerOP_Consent_Struct { char grantname[64]; char ownername[64]; + char zonename[32]; uint8 permission; uint32 zone_id; uint16 instance_id; uint32 message_string_id; + uint8 consent_type; // 0 = normal, 1 = group, 2 = raid, 3 = guild + uint32 consent_id; }; struct ReloadTasks_Struct { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index dd55fac39..1bbc7f1fc 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1057,110 +1057,37 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } case ServerOP_Consent: { - // Message string id's likely to be used here are: - // CONSENT_YOURSELF = 399 - // CONSENT_INVALID_NAME = 397 - // TARGET_NOT_FOUND = 101 - ZoneServer* zs; - ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; - ClientListEntry* cle = client_list.FindCharacter(s->grantname); - if (cle) { - if (cle->instance() != 0) - { - zs = zoneserver_list.FindByInstanceID(cle->instance()); - if (zs) { - zs->SendPacket(pack); - } - else - { - auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, s->grantname); - strcpy(scs->ownername, s->ownername); - scs->permission = s->permission; - scs->zone_id = s->zone_id; - scs->instance_id = s->instance_id; - scs->message_string_id = 101; - zs = zoneserver_list.FindByInstanceID(s->instance_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->instance_id); - } - safe_delete(pack); - } - } - else - { - zs = zoneserver_list.FindByZoneID(cle->zone()); - if (zs) { - zs->SendPacket(pack); - } - else { - // send target not found back to requester - auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, s->grantname); - strcpy(scs->ownername, s->ownername); - scs->permission = s->permission; - scs->zone_id = s->zone_id; - scs->message_string_id = 101; - zs = zoneserver_list.FindByZoneID(s->zone_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); - } - safe_delete(pack); - } - } - } - else { - // send target not found back to requester - auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, s->grantname); - strcpy(scs->ownername, s->ownername); - scs->permission = s->permission; - scs->zone_id = s->zone_id; - scs->message_string_id = 397; - zs = zoneserver_list.FindByZoneID(s->zone_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); - } - safe_delete(pack); - } + zoneserver_list.SendPacket(pack); // update corpses in all zones break; } case ServerOP_Consent_Response: { - // Message string id's likely to be used here are: - // CONSENT_YOURSELF = 399 - // CONSENT_INVALID_NAME = 397 - // TARGET_NOT_FOUND = 101 ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; - if (s->instance_id != 0) + + ZoneServer* owner_zs = nullptr; + if ((s->instance_id != 0 && (owner_zs = zoneserver_list.FindByInstanceID(s->instance_id))) || + (s->instance_id == 0 && (owner_zs = zoneserver_list.FindByZoneID(s->zone_id)))) { - ZoneServer* zs = zoneserver_list.FindByInstanceID(s->instance_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->instance_id); - } + owner_zs->SendPacket(pack); } else { - ZoneServer* zs = zoneserver_list.FindByZoneID(s->zone_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); + LogInfo("Unable to locate zone record for zone id [{}] or instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id, s->instance_id); + } + + if (s->consent_type == EQEmu::consent::Normal) + { + // send the message to the client being granted or denied permission + if (ClientListEntry* cle = client_list.FindCharacter(s->grantname)) + { + ZoneServer* granted_zs = nullptr; + if ((cle->instance() != 0 && (granted_zs = zoneserver_list.FindByInstanceID(cle->instance()))) || + (cle->instance() == 0 && (granted_zs = zoneserver_list.FindByZoneID(cle->zone())))) + { + // avoid sending twice if owner and granted are in same zone + if (granted_zs != owner_zs) { + granted_zs->SendPacket(pack); + } + } } } break; diff --git a/zone/client.cpp b/zone/client.cpp index 9ea27869a..f43b4a48e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -165,6 +165,7 @@ Client::Client(EQStreamInterface* ieqs) hp_self_update_throttle_timer(300), hp_other_update_throttle_timer(500), position_update_timer(10000), + consent_throttle_timer(2000), tmSitting(0) { @@ -6254,6 +6255,51 @@ void Client::DragCorpses() } } +void Client::ConsentCorpses(const char* consent_name, bool deny) +{ + if (strcasecmp(consent_name, GetName()) == 0) { + MessageString(Chat::Red, CONSENT_YOURSELF); + } + else if (!consent_throttle_timer.Check()) { + MessageString(Chat::Red, CONSENT_WAIT); + } + else { + auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; + strcpy(scs->grantname, consent_name); + strcpy(scs->ownername, GetName()); + strcpy(scs->zonename, "Unknown"); + scs->message_string_id = 0; + scs->permission = deny ? 0 : 1; + scs->zone_id = zone->GetZoneID(); + scs->instance_id = zone->GetInstanceID(); + scs->consent_type = EQEmu::consent::Normal; + scs->consent_id = 0; + if (strcasecmp(scs->grantname, "group") == 0) { + if (!deny) { + Group* grp = GetGroup(); + scs->consent_id = grp ? grp->GetID() : 0; + } + scs->consent_type = EQEmu::consent::Group; + } + else if (strcasecmp(scs->grantname, "raid") == 0) { + if (!deny) { + Raid* raid = GetRaid(); + scs->consent_id = raid ? raid->GetID() : 0; + } + scs->consent_type = EQEmu::consent::Raid; + } + else if (strcasecmp(scs->grantname, "guild") == 0) { + if (!deny) { + scs->consent_id = GuildID(); + } + scs->consent_type = EQEmu::consent::Guild; + } + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration) { if(!target || !IsValidSpell(spell_id) || this->GetID() == target->GetID()) diff --git a/zone/client.h b/zone/client.h index 4ff431520..7f6a9bd9e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -793,6 +793,9 @@ public: virtual void UpdateEquipmentLight() { m_Light.Type[EQEmu::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQEmu::lightsource::LightEquipment] = EQEmu::lightsource::TypeToLevel(m_Light.Type[EQEmu::lightsource::LightEquipment]); } inline bool AutoSplitEnabled() { return m_pp.autosplit != 0; } + inline bool AutoConsentGroupEnabled() const { return m_pp.groupAutoconsent != 0; } + inline bool AutoConsentRaidEnabled() const { return m_pp.raidAutoconsent != 0; } + inline bool AutoConsentGuildEnabled() const { return m_pp.guildAutoconsent != 0; } void SummonHorse(uint16 spell_id); void SetHorseId(uint16 horseid_in); @@ -957,7 +960,6 @@ public: void EnteringMessages(Client* client); void SendRules(Client* client); - std::list consent_list; const bool GetGMSpeed() const { return (gmspeed > 0); } bool CanUseReport; @@ -1137,6 +1139,7 @@ public: inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } void DragCorpses(); inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } + void ConsentCorpses(const char* consent_name, bool deny = false); void SendAltCurrencies(); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); @@ -1527,6 +1530,7 @@ private: Timer hp_self_update_throttle_timer; /* This is to prevent excessive packet sending under trains/fast combat */ Timer hp_other_update_throttle_timer; /* This is to keep clients from DOSing the server with macros that change client targets constantly */ Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */ + Timer consent_throttle_timer; glm::vec3 m_Proximity; glm::vec4 last_position_before_bulk_update; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ebc6bd38a..09dae201a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4643,43 +4643,16 @@ void Client::Handle_OP_Consent(const EQApplicationPacket *app) { if (app->size<64) { Consent_Struct* c = (Consent_Struct*)app->pBuffer; - if (strcmp(c->name, GetName()) != 0) { - auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, c->name); - strcpy(scs->ownername, GetName()); - scs->message_string_id = 0; - scs->permission = 1; - scs->zone_id = zone->GetZoneID(); - scs->instance_id = zone->GetInstanceID(); - //consent_list.push_back(scs->grantname); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else { - MessageString(Chat::White, CONSENT_YOURSELF); - } + ConsentCorpses(c->name, false); } - return; } void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app) { if (app->size<64) { Consent_Struct* c = (Consent_Struct*)app->pBuffer; - auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, c->name); - strcpy(scs->ownername, GetName()); - scs->message_string_id = 0; - scs->permission = 0; - scs->zone_id = zone->GetZoneID(); - scs->instance_id = zone->GetInstanceID(); - //consent_list.remove(scs->grantname); - worldserver.SendPacket(pack); - safe_delete(pack); + ConsentCorpses(c->name, true); } - return; } void Client::Handle_OP_Consider(const EQApplicationPacket *app) @@ -13315,6 +13288,21 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) entity_list.QueueClients(this, app, true); } } + else if (sa->type == AT_GroupConsent) + { + m_pp.groupAutoconsent = (sa->parameter == 1); + ConsentCorpses("Group", (sa->parameter != 1)); + } + else if (sa->type == AT_RaidConsent) + { + m_pp.raidAutoconsent = (sa->parameter == 1); + ConsentCorpses("Raid", (sa->parameter != 1)); + } + else if (sa->type == AT_GuildConsent) + { + m_pp.guildAutoconsent = (sa->parameter == 1); + ConsentCorpses("Guild", (sa->parameter != 1)); + } else { std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 09661826d..6b52a0c63 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -282,6 +282,12 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( allowed_looters[i] = 0; } + Group* grp = nullptr; + Raid* raid = nullptr; + consent_group_id = (client->AutoConsentGroupEnabled() && (grp = client->GetGroup())) ? grp->GetID() : 0; + consent_raid_id = (client->AutoConsentRaidEnabled() && (raid = client->GetRaid())) ? raid->GetID() : 0; + consent_guild_id = client->AutoConsentGuildEnabled() ? client->GuildID() : 0; + is_corpse_changed = true; rez_experience = in_rezexp; can_corpse_be_rezzed = true; @@ -647,6 +653,25 @@ void Corpse::DepopPlayerCorpse() { player_corpse_depop = true; } +void Corpse::AddConsentName(const char* add_name) +{ + for (const auto& n : consent_names) { + if (strcasecmp(n.c_str(), add_name) == 0) { + return; + } + } + consent_names.emplace_back(add_name); +} + +void Corpse::RemoveConsentName(const char* rem_name) +{ + consent_names.erase(std::remove_if(consent_names.begin(), consent_names.end(), + [rem_name](const std::string& n) { + return strcasecmp(n.c_str(), rem_name) == 0; + } + ), consent_names.end()); +} + uint32 Corpse::CountItems() { return itemlist.size(); } @@ -1434,29 +1459,43 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { is_corpse_changed = true; } else { - client->Message(Chat::White, "Corpse is too far away."); + client->MessageString(Chat::Red, CORPSE_TOO_FAR); return false; } } else { bool consented = false; - std::list::iterator itr; - for(itr = client->consent_list.begin(); itr != client->consent_list.end(); ++itr) { - if(strcmp(this->GetOwnerName(), itr->c_str()) == 0) { - if (!CheckDistance || (DistanceSquaredNoZ(m_Position, client->GetPosition()) <= dist2)) { - GMMove(client->GetX(), client->GetY(), client->GetZ()); - is_corpse_changed = true; - } - else { - client->Message(Chat::White, "Corpse is too far away."); - return false; - } + for (const auto& n : consent_names) { + if (strcasecmp(client->GetName(), n.c_str()) == 0) { + consented = true; + break; + } + } + + if (!consented) { + Group* grp = nullptr; + Raid* raid = nullptr; + if ((consent_guild_id && consent_guild_id != GUILD_NONE && client->GuildID() == consent_guild_id) || + (consent_group_id && (grp = client->GetGroup()) && grp->GetID() == consent_group_id) || + (consent_raid_id && (raid = client->GetRaid()) && raid->GetID() == consent_raid_id)) + { consented = true; } } - if(!consented) { - client->Message(Chat::White, "You do not have permission to move this corpse."); + + if (consented) { + if (!CheckDistance || (DistanceSquaredNoZ(m_Position, client->GetPosition()) <= dist2)) { + GMMove(client->GetX(), client->GetY(), client->GetZ()); + is_corpse_changed = true; + } + else { + client->MessageString(Chat::Red, CORPSE_TOO_FAR); + return false; + } + } + else { + client->MessageString(Chat::Red, CONSENT_DENIED); return false; } } diff --git a/zone/corpse.h b/zone/corpse.h index e7a7c5805..ae365abef 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -74,6 +74,11 @@ class Corpse : public Mob { uint32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); } uint32 GetRezTime() { if (!corpse_rez_timer.Enabled()) return 0; else return corpse_rez_timer.GetRemainingTime(); } void SetDecayTimer(uint32 decay_time); + void SetConsentGroupID(uint32 id) { if (IsPlayerCorpse()) { consent_group_id = id; } } + void SetConsentRaidID(uint32 id) { if (IsPlayerCorpse()) { consent_raid_id = id; } } + void SetConsentGuildID(uint32 id) { if (IsPlayerCorpse()) { consent_guild_id = id; } } + void AddConsentName(const char* name); + void RemoveConsentName(const char* name); void Delete(); void Bury(); @@ -142,6 +147,9 @@ private: int32 player_kill_item; /* Determines if Player Kill Item */ uint32 corpse_db_id; /* Corpse Database ID (Player Corpse) */ uint32 char_id; /* Character ID */ + uint32 consent_group_id = 0; /* consented group id */ + uint32 consent_raid_id = 0; /* consented raid id */ + uint32 consent_guild_id = 0; /* consented guild id */ ItemList itemlist; /* Internal Item list used for corpses */ uint32 copper; uint32 silver; @@ -160,6 +168,7 @@ private: Timer corpse_graveyard_timer; Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */ EQEmu::TintProfile item_tint; + std::vector consent_names; LootRequestType loot_request_type; }; diff --git a/zone/string_ids.h b/zone/string_ids.h index 0948d13a8..7c4066b55 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -122,11 +122,13 @@ #define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one. #define PICK_LORE 379 //You cannot pick up a lore item you already possess. #define POISON_TOO_HIGH 382 // This poison is too high level for you to apply. +#define CORPSE_TOO_FAR 389 //The corpse is too far away to summon. #define CONSENT_DENIED 390 //You do not have consent to summon that corpse. #define DISCIPLINE_RDY 393 //You are ready to use a new discipline now. #define CONSENT_INVALID_NAME 397 //Not a valid consent name. #define CONSENT_NPC 398 //You cannot consent NPC\'s. #define CONSENT_YOURSELF 399 //You cannot consent yourself. +#define CONSENT_WAIT 400 //You must wait 2 seconds between consents. #define SONG_NEEDS_DRUM 405 //You need to play a percussion instrument for this song #define SONG_NEEDS_WIND 406 //You need to play a wind instrument for this song #define SONG_NEEDS_STRINGS 407 //You need to play a stringed instrument for this song @@ -266,6 +268,8 @@ #define REZZ_ALREADY_PENDING 1379 //You were unable to restore the corpse to life, but you may have success with a later attempt. #define IN_USE 1406 //Someone else is using that. Try again later. #define DUEL_FLED 1408 //%1 has defeated %2 in a duel to the death! %3 has fled like a cowardly dog! +#define GIVE_CONSENT 1427 //You have given %1 permission to drag your corpse in %2. +#define DENY_CONSENT 1428 //You have denied %1 permission to drag your corpse in %2. #define MEMBER_OF_YOUR_GUILD 1429 #define OFFICER_OF_YOUR_GUILD 1430 #define LEADER_OF_YOUR_GUILD 1431 diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index a5180963d..4d01f792a 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1442,50 +1442,67 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } case ServerOP_Consent: { ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; - Client* client = entity_list.GetClientByName(s->grantname); - if (client) { - if (s->permission == 1) - client->consent_list.push_back(s->ownername); - else - client->consent_list.remove(s->ownername); - auto outapp = - new EQApplicationPacket(OP_ConsentResponse, sizeof(ConsentResponse_Struct)); - ConsentResponse_Struct* crs = (ConsentResponse_Struct*)outapp->pBuffer; - strcpy(crs->grantname, s->grantname); - strcpy(crs->ownername, s->ownername); - crs->permission = s->permission; - strcpy(crs->zonename, "all zones"); - client->QueuePacket(outapp); - safe_delete(outapp); + bool found_corpse = false; + for (auto const& it : entity_list.GetCorpseList()) { + if (it.second->IsPlayerCorpse() && strcmp(it.second->GetOwnerName(), s->ownername) == 0) { + if (s->consent_type == EQEmu::consent::Normal) { + if (s->permission == 1) { + it.second->AddConsentName(s->grantname); + } + else { + it.second->RemoveConsentName(s->grantname); + } + } + else if (s->consent_type == EQEmu::consent::Group) { + it.second->SetConsentGroupID(s->consent_id); + } + else if (s->consent_type == EQEmu::consent::Raid) { + it.second->SetConsentRaidID(s->consent_id); + } + else if (s->consent_type == EQEmu::consent::Guild) { + it.second->SetConsentGuildID(s->consent_id); + } + found_corpse = true; + } } - else { - // target not found - // Message string id's likely to be used here are: - // CONSENT_YOURSELF = 399 - // CONSENT_INVALID_NAME = 397 - // TARGET_NOT_FOUND = 101 - - auto scs_pack = - new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)scs_pack->pBuffer; - strcpy(scs->grantname, s->grantname); - strcpy(scs->ownername, s->ownername); - scs->permission = s->permission; - scs->zone_id = s->zone_id; - scs->instance_id = s->instance_id; - scs->message_string_id = TARGET_NOT_FOUND; - worldserver.SendPacket(scs_pack); - safe_delete(scs_pack); + if (found_corpse) + { + // forward the grant/deny message for this zone to both owner and granted + auto outapp = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)outapp->pBuffer; + memcpy(outapp->pBuffer, s, sizeof(ServerOP_Consent_Struct)); + if (zone) { + strn0cpy(scs->zonename, zone->GetLongName(), sizeof(scs->zonename)); + } + scs->message_string_id = s->permission ? GIVE_CONSENT : DENY_CONSENT; + worldserver.SendPacket(outapp); + safe_delete(outapp); } break; } case ServerOP_Consent_Response: { ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; - Client* client = entity_list.GetClientByName(s->ownername); - if (client) { - client->MessageString(Chat::White, s->message_string_id); + if (s->consent_type == EQEmu::consent::Normal) + { + if (Client* client = entity_list.GetClientByName(s->grantname)) + { + // send the message to the client being granted or denied permission + auto outapp = new EQApplicationPacket(OP_ConsentResponse, sizeof(ConsentResponse_Struct)); + ConsentResponse_Struct* crs = (ConsentResponse_Struct*)outapp->pBuffer; + strcpy(crs->grantname, s->grantname); + strcpy(crs->ownername, s->ownername); + crs->permission = s->permission; + strn0cpy(crs->zonename, s->zonename, sizeof(crs->zonename)); + client->QueuePacket(outapp); + safe_delete(outapp); + } + } + if (Client* client = entity_list.GetClientByName(s->ownername)) + { + // send owner consent/deny confirmation message + client->MessageString(Chat::White, s->message_string_id, s->grantname, s->zonename); } break; } From 371265d143e58aa88ecbddacb4584f68f675ef61 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Tue, 28 Jan 2020 18:12:24 -0500 Subject: [PATCH 05/60] Make guild consent persistent for summoned corpses Live drops group/raid consent but not guild when moving corpse to another zone Store guild consent id in db for character corpses and keep it updated --- utils/sql/db_update_manifest.txt | 1 + .../2020_01_28_corpse_guild_consent_id.sql | 1 + zone/client.cpp | 2 + zone/corpse.cpp | 7 ++-- zone/corpse.h | 2 +- zone/zonedb.cpp | 39 ++++++++++++------- zone/zonedb.h | 5 ++- 7 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 utils/sql/git/required/2020_01_28_corpse_guild_consent_id.sql diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 53a71e109..8e3172b87 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -401,6 +401,7 @@ 9145|2019_12_24_banned_ips_update.sql|SHOW TABLES LIKE 'Banned_IPs'|not_empty| 9146|2020_01_10_character_soft_deletes.sql|SHOW COLUMNS FROM `character_data` LIKE 'deleted_at'|empty| 9147|2020_01_24_grid_centerpoint_wp.sql|SHOW COLUMNS FROM `grid_entries` LIKE 'centerpoint'|empty| +9148|2020_01_28_corpse_guild_consent_id.sql|SHOW COLUMNS FROM `character_corpses` LIKE 'guild_consent_id'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2020_01_28_corpse_guild_consent_id.sql b/utils/sql/git/required/2020_01_28_corpse_guild_consent_id.sql new file mode 100644 index 000000000..b42f257ea --- /dev/null +++ b/utils/sql/git/required/2020_01_28_corpse_guild_consent_id.sql @@ -0,0 +1 @@ +ALTER TABLE `character_corpses` ADD COLUMN `guild_consent_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `time_of_death`; diff --git a/zone/client.cpp b/zone/client.cpp index f43b4a48e..b6d91602c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6294,6 +6294,8 @@ void Client::ConsentCorpses(const char* consent_name, bool deny) scs->consent_id = GuildID(); } scs->consent_type = EQEmu::consent::Guild; + // update all corpses in db so buried/unloaded corpses see new consent id + database.UpdateCharacterCorpseConsent(CharacterID(), scs->consent_id); } worldserver.SendPacket(pack); safe_delete(pack); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 6b52a0c63..b24a335d2 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -73,7 +73,7 @@ void Corpse::SendLootReqErrorPacket(Client* client, LootResponse response) { safe_delete(outapp); } -Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard) { +Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard, uint32 guild_consent_id) { uint32 item_count = database.GetCharacterCorpseItemCount(in_dbid); auto buffer = new char[sizeof(PlayerCorpse_Struct) + (item_count * sizeof(player_lootitem::ServerLootItem_Struct))]; @@ -138,6 +138,7 @@ Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std: pc->drakkin_details = pcs->drakkin_details; pc->IsRezzed(rezzed); pc->become_npc = false; + pc->consent_guild_id = guild_consent_id; pc->UpdateEquipmentLight(); // itemlist populated above..need to determine actual values @@ -617,11 +618,11 @@ bool Corpse::Save() { /* Create New Corpse*/ if (corpse_db_id == 0) { - corpse_db_id = database.SaveCharacterCorpse(char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position); + corpse_db_id = database.SaveCharacterCorpse(char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, consent_guild_id); } /* Update Corpse Data */ else{ - corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, IsRezzed()); + corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, consent_guild_id, IsRezzed()); } safe_delete_array(dbpc); diff --git a/zone/corpse.h b/zone/corpse.h index ae365abef..02eb707a4 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -48,7 +48,7 @@ class Corpse : public Mob { Corpse(uint32 in_corpseid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, const glm::vec4& position, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture, uint32 in_rezexp, bool wasAtGraveyard = false); ~Corpse(); - static Corpse* LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard); + static Corpse* LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard, uint32 guild_consent_id); /* Corpse: General */ virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) { return true; } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index d5346877d..899861c3d 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -4288,10 +4288,10 @@ uint32 ZoneDatabase::GetCharacterCorpseDecayTimer(uint32 corpse_db_id){ return 0; } -uint32 ZoneDatabase::UpdateCharacterCorpse(uint32 db_id, uint32 char_id, const char* char_name, uint32 zone_id, uint16 instance_id, PlayerCorpse_Struct* dbpc, const glm::vec4& position, bool is_rezzed) { +uint32 ZoneDatabase::UpdateCharacterCorpse(uint32 db_id, uint32 char_id, const char* char_name, uint32 zone_id, uint16 instance_id, PlayerCorpse_Struct* dbpc, const glm::vec4& position, uint32 guild_id, bool is_rezzed) { std::string query = StringFormat("UPDATE `character_corpses` " "SET `charname` = '%s', `zone_id` = %u, `instance_id` = %u, `charid` = %d, " - "`x` = %1.1f,`y` = %1.1f,`z` = %1.1f, `heading` = %1.1f, " + "`x` = %1.1f,`y` = %1.1f,`z` = %1.1f, `heading` = %1.1f, `guild_consent_id` = %u, " "`is_locked` = %d, `exp` = %u, `size` = %f, `level` = %u, " "`race` = %u, `gender` = %u, `class` = %u, `deity` = %u, " "`texture` = %u, `helm_texture` = %u, `copper` = %u, " @@ -4303,7 +4303,7 @@ uint32 ZoneDatabase::UpdateCharacterCorpse(uint32 db_id, uint32 char_id, const c "`wc_7` = %u, `wc_8` = %u, `wc_9` = %u " "WHERE `id` = %u", EscapeString(char_name).c_str(), zone_id, instance_id, char_id, - position.x, position.y, position.z, position.w, + position.x, position.y, position.z, position.w, guild_id, dbpc->locked, dbpc->exp, dbpc->size, dbpc->level, dbpc->race, dbpc->gender, dbpc->class_, dbpc->deity, dbpc->texture, dbpc->helmtexture, dbpc->copper, dbpc->silver, dbpc->gold, @@ -4319,12 +4319,19 @@ uint32 ZoneDatabase::UpdateCharacterCorpse(uint32 db_id, uint32 char_id, const c return db_id; } +uint32 ZoneDatabase::UpdateCharacterCorpseConsent(uint32 charid, uint32 guildid) +{ + std::string query = StringFormat("UPDATE `character_corpses` SET `guild_consent_id` = %u WHERE `charid` = %u", guildid, charid); + auto results = QueryDatabase(query); + return results.RowsAffected(); +} + void ZoneDatabase::MarkCorpseAsRezzed(uint32 db_id) { std::string query = StringFormat("UPDATE `character_corpses` SET `is_rezzed` = 1 WHERE `id` = %i", db_id); auto results = QueryDatabase(query); } -uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const glm::vec4& position) { +uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const glm::vec4& position, uint32 guildid) { /* Dump Basic Corpse Data */ std::string query = StringFormat( "INSERT INTO `character_corpses` " @@ -4336,6 +4343,7 @@ uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, ui "`y` = %1.1f, " "`z` = %1.1f, " "`heading` = %1.1f, " + "`guild_consent_id` = %u, " "`time_of_death` = NOW(), " "`is_buried` = 0, " "`is_locked` = %d, " @@ -4379,6 +4387,7 @@ uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, ui position.y, position.z, position.w, + guildid, dbpc->locked, dbpc->exp, dbpc->size, @@ -4637,7 +4646,7 @@ bool ZoneDatabase::LoadCharacterCorpseData(uint32 corpse_id, PlayerCorpse_Struct Corpse* ZoneDatabase::SummonBuriedCharacterCorpses(uint32 char_id, uint32 dest_zone_id, uint16 dest_instance_id, const glm::vec4& position) { Corpse* corpse = nullptr; - std::string query = StringFormat("SELECT `id`, `charname`, `time_of_death`, `is_rezzed` " + std::string query = StringFormat("SELECT `id`, `charname`, `time_of_death`, `is_rezzed`, `guild_consent_id` " "FROM `character_corpses` " "WHERE `charid` = '%u' AND `is_buried` = 1 " "ORDER BY `time_of_death` LIMIT 1", @@ -4652,7 +4661,8 @@ Corpse* ZoneDatabase::SummonBuriedCharacterCorpses(uint32 char_id, uint32 dest_z position, row[2], // char* time_of_death atoi(row[3]) == 1, // bool rezzed - false // bool was_at_graveyard + false, // bool was_at_graveyard + atoul(row[4]) // uint32 guild_consent_id ); if (!corpse) continue; @@ -4678,7 +4688,7 @@ bool ZoneDatabase::SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zone_id auto results = QueryDatabase(query); query = StringFormat( - "SELECT `id`, `charname`, `time_of_death`, `is_rezzed` FROM `character_corpses` WHERE `charid` = '%u'" + "SELECT `id`, `charname`, `time_of_death`, `is_rezzed`, `guild_consent_id` FROM `character_corpses` WHERE `charid` = '%u'" "ORDER BY time_of_death", char_id); results = QueryDatabase(query); @@ -4691,7 +4701,8 @@ bool ZoneDatabase::SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zone_id position, row[2], atoi(row[3]) == 1, - false); + false, + atoul(row[4])); if (corpse) { entity_list.AddCorpse(corpse); @@ -4766,7 +4777,7 @@ bool ZoneDatabase::UnburyCharacterCorpse(uint32 db_id, uint32 new_zone_id, uint1 Corpse* ZoneDatabase::LoadCharacterCorpse(uint32 player_corpse_id) { Corpse* NewCorpse = 0; std::string query = StringFormat( - "SELECT `id`, `charid`, `charname`, `x`, `y`, `z`, `heading`, `time_of_death`, `is_rezzed`, `was_at_graveyard` FROM `character_corpses` WHERE `id` = '%u' LIMIT 1", + "SELECT `id`, `charid`, `charname`, `x`, `y`, `z`, `heading`, `time_of_death`, `is_rezzed`, `was_at_graveyard`, `guild_consent_id` FROM `character_corpses` WHERE `id` = '%u' LIMIT 1", player_corpse_id ); auto results = QueryDatabase(query); @@ -4779,7 +4790,8 @@ Corpse* ZoneDatabase::LoadCharacterCorpse(uint32 player_corpse_id) { position, row[7], // time_of_death char* time_of_death atoi(row[8]) == 1, // is_rezzed bool rezzed - atoi(row[9]) // was_at_graveyard bool was_at_graveyard + atoi(row[9]), // was_at_graveyard bool was_at_graveyard + atoul(row[10]) // guild_consent_id uint32 guild_consent_id ); entity_list.AddCorpse(NewCorpse); } @@ -4789,10 +4801,10 @@ Corpse* ZoneDatabase::LoadCharacterCorpse(uint32 player_corpse_id) { bool ZoneDatabase::LoadCharacterCorpses(uint32 zone_id, uint16 instance_id) { std::string query; if (!RuleB(Zone, EnableShadowrest)){ - query = StringFormat("SELECT id, charid, charname, x, y, z, heading, time_of_death, is_rezzed, was_at_graveyard FROM character_corpses WHERE zone_id='%u' AND instance_id='%u'", zone_id, instance_id); + query = StringFormat("SELECT id, charid, charname, x, y, z, heading, time_of_death, is_rezzed, was_at_graveyard, guild_consent_id FROM character_corpses WHERE zone_id='%u' AND instance_id='%u'", zone_id, instance_id); } else{ - query = StringFormat("SELECT id, charid, charname, x, y, z, heading, time_of_death, is_rezzed, 0 as was_at_graveyard FROM character_corpses WHERE zone_id='%u' AND instance_id='%u' AND is_buried=0", zone_id, instance_id); + query = StringFormat("SELECT id, charid, charname, x, y, z, heading, time_of_death, is_rezzed, 0 as was_at_graveyard, guild_consent_id FROM character_corpses WHERE zone_id='%u' AND instance_id='%u' AND is_buried=0", zone_id, instance_id); } auto results = QueryDatabase(query); @@ -4806,7 +4818,8 @@ bool ZoneDatabase::LoadCharacterCorpses(uint32 zone_id, uint16 instance_id) { position, row[7], // time_of_death char* time_of_death atoi(row[8]) == 1, // is_rezzed bool rezzed - atoi(row[9])) + atoi(row[9]), + atoul(row[10])) // guild_consent_id uint32 guild_consent_id ); } diff --git a/zone/zonedb.h b/zone/zonedb.h index d5c02c693..b1d88539d 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -367,8 +367,9 @@ public: uint32 SendCharacterCorpseToGraveyard(uint32 dbid, uint32 zoneid, uint16 instanceid, const glm::vec4& position); uint32 CreateGraveyardRecord(uint32 graveyard_zoneid, const glm::vec4& position); uint32 AddGraveyardIDToZone(uint32 zone_id, uint32 graveyard_id); - uint32 SaveCharacterCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const glm::vec4& position); - uint32 UpdateCharacterCorpse(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const glm::vec4& position, bool rezzed = false); + uint32 SaveCharacterCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const glm::vec4& position, uint32 guildid); + uint32 UpdateCharacterCorpse(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const glm::vec4& position, uint32 guildid, bool rezzed = false); + uint32 UpdateCharacterCorpseConsent(uint32 charid, uint32 guildid); uint32 GetFirstCorpseID(uint32 char_id); uint32 GetCharacterCorpseCount(uint32 char_id); uint32 GetCharacterCorpseID(uint32 char_id, uint8 corpse); From 9689787e563d2c7ecb2eca8ff58798c08023e8f8 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Wed, 29 Jan 2020 18:34:33 -0500 Subject: [PATCH 06/60] Remove assignments in conditions --- world/zoneserver.cpp | 37 +++++++++++++++++++++---------------- zone/corpse.cpp | 35 ++++++++++++++++++++++++----------- zone/worldserver.cpp | 16 +++++++--------- 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 1bbc7f1fc..08ccbfaff 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1064,29 +1064,34 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; ZoneServer* owner_zs = nullptr; - if ((s->instance_id != 0 && (owner_zs = zoneserver_list.FindByInstanceID(s->instance_id))) || - (s->instance_id == 0 && (owner_zs = zoneserver_list.FindByZoneID(s->zone_id)))) - { + if (s->instance_id == 0) { + owner_zs = zoneserver_list.FindByZoneID(s->zone_id); + } + else { + owner_zs = zoneserver_list.FindByInstanceID(s->instance_id); + } + + if (owner_zs) { owner_zs->SendPacket(pack); } - else - { + else { LogInfo("Unable to locate zone record for zone id [{}] or instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id, s->instance_id); } - if (s->consent_type == EQEmu::consent::Normal) - { + if (s->consent_type == EQEmu::consent::Normal) { // send the message to the client being granted or denied permission - if (ClientListEntry* cle = client_list.FindCharacter(s->grantname)) - { + ClientListEntry* cle = client_list.FindCharacter(s->grantname); + if (cle) { ZoneServer* granted_zs = nullptr; - if ((cle->instance() != 0 && (granted_zs = zoneserver_list.FindByInstanceID(cle->instance()))) || - (cle->instance() == 0 && (granted_zs = zoneserver_list.FindByZoneID(cle->zone())))) - { - // avoid sending twice if owner and granted are in same zone - if (granted_zs != owner_zs) { - granted_zs->SendPacket(pack); - } + if (cle->instance() == 0) { + granted_zs = zoneserver_list.FindByZoneID(cle->zone()); + } + else { + granted_zs = zoneserver_list.FindByInstanceID(cle->instance()); + } + // avoid sending twice if owner and granted are in same zone + if (granted_zs && granted_zs != owner_zs) { + granted_zs->SendPacket(pack); } } } diff --git a/zone/corpse.cpp b/zone/corpse.cpp index b24a335d2..734bfa937 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -283,10 +283,16 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( allowed_looters[i] = 0; } - Group* grp = nullptr; - Raid* raid = nullptr; - consent_group_id = (client->AutoConsentGroupEnabled() && (grp = client->GetGroup())) ? grp->GetID() : 0; - consent_raid_id = (client->AutoConsentRaidEnabled() && (raid = client->GetRaid())) ? raid->GetID() : 0; + if (client->AutoConsentGroupEnabled()) { + Group* grp = client->GetGroup(); + consent_group_id = grp ? grp->GetID() : 0; + } + + if (client->AutoConsentRaidEnabled()) { + Raid* raid = client->GetRaid(); + consent_raid_id = raid ? raid->GetID() : 0; + } + consent_guild_id = client->AutoConsentGuildEnabled() ? client->GuildID() : 0; is_corpse_changed = true; @@ -1474,13 +1480,20 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { } } - if (!consented) { - Group* grp = nullptr; - Raid* raid = nullptr; - if ((consent_guild_id && consent_guild_id != GUILD_NONE && client->GuildID() == consent_guild_id) || - (consent_group_id && (grp = client->GetGroup()) && grp->GetID() == consent_group_id) || - (consent_raid_id && (raid = client->GetRaid()) && raid->GetID() == consent_raid_id)) - { + if (!consented && consent_guild_id && consent_guild_id != GUILD_NONE) { + if (client->GuildID() == consent_guild_id) { + consented = true; + } + } + if (!consented && consent_group_id) { + Group* grp = client->GetGroup(); + if (grp && grp->GetID() == consent_group_id) { + consented = true; + } + } + if (!consented && consent_raid_id) { + Raid* raid = client->GetRaid(); + if (raid && raid->GetID() == consent_raid_id) { consented = true; } } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 4d01f792a..e1d90920f 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1467,8 +1467,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } - if (found_corpse) - { + if (found_corpse) { // forward the grant/deny message for this zone to both owner and granted auto outapp = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)outapp->pBuffer; @@ -1484,10 +1483,9 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } case ServerOP_Consent_Response: { ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; - if (s->consent_type == EQEmu::consent::Normal) - { - if (Client* client = entity_list.GetClientByName(s->grantname)) - { + if (s->consent_type == EQEmu::consent::Normal) { + Client* grant_client = entity_list.GetClientByName(s->grantname); + if (grant_client) { // send the message to the client being granted or denied permission auto outapp = new EQApplicationPacket(OP_ConsentResponse, sizeof(ConsentResponse_Struct)); ConsentResponse_Struct* crs = (ConsentResponse_Struct*)outapp->pBuffer; @@ -1495,12 +1493,12 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) strcpy(crs->ownername, s->ownername); crs->permission = s->permission; strn0cpy(crs->zonename, s->zonename, sizeof(crs->zonename)); - client->QueuePacket(outapp); + grant_client->QueuePacket(outapp); safe_delete(outapp); } } - if (Client* client = entity_list.GetClientByName(s->ownername)) - { + Client* client = entity_list.GetClientByName(s->ownername); + if (client) { // send owner consent/deny confirmation message client->MessageString(Chat::White, s->message_string_id, s->grantname, s->zonename); } From 14c070f845f61581a11b72f9383af321bdc57d9b Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Thu, 30 Jan 2020 20:00:01 -0500 Subject: [PATCH 07/60] Use strn0cpy instead of strcpy when copying consent name buffers Add nullptr checks to consent functions that accept char pointers --- zone/client.cpp | 11 +++++++---- zone/corpse.cpp | 22 +++++++++++++--------- zone/worldserver.cpp | 4 ++-- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index b6d91602c..fd1080797 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6257,7 +6257,10 @@ void Client::DragCorpses() void Client::ConsentCorpses(const char* consent_name, bool deny) { - if (strcasecmp(consent_name, GetName()) == 0) { + if (!consent_name) { + return; + } + else if (strcasecmp(consent_name, GetName()) == 0) { MessageString(Chat::Red, CONSENT_YOURSELF); } else if (!consent_throttle_timer.Check()) { @@ -6266,9 +6269,9 @@ void Client::ConsentCorpses(const char* consent_name, bool deny) else { auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, consent_name); - strcpy(scs->ownername, GetName()); - strcpy(scs->zonename, "Unknown"); + strn0cpy(scs->grantname, consent_name, sizeof(scs->grantname)); + strn0cpy(scs->ownername, GetName(), sizeof(scs->ownername)); + strn0cpy(scs->zonename, "Unknown", sizeof(scs->zonename)); scs->message_string_id = 0; scs->permission = deny ? 0 : 1; scs->zone_id = zone->GetZoneID(); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 734bfa937..3714ea7f7 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -662,21 +662,25 @@ void Corpse::DepopPlayerCorpse() { void Corpse::AddConsentName(const char* add_name) { - for (const auto& n : consent_names) { - if (strcasecmp(n.c_str(), add_name) == 0) { - return; + if (add_name) { + for (const auto& n : consent_names) { + if (strcasecmp(n.c_str(), add_name) == 0) { + return; + } } + consent_names.emplace_back(add_name); } - consent_names.emplace_back(add_name); } void Corpse::RemoveConsentName(const char* rem_name) { - consent_names.erase(std::remove_if(consent_names.begin(), consent_names.end(), - [rem_name](const std::string& n) { - return strcasecmp(n.c_str(), rem_name) == 0; - } - ), consent_names.end()); + if (rem_name) { + consent_names.erase(std::remove_if(consent_names.begin(), consent_names.end(), + [rem_name](const std::string& n) { + return strcasecmp(n.c_str(), rem_name) == 0; + } + ), consent_names.end()); + } } uint32 Corpse::CountItems() { diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index e1d90920f..2787d3c88 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1489,8 +1489,8 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) // send the message to the client being granted or denied permission auto outapp = new EQApplicationPacket(OP_ConsentResponse, sizeof(ConsentResponse_Struct)); ConsentResponse_Struct* crs = (ConsentResponse_Struct*)outapp->pBuffer; - strcpy(crs->grantname, s->grantname); - strcpy(crs->ownername, s->ownername); + strn0cpy(crs->grantname, s->grantname, sizeof(crs->grantname)); + strn0cpy(crs->ownername, s->ownername, sizeof(crs->ownername)); crs->permission = s->permission; strn0cpy(crs->zonename, s->zonename, sizeof(crs->zonename)); grant_client->QueuePacket(outapp); From 158d8a011f4c7c8bb92935c9b36c148623a2c529 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 31 Jan 2020 20:25:06 -0600 Subject: [PATCH 08/60] Beginning of hot reload work --- common/eqemu_logsys.cpp | 2 ++ common/eqemu_logsys.h | 2 ++ common/eqemu_logsys_log_aliases.h | 16 ++++++++++++ common/servertalk.h | 14 ++++++++--- world/console.cpp | 41 ++++++++++++++++++++++++++----- zone/CMakeLists.txt | 4 ++- zone/worldserver.cpp | 30 ++++++++++++++++++++-- zone/zone.cpp | 32 +++++++++++++++++++++++- zone/zone.h | 7 ++++++ zone/zone_reload.cpp | 35 ++++++++++++++++++++++++++ zone/zone_reload.h | 31 +++++++++++++++++++++++ 11 files changed, 201 insertions(+), 13 deletions(-) create mode 100644 zone/zone_reload.cpp create mode 100644 zone/zone_reload.h diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index f5d58a5fd..dca47dfb8 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -124,6 +124,8 @@ void EQEmuLogSys::LoadLogSettingsDefaults() log_settings[Logs::Loginserver].log_to_console = static_cast(Logs::General); log_settings[Logs::HeadlessClient].log_to_console = static_cast(Logs::General); log_settings[Logs::NPCScaling].log_to_gmsay = static_cast(Logs::General); + log_settings[Logs::HotReload].log_to_gmsay = static_cast(Logs::General); + log_settings[Logs::HotReload].log_to_console = static_cast(Logs::General); /** * RFC 5424 diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 9fa164d98..cb880e038 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -114,6 +114,7 @@ namespace Logs { EntityManagement, Flee, Aura, + HotReload, MaxCategoryID /* Don't Remove this */ }; @@ -187,6 +188,7 @@ namespace Logs { "Entity Management", "Flee", "Aura", + "HotReload", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index ccfc54f3a..2ca5127b6 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -561,6 +561,16 @@ OutF(LogSys, Logs::Detail, Logs::Aura, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogHotReload(message, ...) do {\ + if (LogSys.log_settings[Logs::HotReload].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::HotReload, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogHotReloadDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::HotReload].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::HotReload, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ @@ -894,6 +904,12 @@ #define LogAuraDetail(message, ...) do {\ } while (0) +#define LogHotReload(message, ...) do {\ +} while (0) + +#define LogHotReloadDetail(message, ...) do {\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ } while (0) diff --git a/common/servertalk.h b/common/servertalk.h index b9cb6202e..c361004f6 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -197,7 +197,11 @@ #define ServerOP_CZSetEntityVariableByClientName 0x4012 #define ServerOP_UCSServerStatusRequest 0x4013 #define ServerOP_UCSServerStatusReply 0x4014 -/* Query Server OP Codes */ +#define ServerOP_HotReloadQuests 0x4015 + +/** + * QueryServer + */ #define ServerOP_QSPlayerLogTrades 0x5010 #define ServerOP_QSPlayerLogHandins 0x5011 #define ServerOP_QSPlayerLogNPCKills 0x5012 @@ -1349,12 +1353,16 @@ struct CZSetEntVarByClientName_Struct { char m_var[256]; }; -struct ReloadWorld_Struct{ +struct ReloadWorld_Struct { uint32 Option; }; +struct HotReloadQuestsStruct { + char zone_short_name[200]; +}; + struct ServerRequestTellQueue_Struct { - char name[64]; + char name[64]; }; struct UCSServerStatus_Struct { diff --git a/world/console.cpp b/world/console.cpp index 90e5f7c71..26d42e4b4 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -802,9 +802,9 @@ void ConsoleIpLookup( const std::vector &args ) { - if (args.size() > 0) { + if (!args.empty()) { WorldConsoleTCPConnection console_connection(connection); - client_list.SendCLEList(connection->Admin(), 0, &console_connection, args[0].c_str()); + client_list.SendCLEList(connection->Admin(), nullptr, &console_connection, args[0].c_str()); } } @@ -855,6 +855,34 @@ void ConsoleReloadWorld( safe_delete(pack); } +/** + * @param connection + * @param command + * @param args + */ +void ConsoleReloadZoneQuests( + EQ::Net::ConsoleServerConnection *connection, + const std::string &command, + const std::vector &args +) +{ + if (args.empty()) { + connection->SendLine("[zone_short_name] required as argument"); + return; + } + + std::string zone_short_name = args[0]; + + connection->SendLine(fmt::format("Reloading Zone [{}]...", zone_short_name)); + + auto pack = new ServerPacket(ServerOP_HotReloadQuests, sizeof(HotReloadQuestsStruct)); + auto *hot_reload_quests = (HotReloadQuestsStruct *) pack->pBuffer; + strn0cpy(hot_reload_quests->zone_short_name, (char *) zone_short_name.c_str(), 200); + + zoneserver_list.SendPacket(pack); + safe_delete(pack); +} + /** * @param connection * @param command @@ -892,7 +920,8 @@ void RegisterConsoleFunctions(std::unique_ptr& console) console->RegisterCall("md5", 50, "md5", std::bind(ConsoleMd5, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("ooc", 50, "ooc [message]", std::bind(ConsoleOOC, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("reloadworld", 200, "reloadworld", std::bind(ConsoleReloadWorld, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - console->RegisterCall("setpass", 200, "setpass [accountname] [newpass]", std::bind(ConsoleSetPass, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("reloadzonequests", 200, "reloadzonequests [zone_short_name]", std::bind(ConsoleReloadZoneQuests, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("setpass", 200, "setpass [account_name] [new_password]", std::bind(ConsoleSetPass, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("signalcharbyname", 50, "signalcharbyname charname ID", std::bind(ConsoleSignalCharByName, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("tell", 50, "tell [name] [message]", std::bind(ConsoleTell, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("unlock", 150, "unlock", std::bind(ConsoleUnlock, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); @@ -901,9 +930,9 @@ void RegisterConsoleFunctions(std::unique_ptr& console) console->RegisterCall("who", 50, "who", std::bind(ConsoleWho, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("whoami", 50, "whoami", std::bind(ConsoleWhoami, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("worldshutdown", 200, "worldshutdown", std::bind(ConsoleWorldShutdown, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - console->RegisterCall("zonebootup", 150, "zonebootup [ZoneServerID] [zonename]", std::bind(ConsoleZoneBootup, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - console->RegisterCall("zonelock", 150, "zonelock [list|lock|unlock] [zonename]", std::bind(ConsoleZoneLock, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - console->RegisterCall("zoneshutdown", 150, "zoneshutdown [zonename or ZoneServerID]", std::bind(ConsoleZoneShutdown, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("zonebootup", 150, "zonebootup [zone_server_id] [zone_short_name]", std::bind(ConsoleZoneBootup, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("zonelock", 150, "zonelock [list|lock|unlock] [zone_short_name]", std::bind(ConsoleZoneLock, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("zoneshutdown", 150, "zoneshutdown [zone_short_name or zone_server_id]", std::bind(ConsoleZoneShutdown, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("zonestatus", 50, "zonestatus", std::bind(ConsoleZoneStatus, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));console->RegisterCall("ping", 50, "ping", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("quit", 50, "quit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("exit", 50, "exit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index f75e963f9..80415f6ff 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -141,6 +141,7 @@ SET(zone_sources zone.cpp zone_config.cpp zonedb.cpp + zone_reload.cpp zoning.cpp ) @@ -247,7 +248,8 @@ SET(zone_headers zone.h zone_config.h zonedb.h - zonedump.h) + zonedump.h + zone_reload.h ) ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers}) diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index a5180963d..95a732995 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -51,6 +51,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "worldserver.h" #include "zone.h" #include "zone_config.h" +#include "zone_reload.h" extern EntityList entity_list; @@ -1930,15 +1931,40 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) iter++; } } + case ServerOP_ReloadWorld: { - ReloadWorld_Struct* RW = (ReloadWorld_Struct*)pack->pBuffer; + auto* reload_world = (ReloadWorld_Struct*)pack->pBuffer; if (zone) { - zone->ReloadWorld(RW->Option); + zone->ReloadWorld(reload_world->Option); } break; } + case ServerOP_HotReloadQuests: + { + if (!zone) { + break; + } + + auto *hot_reload_quests = (HotReloadQuestsStruct *) pack->pBuffer; + + LogHotReloadDetail( + "Receiving request [HotReloadQuests] | request_zone [{}] current_zone [{}]", + hot_reload_quests->zone_short_name, + zone->GetShortName() + ); + + std::string request_zone_short_name = hot_reload_quests->zone_short_name; + std::string local_zone_short_name = zone->GetShortName(); + + if (request_zone_short_name == local_zone_short_name || request_zone_short_name == "all"){ + zone->SetQuestHotReloadQueued(true); + } + + break; + } + case ServerOP_ChangeSharedMem: { std::string hotfix_name = std::string((char*)pack->pBuffer); diff --git a/zone/zone.cpp b/zone/zone.cpp index b88ddd071..90d624848 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -55,6 +55,7 @@ #include "mob_movement_manager.h" #include "npc_scale_manager.h" #include "../common/data_verification.h" +#include "zone_reload.h" #include #include @@ -771,6 +772,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) autoshutdown_timer((RuleI(Zone, AutoShutdownDelay))), clientauth_timer(AUTHENTICATION_TIMEOUT * 1000), spawn2_timer(1000), + hot_reload_timer(1000), qglobal_purge_timer(30000), hotzone_timer(120000), m_SafePoint(0.0f,0.0f,0.0f), @@ -874,6 +876,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) mMovementManager = &MobMovementManager::Get(); SetNpcPositionUpdateDistance(0); + SetQuestHotReloadQueued(false); } Zone::~Zone() { @@ -1231,6 +1234,24 @@ bool Zone::Process() { } } + if (hot_reload_timer.Check() && IsQuestHotReloadQueued()) { + + LogHotReloadDetail("Hot reload timer check..."); + + bool perform_reload = true; + for (auto &it : entity_list.GetClientList()) { + auto client = it.second; + if (client->GetAggroCount() > 0) { + perform_reload = false; + break; + } + } + + if (perform_reload) { + ZoneReload::HotReloadQuests(); + } + } + if(initgrids_timer.Check()) { //delayed grid loading stuff. initgrids_timer.Disable(); @@ -1540,7 +1561,6 @@ void Zone::RepopClose(const glm::vec4& client_position, uint32 repop_distance) void Zone::Repop(uint32 delay) { - if (!Depop()) { return; } @@ -2422,3 +2442,13 @@ void Zone::CalculateNpcUpdateDistanceSpread() combined_spread ); } + +bool Zone::IsQuestHotReloadQueued() const +{ + return quest_hot_reload_queued; +} + +void Zone::SetQuestHotReloadQueued(bool in_quest_hot_reload_queued) +{ + quest_hot_reload_queued = in_quest_hot_reload_queued; +} diff --git a/zone/zone.h b/zone/zone.h index 4ed017627..1243e18c2 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -204,6 +204,7 @@ public: time_t weather_timer; Timer spawn2_timer; + Timer hot_reload_timer; uint8 weather_intensity; uint8 zone_weather; @@ -270,6 +271,9 @@ public: void UpdateQGlobal(uint32 qid, QGlobal newGlobal); void weatherSend(Client *client = nullptr); + bool IsQuestHotReloadQueued() const; + void SetQuestHotReloadQueued(bool in_quest_hot_reload_queued); + WaterMap *watermap; 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); @@ -340,6 +344,9 @@ private: bool m_ucss_available; bool staticzone; bool zone_has_current_time; + bool quest_hot_reload_queued; + +private: double max_movement_update_range; char *long_name; char *map_name; diff --git a/zone/zone_reload.cpp b/zone/zone_reload.cpp new file mode 100644 index 000000000..914a67ebc --- /dev/null +++ b/zone/zone_reload.cpp @@ -0,0 +1,35 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY except by those people which sell it, which + * are required to give you total support for your newly bought product; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "zone_reload.h" +#include "quest_parser_collection.h" + +void ZoneReload::HotReloadQuests() +{ + BenchTimer timer; + timer.reset(); + + entity_list.ClearAreas(); + parse->ReloadQuests(); + zone->Repop(0); + zone->SetQuestHotReloadQueued(false); + + LogHotReload("[Quests] Reloading scripts in zone [{}] Time [{:.2f}]", zone->GetShortName(), timer.elapsed()); +} diff --git a/zone/zone_reload.h b/zone/zone_reload.h new file mode 100644 index 000000000..1e4f95826 --- /dev/null +++ b/zone/zone_reload.h @@ -0,0 +1,31 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY except by those people which sell it, which + * are required to give you total support for your newly bought product; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef EQEMU_ZONE_RELOAD_H +#define EQEMU_ZONE_RELOAD_H + + +class ZoneReload { +public: + static void HotReloadQuests(); +}; + + +#endif //EQEMU_ZONE_RELOAD_H From c8e6d031cf5e5b73eb13369ffa25a8dbc2da1207 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 31 Jan 2020 21:51:05 -0600 Subject: [PATCH 09/60] Add more rule-driven behavior --- common/ruletypes.h | 6 ++++++ world/console.cpp | 2 +- zone/zone.cpp | 13 ++++++++----- zone/zone_reload.cpp | 17 ++++++++++++++--- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index dab1d02e1..6a2ca7b57 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -762,6 +762,12 @@ RULE_CATEGORY(Logging) RULE_BOOL(Logging, PrintFileFunctionAndLine, false, "Ex: [World Server] [net.cpp::main:309] Loading variables...") RULE_CATEGORY_END() +RULE_CATEGORY(HotReload) +RULE_BOOL(HotReload, QuestsRepopWithReload, true, "When a hot reload is triggered, the zone will repop") +RULE_BOOL(HotReload, QuestsRepopWhenPlayersNotInCombat, true, "When a hot reload is triggered, the zone will repop when no clients are in combat") +RULE_BOOL(HotReload, QuestsResetTimersWithReload, true, "When a hot reload is triggered, the zone will repop") +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/world/console.cpp b/world/console.cpp index 26d42e4b4..b3f36c923 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -925,7 +925,7 @@ void RegisterConsoleFunctions(std::unique_ptr& console) console->RegisterCall("signalcharbyname", 50, "signalcharbyname charname ID", std::bind(ConsoleSignalCharByName, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("tell", 50, "tell [name] [message]", std::bind(ConsoleTell, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("unlock", 150, "unlock", std::bind(ConsoleUnlock, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - console->RegisterCall("uptime", 50, "uptime [zoneID#]", std::bind(ConsoleUptime, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("uptime", 50, "uptime [zone_server_id]", std::bind(ConsoleUptime, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("version", 50, "version", std::bind(ConsoleVersion, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("who", 50, "who", std::bind(ConsoleWho, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("whoami", 50, "whoami", std::bind(ConsoleWhoami, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); diff --git a/zone/zone.cpp b/zone/zone.cpp index 90d624848..16cdc0af8 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1239,11 +1239,14 @@ bool Zone::Process() { LogHotReloadDetail("Hot reload timer check..."); bool perform_reload = true; - for (auto &it : entity_list.GetClientList()) { - auto client = it.second; - if (client->GetAggroCount() > 0) { - perform_reload = false; - break; + + if (RuleB(HotReload, QuestsRepopWhenPlayersNotInCombat)) { + for (auto &it : entity_list.GetClientList()) { + auto client = it.second; + if (client->GetAggroCount() > 0) { + perform_reload = false; + break; + } } } diff --git a/zone/zone_reload.cpp b/zone/zone_reload.cpp index 914a67ebc..5a9aeda99 100644 --- a/zone/zone_reload.cpp +++ b/zone/zone_reload.cpp @@ -27,9 +27,20 @@ void ZoneReload::HotReloadQuests() timer.reset(); entity_list.ClearAreas(); - parse->ReloadQuests(); - zone->Repop(0); + + parse->ReloadQuests(RuleB(HotReload, QuestsResetTimersWithReload)); + + if (RuleB(HotReload, QuestsRepopWithReload)) { + zone->Repop(0); + } zone->SetQuestHotReloadQueued(false); - LogHotReload("[Quests] Reloading scripts in zone [{}] Time [{:.2f}]", zone->GetShortName(), timer.elapsed()); + LogHotReload( + "[Quests] Reloading scripts in zone [{}] repop_with_reload [{}] reset_timers [{}] when_not_in_combat [{}] Time [{:.4f}]", + zone->GetShortName(), + (RuleB(HotReload, QuestsRepopWithReload) ? "true" : "false"), + (RuleB(HotReload, QuestsResetTimersWithReload) ? "true" : "false"), + (RuleB(HotReload, QuestsRepopWhenPlayersNotInCombat) ? "true" : "false"), + timer.elapsed() + ); } From c2300d514cecc32547679b4fdcebac5d26d3d662 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 1 Feb 2020 20:49:04 -0800 Subject: [PATCH 10/60] Packet warnings --- common/net/packet.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/net/packet.h b/common/net/packet.h index 1ce173a10..4ff5f8510 100644 --- a/common/net/packet.h +++ b/common/net/packet.h @@ -89,9 +89,9 @@ namespace EQ { public: StaticPacket(void *data, size_t size) { m_data = data; m_data_length = size; m_max_data_length = size; } virtual ~StaticPacket() { } - StaticPacket(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; } + StaticPacket(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; m_max_data_length = o.m_max_data_length; } StaticPacket& operator=(const StaticPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; return *this; } - StaticPacket(StaticPacket &&o) { m_data = o.m_data; m_data_length = o.m_data_length; } + StaticPacket(StaticPacket &&o) noexcept { m_data = o.m_data; m_data_length = o.m_data_length; } virtual const void *Data() const { return m_data; } virtual void *Data() { return m_data; } @@ -112,7 +112,7 @@ namespace EQ { public: DynamicPacket() { } virtual ~DynamicPacket() { } - DynamicPacket(DynamicPacket &&o) { m_data = std::move(o.m_data); } + DynamicPacket(DynamicPacket &&o) noexcept { m_data = std::move(o.m_data); } DynamicPacket(const DynamicPacket &o) { m_data = o.m_data; } DynamicPacket& operator=(const DynamicPacket &o) { m_data = o.m_data; return *this; } @@ -127,4 +127,4 @@ namespace EQ { std::vector m_data; }; } -} \ No newline at end of file +} From e09b0ae1e9ca8573a1e213654765c690a216af46 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sun, 2 Feb 2020 14:12:13 -0500 Subject: [PATCH 11/60] Let client handle consent confirmation messages to corpse owner --- common/servertalk.h | 1 - zone/client.cpp | 1 - zone/string_ids.h | 2 -- zone/worldserver.cpp | 34 +++++++++++++++++----------------- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index 2557941e7..aa3529756 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -870,7 +870,6 @@ struct ServerOP_Consent_Struct { uint8 permission; uint32 zone_id; uint16 instance_id; - uint32 message_string_id; uint8 consent_type; // 0 = normal, 1 = group, 2 = raid, 3 = guild uint32 consent_id; }; diff --git a/zone/client.cpp b/zone/client.cpp index fd1080797..31333dc57 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6272,7 +6272,6 @@ void Client::ConsentCorpses(const char* consent_name, bool deny) strn0cpy(scs->grantname, consent_name, sizeof(scs->grantname)); strn0cpy(scs->ownername, GetName(), sizeof(scs->ownername)); strn0cpy(scs->zonename, "Unknown", sizeof(scs->zonename)); - scs->message_string_id = 0; scs->permission = deny ? 0 : 1; scs->zone_id = zone->GetZoneID(); scs->instance_id = zone->GetInstanceID(); diff --git a/zone/string_ids.h b/zone/string_ids.h index 7c4066b55..3b17b6d5b 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -268,8 +268,6 @@ #define REZZ_ALREADY_PENDING 1379 //You were unable to restore the corpse to life, but you may have success with a later attempt. #define IN_USE 1406 //Someone else is using that. Try again later. #define DUEL_FLED 1408 //%1 has defeated %2 in a duel to the death! %3 has fled like a cowardly dog! -#define GIVE_CONSENT 1427 //You have given %1 permission to drag your corpse in %2. -#define DENY_CONSENT 1428 //You have denied %1 permission to drag your corpse in %2. #define MEMBER_OF_YOUR_GUILD 1429 #define OFFICER_OF_YOUR_GUILD 1430 #define LEADER_OF_YOUR_GUILD 1431 diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 2787d3c88..56f99067c 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1475,7 +1475,6 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (zone) { strn0cpy(scs->zonename, zone->GetLongName(), sizeof(scs->zonename)); } - scs->message_string_id = s->permission ? GIVE_CONSENT : DENY_CONSENT; worldserver.SendPacket(outapp); safe_delete(outapp); } @@ -1483,24 +1482,25 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } case ServerOP_Consent_Response: { ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; + Client* owner_client = entity_list.GetClientByName(s->ownername); + Client* grant_client = nullptr; if (s->consent_type == EQEmu::consent::Normal) { - Client* grant_client = entity_list.GetClientByName(s->grantname); - if (grant_client) { - // send the message to the client being granted or denied permission - auto outapp = new EQApplicationPacket(OP_ConsentResponse, sizeof(ConsentResponse_Struct)); - ConsentResponse_Struct* crs = (ConsentResponse_Struct*)outapp->pBuffer; - strn0cpy(crs->grantname, s->grantname, sizeof(crs->grantname)); - strn0cpy(crs->ownername, s->ownername, sizeof(crs->ownername)); - crs->permission = s->permission; - strn0cpy(crs->zonename, s->zonename, sizeof(crs->zonename)); - grant_client->QueuePacket(outapp); - safe_delete(outapp); - } + grant_client = entity_list.GetClientByName(s->grantname); } - Client* client = entity_list.GetClientByName(s->ownername); - if (client) { - // send owner consent/deny confirmation message - client->MessageString(Chat::White, s->message_string_id, s->grantname, s->zonename); + if (owner_client || grant_client) { + auto outapp = new EQApplicationPacket(OP_ConsentResponse, sizeof(ConsentResponse_Struct)); + ConsentResponse_Struct* crs = (ConsentResponse_Struct*)outapp->pBuffer; + strn0cpy(crs->grantname, s->grantname, sizeof(crs->grantname)); + strn0cpy(crs->ownername, s->ownername, sizeof(crs->ownername)); + crs->permission = s->permission; + strn0cpy(crs->zonename, s->zonename, sizeof(crs->zonename)); + if (owner_client) { + owner_client->QueuePacket(outapp); // confirmation message to the owner + } + if (grant_client) { + grant_client->QueuePacket(outapp); // message to the client being granted/denied + } + safe_delete(outapp); } break; } From b8229c84591fbf8235f0dfa9ae4757d443526187 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sun, 2 Feb 2020 22:57:59 -0500 Subject: [PATCH 12/60] Update CURRENT_BINARY_DATABASE_VERSION for consent sql update --- common/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/version.h b/common/version.h index 5d00daaf4..ca37150ec 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9147 +#define CURRENT_BINARY_DATABASE_VERSION 9148 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026 From d65a97e556a39e4edb0f4734773311961b3c754c Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 2 Feb 2020 20:19:37 -0800 Subject: [PATCH 13/60] Rule for setting max navmesh nodes, default set higher than current to improve accuracy --- common/ruletypes.h | 1 + zone/mob_movement_manager.cpp | 9 +-- zone/pathfinder_nav_mesh.cpp | 109 ++++++++++++++++------------------ 3 files changed, 57 insertions(+), 62 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index c6e99e5f4..8acbb2c14 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -293,6 +293,7 @@ RULE_BOOL(Pathing, Find, true, "Enable pathing for FindPerson requests from the RULE_BOOL(Pathing, Fear, true, "Enable pathing for fear") RULE_REAL(Pathing, NavmeshStepSize, 100.0f, "") RULE_REAL(Pathing, ShortMovementUpdateRange, 130.0f, "") +RULE_INT(Pathing, MaxNavmeshNodes, 4092) RULE_CATEGORY_END() RULE_CATEGORY(Watermap) diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index 5e40dfeee..4ac0c7f23 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -1300,7 +1300,9 @@ void MobMovementManager::PushEvadeCombat(MobMovementEntry &mob_movement_entry) */ void MobMovementManager::HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode) { - auto sb = who->GetStuckBehavior(); + LogDebug("Handle stuck behavior for {0} at ({1}, {2}, {3}) with movement_mode {4}", who->GetName(), x, y, z, mob_movement_mode); + + auto sb = who->GetStuckBehavior(); MobStuckBehavior behavior = RunToTarget; if (sb >= 0 && sb < MaxStuckBehavior) { @@ -1308,7 +1310,7 @@ void MobMovementManager::HandleStuckBehavior(Mob *who, float x, float y, float z } auto eiter = _impl->Entries.find(who); - auto &ent = (*eiter); + auto &ent = (*eiter); switch (sb) { case RunToTarget: @@ -1323,8 +1325,7 @@ void MobMovementManager::HandleStuckBehavior(Mob *who, float x, float y, float z PushStopMoving(ent.second); break; case EvadeCombat: - //PushEvadeCombat(ent.second); - PushStopMoving(ent.second); + PushEvadeCombat(ent.second); break; } } diff --git a/zone/pathfinder_nav_mesh.cpp b/zone/pathfinder_nav_mesh.cpp index 5be10b64f..63e76f486 100644 --- a/zone/pathfinder_nav_mesh.cpp +++ b/zone/pathfinder_nav_mesh.cpp @@ -12,8 +12,6 @@ extern Zone *zone; -const int MaxNavmeshNodes = 1024; - struct PathfinderNavmesh::Implementation { dtNavMesh *nav_mesh; @@ -36,19 +34,19 @@ PathfinderNavmesh::~PathfinderNavmesh() IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags) { partial = false; - + if (!m_impl->nav_mesh) { return IPath(); } - + if (!m_impl->query) { m_impl->query = dtAllocNavMeshQuery(); } - - m_impl->query->init(m_impl->nav_mesh, MaxNavmeshNodes); + + m_impl->query->init(m_impl->nav_mesh, RuleI(Pathing, MaxNavmeshNodes)); glm::vec3 current_location(start.x, start.z, start.y); glm::vec3 dest_location(end.x, end.z, end.y); - + dtQueryFilter filter; filter.setIncludeFlags(flags); filter.setAreaCost(0, 1.0f); //Normal @@ -61,48 +59,48 @@ IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const gl filter.setAreaCost(8, 1.0f); //General Area filter.setAreaCost(9, 0.1f); //Portal filter.setAreaCost(10, 0.1f); //Prefer - + dtPolyRef start_ref; dtPolyRef end_ref; glm::vec3 ext(5.0f, 100.0f, 5.0f); - + m_impl->query->findNearestPoly(¤t_location[0], &ext[0], &filter, &start_ref, 0); m_impl->query->findNearestPoly(&dest_location[0], &ext[0], &filter, &end_ref, 0); - + if (!start_ref || !end_ref) { return IPath(); } - + int npoly = 0; dtPolyRef path[1024] = { 0 }; auto status = m_impl->query->findPath(start_ref, end_ref, ¤t_location[0], &dest_location[0], &filter, path, &npoly, 1024); - + if (npoly) { glm::vec3 epos = dest_location; if (path[npoly - 1] != end_ref) { m_impl->query->closestPointOnPoly(path[npoly - 1], &dest_location[0], &epos[0], 0); partial = true; - + auto dist = DistanceSquared(epos, current_location); if (dist < 10000.0f) { stuck = true; } } - + float straight_path[2048 * 3]; unsigned char straight_path_flags[2048]; - + int n_straight_polys; dtPolyRef straight_path_polys[2048]; - + status = m_impl->query->findStraightPath(¤t_location[0], &epos[0], path, npoly, straight_path, straight_path_flags, straight_path_polys, &n_straight_polys, 2048, DT_STRAIGHTPATH_AREA_CROSSINGS); - + if (dtStatusFailed(status)) { return IPath(); } - + if (n_straight_polys) { IPath Route; for (int i = 0; i < n_straight_polys; ++i) @@ -111,9 +109,9 @@ IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const gl node.x = straight_path[i * 3]; node.z = straight_path[i * 3 + 1]; node.y = straight_path[i * 3 + 2]; - + Route.push_back(node); - + unsigned short flag = 0; if (dtStatusSucceed(m_impl->nav_mesh->getPolyFlags(straight_path_polys[i], &flag))) { if (flag & 512) { @@ -121,11 +119,11 @@ IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const gl } } } - + return Route; } } - + IPath Route; Route.push_back(end); return Route; @@ -134,19 +132,19 @@ IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const gl IPathfinder::IPath PathfinderNavmesh::FindPath(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, const PathfinderOptions &opts) { partial = false; - + if (!m_impl->nav_mesh) { return IPath(); } - + if (!m_impl->query) { m_impl->query = dtAllocNavMeshQuery(); } - - m_impl->query->init(m_impl->nav_mesh, MaxNavmeshNodes); + + m_impl->query->init(m_impl->nav_mesh, RuleI(Pathing, MaxNavmeshNodes)); glm::vec3 current_location(start.x, start.z, start.y); glm::vec3 dest_location(end.x, end.z, end.y); - + dtQueryFilter filter; filter.setIncludeFlags(opts.flags); filter.setAreaCost(0, opts.flag_cost[0]); //Normal @@ -159,83 +157,78 @@ IPathfinder::IPath PathfinderNavmesh::FindPath(const glm::vec3 &start, const glm filter.setAreaCost(8, opts.flag_cost[7]); //General Area filter.setAreaCost(9, opts.flag_cost[8]); //Portal filter.setAreaCost(10, opts.flag_cost[9]); //Prefer - + static const int max_polys = 256; dtPolyRef start_ref; dtPolyRef end_ref; glm::vec3 ext(10.0f, 200.0f, 10.0f); - + m_impl->query->findNearestPoly(¤t_location[0], &ext[0], &filter, &start_ref, 0); m_impl->query->findNearestPoly(&dest_location[0], &ext[0], &filter, &end_ref, 0); - + if (!start_ref || !end_ref) { return IPath(); } - + int npoly = 0; dtPolyRef path[max_polys] = { 0 }; - m_impl->query->findPath(start_ref, end_ref, ¤t_location[0], &dest_location[0], &filter, path, &npoly, max_polys); - + auto status = m_impl->query->findPath(start_ref, end_ref, ¤t_location[0], &dest_location[0], &filter, path, &npoly, max_polys); + if (npoly) { glm::vec3 epos = dest_location; if (path[npoly - 1] != end_ref) { m_impl->query->closestPointOnPoly(path[npoly - 1], &dest_location[0], &epos[0], 0); partial = true; - - auto dist = DistanceSquared(epos, current_location); - if (dist < 10000.0f) { - stuck = true; - } } - + int n_straight_polys; glm::vec3 straight_path[max_polys]; unsigned char straight_path_flags[max_polys]; dtPolyRef straight_path_polys[max_polys]; - + auto status = m_impl->query->findStraightPath(¤t_location[0], &epos[0], path, npoly, (float*)&straight_path[0], straight_path_flags, straight_path_polys, &n_straight_polys, 2048, DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS); - + if (dtStatusFailed(status)) { return IPath(); } - + if (n_straight_polys) { if (opts.smooth_path) { IPath Route; - + //Add the first point { auto &flag = straight_path_flags[0]; if (flag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) { auto &p = straight_path[0]; - + Route.push_back(glm::vec3(p.x, p.z, p.y)); } else { auto &p = straight_path[0]; - + float h = 0.0f; if (dtStatusSucceed(GetPolyHeightOnPath(path, npoly, p, &h))) { p.y = h + opts.offset; } - + Route.push_back(glm::vec3(p.x, p.z, p.y)); } } - + for (int i = 0; i < n_straight_polys - 1; ++i) { auto &flag = straight_path_flags[i]; - + if (flag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) { auto &poly = straight_path_polys[i]; - + auto &p2 = straight_path[i + 1]; glm::vec3 node(p2.x, p2.z, p2.y); Route.push_back(node); - + unsigned short pflag = 0; if (dtStatusSucceed(m_impl->nav_mesh->getPolyFlags(straight_path_polys[i], &pflag))) { if (pflag & 512) { @@ -250,12 +243,12 @@ IPathfinder::IPath PathfinderNavmesh::FindPath(const glm::vec3 &start, const glm auto dir = glm::normalize(p2 - p1); float total = 0.0f; glm::vec3 previous_pt = p1; - + while (total < dist) { glm::vec3 current_pt; float dist_to_move = opts.step_size; float ff = opts.step_size / 2.0f; - + if (total + dist_to_move + ff >= dist) { current_pt = p2; total = dist; @@ -264,18 +257,18 @@ IPathfinder::IPath PathfinderNavmesh::FindPath(const glm::vec3 &start, const glm total += dist_to_move; current_pt = p1 + dir * total; } - + float h = 0.0f; if (dtStatusSucceed(GetPolyHeightOnPath(path, npoly, current_pt, &h))) { current_pt.y = h + opts.offset; } - + Route.push_back(glm::vec3(current_pt.x, current_pt.z, current_pt.y)); previous_pt = current_pt; } } } - + return Route; } else { @@ -285,7 +278,7 @@ IPathfinder::IPath PathfinderNavmesh::FindPath(const glm::vec3 &start, const glm auto ¤t = straight_path[i]; glm::vec3 node(current.x, current.z, current.y); Route.push_back(node); - + unsigned short flag = 0; if (dtStatusSucceed(m_impl->nav_mesh->getPolyFlags(straight_path_polys[i], &flag))) { if (flag & 512) { @@ -293,7 +286,7 @@ IPathfinder::IPath PathfinderNavmesh::FindPath(const glm::vec3 &start, const glm } } } - + return Route; } } @@ -313,7 +306,7 @@ glm::vec3 PathfinderNavmesh::GetRandomLocation(const glm::vec3 &start) if (!m_impl->query) { m_impl->query = dtAllocNavMeshQuery(); - m_impl->query->init(m_impl->nav_mesh, MaxNavmeshNodes); + m_impl->query->init(m_impl->nav_mesh, RuleI(Pathing, MaxNavmeshNodes)); } dtQueryFilter filter; From a7633f4ddf6b1fe72a2028202a56c86fa680118d Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 3 Feb 2020 08:18:55 -0500 Subject: [PATCH 14/60] Massaged database update code [skip ci] --- utils/scripts/eqemu_server.pl | 155 +++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 68 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 2c769e811..929d9a79a 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -531,31 +531,7 @@ sub check_for_world_bootup_database_update { } $binary_database_version = trim($db_version[1]); - $local_database_version = trim(get_mysql_result("SELECT version FROM db_version LIMIT 1")); - - #::: Bots - $bots_binary_version = trim($db_version[2]); - if ($bots_binary_version > 0) { - $bots_local_db_version = get_bots_db_version(); - #::: We ran world - Database needs to update, lets backup and run updates and continue world bootup - - if ($bots_local_db_version < $bots_binary_version && $ARGV[0] eq "ran_from_world") { - print "[Update] Bots Database not up to date with binaries... Automatically updating...\n"; - print "[Update] Issuing database backup first...\n"; - database_dump_compress(); - print "[Update] Updating bots database...\n"; - sleep(1); - bots_db_management(); - run_database_check(); - print "[Update] Continuing bootup\n"; - analytics_insertion("auto database bots upgrade world", $db . " :: Binary DB Version / Local DB Version :: " . $binary_database_version . " / " . $local_database_version); - - exit; - } - else { - print "[Update] Bots database up to Date: Continuing World Bootup...\n"; - } - } + $local_database_version = get_main_db_version(); if ($binary_database_version == $local_database_version && $ARGV[0] eq "ran_from_world") { print "[Update] Database up to date...\n"; @@ -567,22 +543,63 @@ sub check_for_world_bootup_database_update { print "[Update] Database not up to date with binaries... Automatically updating...\n"; print "[Update] Issuing database backup first...\n"; database_dump_compress(); + $db_already_backed_up = 1; print "[Update] Updating database...\n"; sleep(1); main_db_management(); main_db_management(); - print "[Update] Continuing bootup\n"; + analytics_insertion("auto database upgrade world", $db . " :: Binary DB Version / Local DB Version :: " . $binary_database_version . " / " . $local_database_version); - - exit; } #::: Make sure that we didn't pass any arugments to the script else { + if ($local_database_version > $binary_database_version) { + print "[Update] Database version is ahead of current binaries...\n"; + } + if (!$db) { print "[eqemu_server.pl] No database connection found... Running without\n"; } show_menu_prompt(); } } + + #::: Bots + $binary_database_version = trim($db_version[2]); + if ($binary_database_version > 0) { + $local_database_version = get_bots_db_version(); + + #::: We ran world - Database needs to update, lets backup and run updates and continue world bootup + if ($binary_database_version == $local_database_version && $ARGV[0] eq "ran_from_world") { + print "[Update] Bots database up to date...\n"; + } + else { + if ($local_database_version < $binary_database_version && $ARGV[0] eq "ran_from_world") { + print "[Update] Bots Database not up to date with binaries... Automatically updating...\n"; + if (!$db_already_backed_up) { + print "[Update] Issuing database backup first...\n"; + database_dump_compress(); + } + print "[Update] Updating bots database...\n"; + sleep(1); + bots_db_management(); + bots_db_management(); + + analytics_insertion("auto database bots upgrade world", $db . " :: Binary DB Version / Local DB Version :: " . $binary_database_version . " / " . $local_database_version); + } + + #::: Make sure that we didn't pass any arugments to the script + else { + if ($local_database_version > $binary_database_version) { + print "[Update] Bots database version is ahead of current binaries...\n"; + } + + if (!$db) { print "[eqemu_server.pl] No database connection found... Running without\n"; } + show_menu_prompt(); + } + } + } + + print "[Update] Continuing bootup\n"; } sub check_internet_connection { @@ -629,7 +646,7 @@ sub do_self_update_check_routine { #::: Check for internet connection before updating if (!$has_internet_connection) { - print "[Update] Cannot check update without internet connection...\n"; + print "[Update] Cannot check self-update without internet connection...\n"; return; } @@ -819,7 +836,7 @@ sub setup_bots { build_linux_source("bots"); } bots_db_management(); - run_database_check(); + bots_db_management(); print "Bots should be setup, run your server and the bot command should be available in-game (type '^help')\n"; } @@ -959,7 +976,7 @@ sub show_menu_prompt { } elsif ($input eq "check_bot_db_updates") { bots_db_management(); - run_database_check(); + bots_db_management(); $dc = 1; } elsif ($input eq "setup_loginserver") { @@ -1400,6 +1417,7 @@ sub remove_duplicate_rule_values { sub copy_file { $l_source_file = $_[0]; $l_destination_file = $_[1]; + if ($l_destination_file =~ /\//i) { my @directory_path = split('/', $l_destination_file); $build_path = ""; @@ -1418,6 +1436,7 @@ sub copy_file { $directory_index++; } } + copy $l_source_file, $l_destination_file; } @@ -2205,54 +2224,43 @@ sub convert_existing_bot_data { } } +sub get_main_db_version { + $main_local_db_version = trim(get_mysql_result("SELECT version FROM db_version LIMIT 1")); + return $main_local_db_version; +} + sub get_bots_db_version { #::: Check if bots_version column exists... if (get_mysql_result("SHOW COLUMNS FROM db_version LIKE 'bots_version'") eq "" && $db) { print get_mysql_result("ALTER TABLE db_version ADD bots_version int(11) DEFAULT '0' AFTER version;"); print "[Database] Column 'bots_version' does not exists.... Adding to 'db_version' table...\n\n"; } + $bots_local_db_version = trim(get_mysql_result("SELECT bots_version FROM db_version LIMIT 1")); return $bots_local_db_version; } +#::: Safe for call from world startup or menu option sub bots_db_management { - - my $world_path = "world"; - if (-e "bin/world") { - $world_path = "bin/world"; - } - - #::: Get Binary DB version - if ($OS eq "Windows") { - @db_version = split(': ', `$world_path db_version`); - } - if ($OS eq "Linux") { - @db_version = split(': ', `./$world_path db_version`); - } - - #::: Main Binary Database version - $binary_database_version = trim($db_version[2]); - #::: If we have stale data from main db run if ($db_run_stage > 0 && $bots_db_management == 0) { clear_database_runs(); } - + + #::: Main Binary Database version + $binary_database_version = trim($db_version[2]); if ($binary_database_version == 0) { print "[Database] Your server binaries (world/zone) are not compiled for bots...\n\n"; return; } - + $local_database_version = get_bots_db_version(); + #::: Set on flag for running bot updates... $bots_db_management = 1; - - $bots_local_db_version = get_bots_db_version(); - - $local_database_version = $bots_local_db_version; - run_database_check(); } +#::: Safe for call from world startup or menu option sub main_db_management { #::: If we have stale data from bots db run if ($db_run_stage > 0 && $bots_db_management == 1) { @@ -2261,6 +2269,7 @@ sub main_db_management { #::: Main Binary Database version $binary_database_version = trim($db_version[1]); + $local_database_version = get_main_db_version(); $bots_db_management = 0; run_database_check(); @@ -2304,14 +2313,20 @@ sub run_database_check { $file_name = trim($m_d{$val}[1]); print "[Database] Running Update: " . $val . " - " . $file_name . "\n"; print get_mysql_result_from_file("db_update/$file_name"); - print get_mysql_result("UPDATE db_version SET version = $val WHERE version < $val"); - - if ($bots_db_management == 1 && $val == 9000) { - modify_db_for_bots(); + + if ($bots_db_management == 1) { + print get_mysql_result("UPDATE db_version SET bots_version = $val WHERE bots_version < $val"); + + if ($val == 9000) { + modify_db_for_bots(); + } } - - if ($val == 9138) { - fix_quest_factions(); + else { + print get_mysql_result("UPDATE db_version SET version = $val WHERE version < $val"); + + if ($val == 9138) { + fix_quest_factions(); + } } } $db_run_stage = 2; @@ -2343,6 +2358,7 @@ sub run_database_check { $revision_check = $local_database_version; } else { + #::: This does not negatively affect bots $revision_check = 1000; if (get_mysql_result("SHOW TABLES LIKE 'character_data'") ne "") { $revision_check = 8999; @@ -2431,16 +2447,19 @@ sub run_database_check { sub fetch_missing_db_update { $db_update = $_[0]; $update_file = $_[1]; - if ($db_update >= 9000) { - if ($bots_db_management == 1) { + + if ($bots_db_management == 1) { + if ($db_update >= 9000) { get_remote_file($eqemu_repository_request_url . "utils/sql/git/bots/required/" . $update_file, "db_update/" . $update_file . ""); } - else { + } + else { + if ($db_update >= 9000) { get_remote_file($eqemu_repository_request_url . "utils/sql/git/required/" . $update_file, "db_update/" . $update_file . ""); } - } - elsif ($db_update >= 5000 && $db_update <= 9000) { - get_remote_file($eqemu_repository_request_url . "utils/sql/svn/" . $update_file, "db_update/" . $update_file . ""); + elsif ($db_update >= 5000 && $db_update <= 9000) { + get_remote_file($eqemu_repository_request_url . "utils/sql/svn/" . $update_file, "db_update/" . $update_file . ""); + } } } From 00f118cfb49a0763d97973a248c9286e8d82ff0b Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 3 Feb 2020 08:36:14 -0500 Subject: [PATCH 15/60] Removed bot rule and command update code from setup because of auto-injection/removal feature added to server code [skip ci] --- utils/scripts/eqemu_server.pl | 73 ----------------------------------- 1 file changed, 73 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 929d9a79a..0568ca26a 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -2020,15 +2020,6 @@ sub do_bots_db_schema_drop { print get_mysql_result_from_file("db_update/drop_bots.sql"); print "[Database] Removing bot database tables...\n"; - print get_mysql_result("DELETE FROM `rule_values` WHERE `rule_name` LIKE 'Bots:%';"); - - if (get_mysql_result("SHOW TABLES LIKE 'commands'") ne "" && $db) { - print get_mysql_result("DELETE FROM `commands` WHERE `command` LIKE 'bot';"); - } - - if (get_mysql_result("SHOW TABLES LIKE 'command_settings'") ne "" && $db) { - print get_mysql_result("DELETE FROM `command_settings` WHERE `command` LIKE 'bot';"); - } if (get_mysql_result("SHOW KEYS FROM `group_id` WHERE `Key_name` LIKE 'PRIMARY'") ne "" && $db) { print get_mysql_result("ALTER TABLE `group_id` DROP PRIMARY KEY;"); @@ -2062,70 +2053,6 @@ sub modify_db_for_bots { } print get_mysql_result("ALTER TABLE `group_id` ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`, `ismerc`);"); - if (get_mysql_result("SHOW TABLES LIKE 'command_settings'") ne "" && get_mysql_result("SELECT `command` FROM `command_settings` WHERE `command` LIKE 'bot'") eq "" && $db) { - print get_mysql_result("INSERT INTO `command_settings` VALUES ('bot', '0', '');"); - } - - if (get_mysql_result("SHOW TABLES LIKE 'commands'") ne "" && get_mysql_result("SELECT `command` FROM `commands` WHERE `command` LIKE 'bot'") eq "" && $db) { - print get_mysql_result("INSERT INTO `commands` VALUES ('bot', '0');"); - } - - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:BotAAExpansion'") ne "" && $db) { - print get_mysql_result("UPDATE `rule_values` SET `rule_name` = 'Bots:AAExpansion' WHERE `rule_name` LIKE 'Bots:BotAAExpansion';"); - } - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:AAExpansion'") eq "" && $db) { - print get_mysql_result("INSERT INTO `rule_values` VALUES ('1', 'Bots:AAExpansion', '8', 'The expansion through which bots will obtain AAs');"); - } - - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:CreateBotCount'") ne "" && $db) { - print get_mysql_result("UPDATE `rule_values` SET `rule_name` = 'Bots:CreationLimit' WHERE `rule_name` LIKE 'Bots:CreateBotCount';"); - } - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:CreationLimit'") eq "" && $db) { - print get_mysql_result("INSERT INTO `rule_values` VALUES ('1', 'Bots:CreationLimit', '150', 'Number of bots that each account can create');"); - } - - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:BotFinishBuffing'") ne "" && $db) { - print get_mysql_result("UPDATE `rule_values` SET `rule_name` = 'Bots:FinishBuffing' WHERE `rule_name` LIKE 'Bots:BotFinishBuffing';"); - } - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:FinishBuffing'") eq "" && $db) { - print get_mysql_result("INSERT INTO `rule_values` VALUES ('1', 'Bots:FinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.');"); - } - - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:BotGroupBuffing'") ne "" && $db) { - print get_mysql_result("UPDATE `rule_values` SET `rule_name` = 'Bots:GroupBuffing' WHERE `rule_name` LIKE 'Bots:BotGroupBuffing';"); - } - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:GroupBuffing'") eq "" && $db) { - print get_mysql_result("INSERT INTO `rule_values` VALUES ('1', 'Bots:GroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.');"); - } - - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:BotManaRegen'") ne "" && $db) { - print get_mysql_result("UPDATE `rule_values` SET `rule_name` = 'Bots:ManaRegen' WHERE `rule_name` LIKE 'Bots:BotManaRegen';"); - } - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:ManaRegen'") eq "" && $db) { - print get_mysql_result("INSERT INTO `rule_values` VALUES ('1', 'Bots:ManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.');"); - } - - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:BotQuest'") ne "" && $db) { - print get_mysql_result("UPDATE `rule_values` SET `rule_name` = 'Bots:QuestableSpawnLimit' WHERE `rule_name` LIKE 'Bots:BotQuest';"); - } - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:QuestableSpawnLimit'") eq "" && $db) { - print get_mysql_result("INSERT INTO `rule_values` VALUES ('1', 'Bots:QuestableSpawnLimit', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl');"); - } - - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:BotSpellQuest'") ne "" && $db) { - print get_mysql_result("UPDATE `rule_values` SET `rule_name` = 'Bots:QuestableSpells' WHERE `rule_name` LIKE 'Bots:BotSpellQuest';"); - } - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:QuestableSpells'") eq "" && $db) { - print get_mysql_result("INSERT INTO `rule_values` VALUES ('1', 'Bots:QuestableSpells', 'false', 'Anita Thrall\\\'s (Anita_Thrall.pl) Bot Spell Scriber quests.');"); - } - - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:SpawnBotCount'") ne "" && $db) { - print get_mysql_result("UPDATE `rule_values` SET `rule_name` = 'Bots:SpawnLimit' WHERE `rule_name` LIKE 'Bots:SpawnBotCount';"); - } - if (get_mysql_result("SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:SpawnLimit'") eq "" && $db) { - print get_mysql_result("INSERT INTO `rule_values` VALUES ('1', 'Bots:SpawnLimit', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid');"); - } - convert_existing_bot_data(); } From 5fefdfcc170a9f42beee3381c7bb000e4e9f36a0 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 3 Feb 2020 12:50:05 -0500 Subject: [PATCH 16/60] Added new DefaultGuild rule to Character --- common/ruletypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index dab1d02e1..a7b70bee6 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -159,6 +159,7 @@ RULE_BOOL(Character, PetsUseReagents, true, "Pets use reagent on spells") RULE_BOOL(Character, DismountWater, true, "Dismount horses when entering water") RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing") RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted") +RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) From f9b3b7aecf8c111cd7f87eb166075036c6973294 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 3 Feb 2020 12:54:26 -0500 Subject: [PATCH 17/60] Implement DefaultGuild rule --- common/database.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/common/database.cpp b/common/database.cpp index ac5ae16bf..581f4c082 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -338,6 +338,27 @@ bool Database::ReserveName(uint32 account_id, char* name) { query = StringFormat("INSERT INTO `character_data` SET `account_id` = %i, `name` = '%s'", account_id, name); results = QueryDatabase(query); if (!results.Success() || results.ErrorMessage() != ""){ return false; } + + // Put character into the default guild if rule is being used. + int guild_id = RuleI(Character, DefaultGuild); + + if (guild_id != 0) { + int char_id=-1; + query = StringFormat("select `id` FROM `character_data` WHERE `name` = '%s'", name); + results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + char_id = atoi(row[0]); + } + + if (char_id > -1) { + query = StringFormat("INSERT INTO `guild_members` SET `char_id` = %i, `guild_id` = '%i'", char_id, guild_id); + results = QueryDatabase(query); + if (!results.Success() || results.ErrorMessage() != ""){ + LogInfo("Could not put character [{}] into default Guild", name); + } + } + } + return true; } From ad1f18306b99a802b7c8751e1875dafda1df4a7b Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 3 Feb 2020 13:47:16 -0500 Subject: [PATCH 18/60] Update command.cpp Fix #size command to be useful for anyone using the model field in npc_types. All will remain the same for everyone else. --- zone/command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/command.cpp b/zone/command.cpp index 877529c2b..757e2d522 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2543,7 +2543,7 @@ void command_size(Client *c, const Seperator *sep) else if (!target) c->Message(Chat::White,"Error: this command requires a target"); else { - uint16 Race = target->GetRace(); + uint16 Race = target->GetModel(); uint8 Gender = target->GetGender(); uint8 Texture = 0xFF; uint8 HelmTexture = 0xFF; From 342012c4f4e61d09b3f03f5e9fd061b60830a2bb Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 4 Feb 2020 00:24:43 -0600 Subject: [PATCH 19/60] Fix compile issue --- common/ruletypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 8d3b85d3e..897f249f4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -294,7 +294,7 @@ RULE_BOOL(Pathing, Find, true, "Enable pathing for FindPerson requests from the RULE_BOOL(Pathing, Fear, true, "Enable pathing for fear") RULE_REAL(Pathing, NavmeshStepSize, 100.0f, "") RULE_REAL(Pathing, ShortMovementUpdateRange, 130.0f, "") -RULE_INT(Pathing, MaxNavmeshNodes, 4092) +RULE_INT(Pathing, MaxNavmeshNodes, 4092, "Max navmesh nodes in a traversable path") RULE_CATEGORY_END() RULE_CATEGORY(Watermap) From d7138e84c011c684d3d0ab798c646f6763dc17c8 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Tue, 4 Feb 2020 18:27:59 -0500 Subject: [PATCH 20/60] Make consent variable names more descriptive and replace char pointer parameters with std::string Use fmt::format for SQL statement when updating corpse guild id --- zone/client.cpp | 9 +++------ zone/client.h | 2 +- zone/corpse.cpp | 54 +++++++++++++++++++++++-------------------------- zone/corpse.h | 18 ++++++++--------- zone/zonedb.cpp | 2 +- 5 files changed, 39 insertions(+), 46 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 31333dc57..003fe3c1a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6255,12 +6255,9 @@ void Client::DragCorpses() } } -void Client::ConsentCorpses(const char* consent_name, bool deny) +void Client::ConsentCorpses(std::string consent_name, bool deny) { - if (!consent_name) { - return; - } - else if (strcasecmp(consent_name, GetName()) == 0) { + if (strcasecmp(consent_name.c_str(), GetName()) == 0) { MessageString(Chat::Red, CONSENT_YOURSELF); } else if (!consent_throttle_timer.Check()) { @@ -6269,7 +6266,7 @@ void Client::ConsentCorpses(const char* consent_name, bool deny) else { auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strn0cpy(scs->grantname, consent_name, sizeof(scs->grantname)); + strn0cpy(scs->grantname, consent_name.c_str(), sizeof(scs->grantname)); strn0cpy(scs->ownername, GetName(), sizeof(scs->ownername)); strn0cpy(scs->zonename, "Unknown", sizeof(scs->zonename)); scs->permission = deny ? 0 : 1; diff --git a/zone/client.h b/zone/client.h index 7f6a9bd9e..ecbc67b04 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1139,7 +1139,7 @@ public: inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } void DragCorpses(); inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } - void ConsentCorpses(const char* consent_name, bool deny = false); + void ConsentCorpses(std::string consent_name, bool deny = false); void SendAltCurrencies(); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 3714ea7f7..0ac100997 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -138,7 +138,7 @@ Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std: pc->drakkin_details = pcs->drakkin_details; pc->IsRezzed(rezzed); pc->become_npc = false; - pc->consent_guild_id = guild_consent_id; + pc->consented_guild_id = guild_consent_id; pc->UpdateEquipmentLight(); // itemlist populated above..need to determine actual values @@ -285,15 +285,15 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( if (client->AutoConsentGroupEnabled()) { Group* grp = client->GetGroup(); - consent_group_id = grp ? grp->GetID() : 0; + consented_group_id = grp ? grp->GetID() : 0; } if (client->AutoConsentRaidEnabled()) { Raid* raid = client->GetRaid(); - consent_raid_id = raid ? raid->GetID() : 0; + consented_raid_id = raid ? raid->GetID() : 0; } - consent_guild_id = client->AutoConsentGuildEnabled() ? client->GuildID() : 0; + consented_guild_id = client->AutoConsentGuildEnabled() ? client->GuildID() : 0; is_corpse_changed = true; rez_experience = in_rezexp; @@ -624,11 +624,11 @@ bool Corpse::Save() { /* Create New Corpse*/ if (corpse_db_id == 0) { - corpse_db_id = database.SaveCharacterCorpse(char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, consent_guild_id); + corpse_db_id = database.SaveCharacterCorpse(char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, consented_guild_id); } /* Update Corpse Data */ else{ - corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, consent_guild_id, IsRezzed()); + corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, consented_guild_id, IsRezzed()); } safe_delete_array(dbpc); @@ -660,27 +660,23 @@ void Corpse::DepopPlayerCorpse() { player_corpse_depop = true; } -void Corpse::AddConsentName(const char* add_name) +void Corpse::AddConsentName(std::string consent_player_name) { - if (add_name) { - for (const auto& n : consent_names) { - if (strcasecmp(n.c_str(), add_name) == 0) { - return; - } + for (const auto& consented_player_name : consented_player_names) { + if (strcasecmp(consented_player_name.c_str(), consent_player_name.c_str()) == 0) { + return; } - consent_names.emplace_back(add_name); } + consented_player_names.emplace_back(consent_player_name); } -void Corpse::RemoveConsentName(const char* rem_name) +void Corpse::RemoveConsentName(std::string consent_player_name) { - if (rem_name) { - consent_names.erase(std::remove_if(consent_names.begin(), consent_names.end(), - [rem_name](const std::string& n) { - return strcasecmp(n.c_str(), rem_name) == 0; - } - ), consent_names.end()); - } + consented_player_names.erase(std::remove_if(consented_player_names.begin(), consented_player_names.end(), + [consent_player_name](const std::string& consented_player_name) { + return strcasecmp(consented_player_name.c_str(), consent_player_name.c_str()) == 0; + } + ), consented_player_names.end()); } uint32 Corpse::CountItems() { @@ -1477,27 +1473,27 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { else { bool consented = false; - for (const auto& n : consent_names) { - if (strcasecmp(client->GetName(), n.c_str()) == 0) { + for (const auto& consented_player_name : consented_player_names) { + if (strcasecmp(client->GetName(), consented_player_name.c_str()) == 0) { consented = true; break; } } - if (!consented && consent_guild_id && consent_guild_id != GUILD_NONE) { - if (client->GuildID() == consent_guild_id) { + if (!consented && consented_guild_id && consented_guild_id != GUILD_NONE) { + if (client->GuildID() == consented_guild_id) { consented = true; } } - if (!consented && consent_group_id) { + if (!consented && consented_group_id) { Group* grp = client->GetGroup(); - if (grp && grp->GetID() == consent_group_id) { + if (grp && grp->GetID() == consented_group_id) { consented = true; } } - if (!consented && consent_raid_id) { + if (!consented && consented_raid_id) { Raid* raid = client->GetRaid(); - if (raid && raid->GetID() == consent_raid_id) { + if (raid && raid->GetID() == consented_raid_id) { consented = true; } } diff --git a/zone/corpse.h b/zone/corpse.h index 02eb707a4..d453c7e9e 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -74,11 +74,11 @@ class Corpse : public Mob { uint32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); } uint32 GetRezTime() { if (!corpse_rez_timer.Enabled()) return 0; else return corpse_rez_timer.GetRemainingTime(); } void SetDecayTimer(uint32 decay_time); - void SetConsentGroupID(uint32 id) { if (IsPlayerCorpse()) { consent_group_id = id; } } - void SetConsentRaidID(uint32 id) { if (IsPlayerCorpse()) { consent_raid_id = id; } } - void SetConsentGuildID(uint32 id) { if (IsPlayerCorpse()) { consent_guild_id = id; } } - void AddConsentName(const char* name); - void RemoveConsentName(const char* name); + void SetConsentGroupID(uint32 group_id) { if (IsPlayerCorpse()) { consented_group_id = group_id; } } + void SetConsentRaidID(uint32 raid_id) { if (IsPlayerCorpse()) { consented_raid_id = raid_id; } } + void SetConsentGuildID(uint32 guild_id) { if (IsPlayerCorpse()) { consented_guild_id = guild_id; } } + void AddConsentName(std::string consent_player_name); + void RemoveConsentName(std::string consent_player_name); void Delete(); void Bury(); @@ -147,9 +147,9 @@ private: int32 player_kill_item; /* Determines if Player Kill Item */ uint32 corpse_db_id; /* Corpse Database ID (Player Corpse) */ uint32 char_id; /* Character ID */ - uint32 consent_group_id = 0; /* consented group id */ - uint32 consent_raid_id = 0; /* consented raid id */ - uint32 consent_guild_id = 0; /* consented guild id */ + uint32 consented_group_id = 0; + uint32 consented_raid_id = 0; + uint32 consented_guild_id = 0; ItemList itemlist; /* Internal Item list used for corpses */ uint32 copper; uint32 silver; @@ -168,7 +168,7 @@ private: Timer corpse_graveyard_timer; Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */ EQEmu::TintProfile item_tint; - std::vector consent_names; + std::vector consented_player_names; LootRequestType loot_request_type; }; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 899861c3d..ea4287f9d 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -4321,7 +4321,7 @@ uint32 ZoneDatabase::UpdateCharacterCorpse(uint32 db_id, uint32 char_id, const c uint32 ZoneDatabase::UpdateCharacterCorpseConsent(uint32 charid, uint32 guildid) { - std::string query = StringFormat("UPDATE `character_corpses` SET `guild_consent_id` = %u WHERE `charid` = %u", guildid, charid); + std::string query = fmt::format("UPDATE `character_corpses` SET `guild_consent_id` = '{}' WHERE charid = '{}'", guildid, charid); auto results = QueryDatabase(query); return results.RowsAffected(); } From 86b6f543b97b44b45a22b1941e5be46a33e26d75 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 4 Feb 2020 19:07:55 -0500 Subject: [PATCH 21/60] Reworked the update cycle of eqemu_server.pl to process each update cumulatively [skip ci] --- utils/scripts/eqemu_server.pl | 262 +++++++++++++++++----------------- 1 file changed, 130 insertions(+), 132 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 0568ca26a..6a130c852 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -493,9 +493,7 @@ sub do_installer_routines { #::: Download PEQ latest fetch_peq_db_full(); - print "[Database] Fetching Latest Database Updates...\n"; - main_db_management(); - print "[Database] Applying Latest Database Updates...\n"; + print "[Database] Fetching and Applying Latest Database Updates...\n"; main_db_management(); remove_duplicate_rule_values(); @@ -547,7 +545,6 @@ sub check_for_world_bootup_database_update { print "[Update] Updating database...\n"; sleep(1); main_db_management(); - main_db_management(); analytics_insertion("auto database upgrade world", $db . " :: Binary DB Version / Local DB Version :: " . $binary_database_version . " / " . $local_database_version); } @@ -582,7 +579,6 @@ sub check_for_world_bootup_database_update { print "[Update] Updating bots database...\n"; sleep(1); bots_db_management(); - bots_db_management(); analytics_insertion("auto database bots upgrade world", $db . " :: Binary DB Version / Local DB Version :: " . $binary_database_version . " / " . $local_database_version); } @@ -836,7 +832,6 @@ sub setup_bots { build_linux_source("bots"); } bots_db_management(); - bots_db_management(); print "Bots should be setup, run your server and the bot command should be available in-game (type '^help')\n"; } @@ -970,12 +965,10 @@ sub show_menu_prompt { $dc = 1; } elsif ($input eq "check_db_updates") { - main_db_management(); main_db_management(); $dc = 1; } elsif ($input eq "check_bot_db_updates") { - bots_db_management(); bots_db_management(); $dc = 1; } @@ -2184,6 +2177,11 @@ sub bots_db_management { #::: Set on flag for running bot updates... $bots_db_management = 1; + + if ($local_database_version > $binary_database_version) { + print "[Update] Bots database version is ahead of current binaries...\n"; + } + run_database_check(); } @@ -2199,6 +2197,12 @@ sub main_db_management { $local_database_version = get_main_db_version(); $bots_db_management = 0; + + if ($local_database_version > $binary_database_version) { + print "[Update] Database version is ahead of current binaries...\n"; + return; + } + run_database_check(); } @@ -2208,81 +2212,46 @@ sub clear_database_runs { %m_d = (); #::: Clear updates... @total_updates = (); - #::: Clear stage - $db_run_stage = 0; } #::: Responsible for Database Upgrade Routines sub run_database_check { - + if (!$db) { print "No database present, check your eqemu_config.xml for proper MySQL/MariaDB configuration...\n"; return; } - - if (!@total_updates) { - #::: Pull down bots database manifest - if ($bots_db_management == 1) { - print "[Database] Retrieving latest bots database manifest...\n"; - get_remote_file($eqemu_repository_request_url . "utils/sql/git/bots/bots_db_update_manifest.txt", "db_update/db_update_manifest.txt"); - } - #::: Pull down mainstream database manifest - else { - print "[Database] Retrieving latest database manifest...\n"; - get_remote_file($eqemu_repository_request_url . "utils/sql/db_update_manifest.txt", "db_update/db_update_manifest.txt"); - } + + #::: Pull down bots database manifest + if ($bots_db_management == 1) { + print "[Database] Retrieving latest bots database manifest...\n"; + get_remote_file($eqemu_repository_request_url . "utils/sql/git/bots/bots_db_update_manifest.txt", "db_update/db_update_manifest.txt"); } - - #::: Run 2 - Running pending updates... - if (@total_updates || $db_run_stage == 1) { - @total_updates = sort @total_updates; - foreach my $val (@total_updates) { - $file_name = trim($m_d{$val}[1]); - print "[Database] Running Update: " . $val . " - " . $file_name . "\n"; - print get_mysql_result_from_file("db_update/$file_name"); - - if ($bots_db_management == 1) { - print get_mysql_result("UPDATE db_version SET bots_version = $val WHERE bots_version < $val"); - - if ($val == 9000) { - modify_db_for_bots(); - } - } - else { - print get_mysql_result("UPDATE db_version SET version = $val WHERE version < $val"); - - if ($val == 9138) { - fix_quest_factions(); - } - } - } - $db_run_stage = 2; - } - #::: Run 1 - Initial checking of needed updates... + #::: Pull down mainstream database manifest else { - print "[Database] Reading manifest...\n"; - - use Data::Dumper; - open(FILE, "db_update/db_update_manifest.txt"); - while () { - chomp; - $o = $_; - if ($o =~ /#/i) { - next; - } - - @manifest = split('\|', $o); - $m_d{$manifest[0]} = [ @manifest ]; - } - #::: Setting Manifest stage... - $db_run_stage = 1; + print "[Database] Retrieving latest database manifest...\n"; + get_remote_file($eqemu_repository_request_url . "utils/sql/db_update_manifest.txt", "db_update/db_update_manifest.txt"); } - - @total_updates = (); - + + #::: Parse manifest + print "[Database] Reading manifest...\n"; + + use Data::Dumper; + open(FILE, "db_update/db_update_manifest.txt"); + while () { + chomp; + $o = $_; + if ($o =~ /#/i) { + next; + } + + @manifest = split('\|', $o); + $m_d{$manifest[0]} = [ @manifest ]; + } + #::: This is where we set checkpoints for where a database might be so we don't check so far back in the manifest... if ($local_database_version >= 9000) { - $revision_check = $local_database_version; + $revision_check = $local_database_version + 1; } else { #::: This does not negatively affect bots @@ -2291,72 +2260,22 @@ sub run_database_check { $revision_check = 8999; } } - - #::: Iterate through Manifest backwards from binary version down to local version... + + @total_updates = (); + + #::: Fetch and register sqls for this database update cycle for ($i = $revision_check; $i <= $binary_database_version; $i++) { if (!defined($m_d{$i}[0])) { next; } - - $file_name = trim($m_d{$i}[1]); - $query_check = trim($m_d{$i}[2]); - $match_type = trim($m_d{$i}[3]); - $match_text = trim($m_d{$i}[4]); - - #::: Match type update - if ($match_type eq "contains") { - if (trim(get_mysql_result($query_check)) =~ /$match_text/i) { - print "[Database] missing update: " . $i . " '" . $file_name . "' \n"; - fetch_missing_db_update($i, $file_name); - push(@total_updates, $i); - } - else { - print "[Database] has update (" . $i . ") '" . $file_name . "' \n"; - } - print_match_debug(); - print_break(); - } - if ($match_type eq "missing") { - if (get_mysql_result($query_check) =~ /$match_text/i) { - print "[Database] has update (" . $i . ") '" . $file_name . "' \n"; - next; - } - else { - print "[Database] missing update: " . $i . " '" . $file_name . "' \n"; - fetch_missing_db_update($i, $file_name); - push(@total_updates, $i); - } - print_match_debug(); - print_break(); - } - if ($match_type eq "empty") { - if (get_mysql_result($query_check) eq "") { - print "[Database] missing update: " . $i . " '" . $file_name . "' \n"; - fetch_missing_db_update($i, $file_name); - push(@total_updates, $i); - } - else { - print "[Database] has update (" . $i . ") '" . $file_name . "' \n"; - } - print_match_debug(); - print_break(); - } - if ($match_type eq "not_empty") { - if (get_mysql_result($query_check) ne "") { - print "[Database] missing update: " . $i . " '" . $file_name . "' \n"; - fetch_missing_db_update($i, $file_name); - push(@total_updates, $i); - } - else { - print "[Database] has update (" . $i . ") '" . $file_name . "' \n"; - } - print_match_debug(); - print_break(); - } + + $file_name = trim($m_d{$i}[1]); + print "[Database] fetching update: " . $i . " '" . $file_name . "' \n"; + fetch_missing_db_update($i, $file_name); + push(@total_updates, $i); } - print "\n"; - - if (scalar(@total_updates) == 0 && $db_run_stage == 2) { + + if (scalar(@total_updates) == 0) { print "[Database] No updates need to be run...\n"; if ($bots_db_management == 1) { print "[Database] Setting Database to Bots Binary Version (" . $binary_database_version . ") if not already...\n\n"; @@ -2366,11 +2285,90 @@ sub run_database_check { print "[Database] Setting Database to Binary Version (" . $binary_database_version . ") if not already...\n\n"; get_mysql_result("UPDATE db_version SET version = $binary_database_version "); } - + clear_database_runs(); + return; + } + + #::: Execute pending updates + @total_updates = sort @total_updates; + foreach my $val (@total_updates) { + $file_name = trim($m_d{$val}[1]); + $query_check = trim($m_d{$val}[2]); + $match_type = trim($m_d{$val}[3]); + $match_text = trim($m_d{$val}[4]); + + #::: Match type update + if ($match_type eq "contains") { + if (trim(get_mysql_result($query_check)) =~ /$match_text/i) { + print "[Database] running update: " . $val . " '" . $file_name . "' \n"; + print get_mysql_result_from_file("db_update/$file_name"); + } + else { + print "[Database] has update (" . $val . ") '" . $file_name . "' \n"; + } + print_match_debug(); + print_break(); + } + if ($match_type eq "missing") { + if (get_mysql_result($query_check) =~ /$match_text/i) { + print "[Database] has update (" . $val . ") '" . $file_name . "' \n"; + } + else { + print "[Database] running update: " . $val . " '" . $file_name . "' \n"; + print get_mysql_result_from_file("db_update/$file_name"); + } + print_match_debug(); + print_break(); + } + if ($match_type eq "empty") { + if (get_mysql_result($query_check) eq "") { + print "[Database] running update: " . $val . " '" . $file_name . "' \n"; + print get_mysql_result_from_file("db_update/$file_name"); + } + else { + print "[Database] has update (" . $val . ") '" . $file_name . "' \n"; + } + print_match_debug(); + print_break(); + } + if ($match_type eq "not_empty") { + if (get_mysql_result($query_check) ne "") { + print "[Database] running update: " . $val . " '" . $file_name . "' \n"; + print get_mysql_result_from_file("db_update/$file_name"); + } + else { + print "[Database] has update (" . $val . ") '" . $file_name . "' \n"; + } + print_match_debug(); + print_break(); + } + + if ($bots_db_management == 1) { + print get_mysql_result("UPDATE db_version SET bots_version = $val WHERE bots_version < $val"); + + if ($val == 9000) { + modify_db_for_bots(); + } + } + else { + print get_mysql_result("UPDATE db_version SET version = $val WHERE version < $val"); + + if ($val == 9138) { + fix_quest_factions(); + } + } + } + + if ($bots_db_management == 1) { + print "[Database] Bots database update cycle complete at version: " . get_bots_db_version() . "\n"; + } + else { + print "[Database] Database update cycle complete at version: " . get_main_db_version() . "\n"; } } + sub fetch_missing_db_update { $db_update = $_[0]; $update_file = $_[1]; From 929e4c131744a08e4c52f173d27f35e7260bd150 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 4 Feb 2020 23:52:18 -0600 Subject: [PATCH 22/60] Adjust verbiage [skip ci] --- zone/zone_reload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/zone_reload.cpp b/zone/zone_reload.cpp index 5a9aeda99..42424dae2 100644 --- a/zone/zone_reload.cpp +++ b/zone/zone_reload.cpp @@ -36,7 +36,7 @@ void ZoneReload::HotReloadQuests() zone->SetQuestHotReloadQueued(false); LogHotReload( - "[Quests] Reloading scripts in zone [{}] repop_with_reload [{}] reset_timers [{}] when_not_in_combat [{}] Time [{:.4f}]", + "[Quests] Reloading [{}] repop [{}] reset_timers [{}] repop_when_not_in_combat [{}] Time [{:.4f}]", zone->GetShortName(), (RuleB(HotReload, QuestsRepopWithReload) ? "true" : "false"), (RuleB(HotReload, QuestsResetTimersWithReload) ? "true" : "false"), From 861b879a948f281027c1b259c96982f446c19824 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 5 Feb 2020 01:34:29 -0600 Subject: [PATCH 23/60] Add GetCharacterTables() with table - key pair. Use in character hard deletes https://gist.github.com/Akkadius/f10e3757a0b52b971076643eccf9c5d0 --- common/database.cpp | 47 +++++----------------- common/database_schema.h | 85 +++++++++++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index ac5ae16bf..7e62d7935 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -45,6 +45,7 @@ #include "eq_packet_structs.h" #include "extprofile.h" #include "string_util.h" +#include "database_schema.h" extern Client client; @@ -386,46 +387,18 @@ bool Database::DeleteCharacter(char *character_name) { LogInfo("DeleteCharacter | Character [{}] ({}) is being [{}]", character_name, character_id, delete_type); - query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", character_id); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); + for (const auto& iter : DatabaseSchema::GetCharacterTables()) { + std::string table_name = iter.first; + std::string character_id_column_name = iter.second; + + QueryDatabase(fmt::format("DELETE FROM {} WHERE {} = {}", table_name, character_id_column_name, character_id)); + } + #ifdef BOTS query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", character_id); // note: only use of GetMobTypeById() -#else - query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", character_id); -#endif QueryDatabase(query); +#endif + return true; } diff --git a/common/database_schema.h b/common/database_schema.h index 73637c877..b497ad474 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -22,17 +22,76 @@ #define EQEMU_DATABASE_SCHEMA_H #include +#include namespace DatabaseSchema { /** - * Gets player tables + * Character-specific tables + * + * Does not included related meta-data tables such as 'guilds', 'accounts' + * @return + */ + static std::map GetCharacterTables() + { + return { + {"adventure_stats", "player_id"}, + {"buyer", "charid"}, + {"char_recipe_list", "char_id"}, + {"character_activities", "charid"}, + {"character_alt_currency", "char_id"}, + {"character_alternate_abilities", "id"}, + {"character_auras", "id"}, + {"character_bandolier", "id"}, + {"character_bind", "id"}, + {"character_buffs", "character_id"}, + {"character_corpses", "id"}, + {"character_currency", "id"}, + {"character_data", "id"}, + {"character_disciplines", "id"}, + {"character_enabledtasks", "charid"}, + {"character_inspect_messages", "id"}, + {"character_item_recast", "id"}, + {"character_languages", "id"}, + {"character_leadership_abilities", "id"}, + {"character_material", "id"}, + {"character_memmed_spells", "id"}, + {"character_pet_buffs", "char_id"}, + {"character_pet_info", "char_id"}, + {"character_pet_inventory", "char_id"}, + {"character_potionbelt", "id"}, + {"character_skills", "id"}, + {"character_spells", "id"}, + {"character_tasks", "charid"}, + {"character_tribute", "id"}, + {"completed_tasks", "charid"}, + {"data_buckets", "id"}, + {"faction_values", "char_id"}, + {"friends", "charid"}, + {"guild_members", "char_id"}, + {"guilds", "id"}, + {"instance_list_player", "id"}, + {"inventory", "charid"}, + {"inventory_snapshots", "charid"}, + {"keyring", "char_id"}, + {"mail", "charid"}, + {"player_titlesets", "char_id"}, + {"quest_globals", "charid"}, + {"timers", "char_id"}, + {"titles", "char_id"}, + {"trader", "char_id"}, + {"zone_flags", "charID"} + }; + } + + /** + * Gets all player and meta-data tables * * @return */ static std::vector GetPlayerTables() { - std::vector tables = { + return { "account", "account_ip", "account_flags", @@ -91,8 +150,6 @@ namespace DatabaseSchema { "trader_audit", "zone_flags" }; - - return tables; } /** @@ -102,7 +159,7 @@ namespace DatabaseSchema { */ static std::vector GetContentTables() { - std::vector tables = { + return { "aa_ability", "aa_actions", "aa_effects", @@ -188,8 +245,6 @@ namespace DatabaseSchema { "zone_server", "zoneserver_auth", }; - - return tables; } /** @@ -199,7 +254,7 @@ namespace DatabaseSchema { */ static std::vector GetServerTables() { - std::vector tables = { + return { "banned_ips", "bugs", "bug_reports", @@ -225,8 +280,6 @@ namespace DatabaseSchema { "saylink", "variables", }; - - return tables; } /** @@ -237,7 +290,7 @@ namespace DatabaseSchema { */ static std::vector GetStateTables() { - std::vector tables = { + return { "adventure_members", "chatchannels", "group_id", @@ -253,8 +306,6 @@ namespace DatabaseSchema { "spell_buckets", "spell_globals", }; - - return tables; } /** @@ -264,15 +315,13 @@ namespace DatabaseSchema { */ static std::vector GetLoginTables() { - std::vector tables = { + return { "login_accounts", "login_api_tokens", "login_server_admins", "login_server_list_types", "login_world_servers", }; - - return tables; } /** @@ -282,12 +331,10 @@ namespace DatabaseSchema { */ static std::vector GetVersionTables() { - std::vector tables = { + return { "db_version", "inventory_versions", }; - - return tables; } } From 49134644bc067734d011c0905a41181f0b7f79c8 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 5 Feb 2020 01:56:39 -0600 Subject: [PATCH 24/60] Update dbcore logging to use aliases --- common/dbcore.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/common/dbcore.cpp b/common/dbcore.cpp index 46c7fbe12..638fab4ae 100644 --- a/common/dbcore.cpp +++ b/common/dbcore.cpp @@ -115,14 +115,14 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo auto errorBuffer = new char[MYSQL_ERRMSG_SIZE]; snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); - /* Implement Logging at the Root */ + /** + * Error logging + */ if (mysql_errno(&mysql) > 0 && strlen(query) > 0) { - if (LogSys.log_settings[Logs::MySQLError].is_category_enabled == 1) - Log(Logs::General, Logs::MySQLError, "%i: %s \n %s", mysql_errno(&mysql), mysql_error(&mysql), query); + LogMySQLError("[{}] [{}]\n[{}]", mysql_errno(&mysql), mysql_error(&mysql), query); } return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql), errorBuffer); - } // successful query. get results. @@ -143,9 +143,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) { if ((strncasecmp(query, "select", 6) == 0)) { - LogF( - Logs::General, - Logs::MySQLQuery, + LogMySQLQuery( "{0} ({1} row{2} returned) ({3}s)", query, requestResult.RowCount(), @@ -154,9 +152,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo ); } else { - LogF( - Logs::General, - Logs::MySQLQuery, + LogMySQLQuery( "{0} ({1} row{2} affected) ({3}s)", query, requestResult.RowsAffected(), From b4f42c150f2135c499fd7aaba741980e91a0f757 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 5 Feb 2020 12:31:29 -0500 Subject: [PATCH 25/60] Update database.cpp Change variable_name and use LastInsertedID() to remove unneeded call. --- common/database.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 581f4c082..2dca2ba20 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -343,15 +343,9 @@ bool Database::ReserveName(uint32 account_id, char* name) { int guild_id = RuleI(Character, DefaultGuild); if (guild_id != 0) { - int char_id=-1; - query = StringFormat("select `id` FROM `character_data` WHERE `name` = '%s'", name); - results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - char_id = atoi(row[0]); - } - - if (char_id > -1) { - query = StringFormat("INSERT INTO `guild_members` SET `char_id` = %i, `guild_id` = '%i'", char_id, guild_id); + int character_id=results.LastInsertedID(); + if (character_id > -1) { + query = StringFormat("INSERT INTO `guild_members` SET `char_id` = %i, `guild_id` = '%i'", character_id, guild_id); results = QueryDatabase(query); if (!results.Success() || results.ErrorMessage() != ""){ LogInfo("Could not put character [{}] into default Guild", name); From 4accb4ea2ab361916019c3c93cccfdf9319a96c5 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 5 Feb 2020 14:41:36 -0500 Subject: [PATCH 26/60] Update ruletypes.h --- common/ruletypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 5797d614a..28231c32e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -494,6 +494,7 @@ RULE_INT(Combat, NPCAssistCapTimer, 6000, "Time in milliseconds a NPC will take RULE_BOOL(Combat, UseRevampHandToHand, false, "use h2h revamped dmg/delays I believe this was implemented during SoF") RULE_BOOL(Combat, ClassicMasterWu, false, "classic master wu uses a random special, modern doesn't") RULE_INT(Combat, LevelToStopDamageCaps, 0, "1 will effectively disable them, 20 should give basically same results as old incorrect system") +RULE_INT(Combat, LevelToStopACTwinkControl, 50, "1 will effectively disable it, 50 should give basically same results as current system") RULE_BOOL(Combat, ClassicNPCBackstab, false, "true disables npc facestab - npcs get normal attack if not behind") RULE_BOOL(Combat, UseNPCDamageClassLevelMods, true, "Uses GetClassLevelDamageMod calc in npc_scale_manager") RULE_BOOL(Combat, UseExtendedPoisonProcs, false, "Allow old school poisons to last until characrer zones, at a lower proc rate") From 6b27e8831557cefdd24172f5f64725cccf83135d Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 5 Feb 2020 14:42:37 -0500 Subject: [PATCH 27/60] Update attack.cpp --- zone/attack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 308592041..083176e39 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -799,7 +799,7 @@ int Mob::ACSum() // EQ math ac = (ac * 4) / 3; // anti-twink - if (IsClient() && GetLevel() < 50) + if (IsClient() && GetLevel() < RuleI(Combat, LevelToStopACTwinkControl)) ac = std::min(ac, 25 + 6 * GetLevel()); ac = std::max(0, ac + GetClassRaceACBonus()); if (IsNPC()) { From e1adffc4befb0bfb2d68ff62370f1310498119be Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 5 Feb 2020 15:32:07 -0500 Subject: [PATCH 28/60] Update npc.cpp --- zone/npc.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index ed5f37bb5..e88ce4b89 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -778,7 +778,15 @@ bool NPC::Process() } if (GetMana() < GetMaxMana()) { - SetMana(GetMana() + mana_regen + npc_sitting_regen_bonus); + int32 npc_idle_mana_regen_bonus = 2; + uint16 meditate_skill = GetSkill(EQEmu::skills::SkillMeditate); + if (!IsEngaged() && meditate_skill > 0) { + uint8 clevel = GetLevel(); + npc_idle_mana_regen_bonus = + (((meditate_skill / 10) + + (clevel - (clevel / 4))) / 4) + 4; + } + SetMana(GetMana() + mana_regen + npc_idle_mana_regen_bonus); } SendHPUpdate(); @@ -3195,4 +3203,4 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) } } -} \ No newline at end of file +} From e19db3b7f4af69ee3ce39a193401fcca63b72ca2 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 5 Feb 2020 16:25:24 -0500 Subject: [PATCH 29/60] Update npc.cpp --- zone/npc.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index e88ce4b89..006302126 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -778,17 +778,22 @@ bool NPC::Process() } if (GetMana() < GetMaxMana()) { - int32 npc_idle_mana_regen_bonus = 2; - uint16 meditate_skill = GetSkill(EQEmu::skills::SkillMeditate); - if (!IsEngaged() && meditate_skill > 0) { - uint8 clevel = GetLevel(); - npc_idle_mana_regen_bonus = - (((meditate_skill / 10) + - (clevel - (clevel / 4))) / 4) + 4; + if (RuleB(NPC, UseMeditateBasedManaRegen)) { + int32 npc_idle_mana_regen_bonus = 2; + uint16 meditate_skill = GetSkill(EQEmu::skills::SkillMeditate); + if (!IsEngaged() && meditate_skill > 0) { + uint8 clevel = GetLevel(); + npc_idle_mana_regen_bonus = + (((meditate_skill / 10) + + (clevel - (clevel / 4))) / 4) + 4; + } + SetMana(GetMana() + mana_regen + npc_idle_mana_regen_bonus); + } + else { + SetMana(GetMana() + mana_regen + npc_sitting_regen_bonus); } - SetMana(GetMana() + mana_regen + npc_idle_mana_regen_bonus); } - + SendHPUpdate(); if (zone->adv_data && !p_depop) { From 8dacadb4f9de2928c32b9bf882458a0d9a72d414 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 5 Feb 2020 16:26:46 -0500 Subject: [PATCH 30/60] Update ruletypes.h --- common/ruletypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 28231c32e..8e5793bc3 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -526,6 +526,7 @@ RULE_INT(NPC, NPCGatePercent, 20, "% at which the NPC Will attempt to gate at") RULE_BOOL(NPC, NPCGateNearBind, false, "Will NPC attempt to gate when near bind location?") RULE_INT(NPC, NPCGateDistanceBind, 75, "Distance from bind before NPC will attempt to gate") RULE_BOOL(NPC, NPCHealOnGate, true, "Will the NPC Heal on Gate") +RULE_BOOL(NPC, UseMeditateBasedManaRegen, false, "Based NPC ooc regen on Meditate skill") RULE_REAL(NPC, NPCHealOnGateAmount, 25, "How much the npc will heal on gate if enabled") RULE_CATEGORY_END() From 1528e7cb09e8635603b4f3441334b85cf8531033 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 5 Feb 2020 16:28:07 -0500 Subject: [PATCH 31/60] Update npc.cpp --- zone/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 006302126..96efc83a8 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -793,7 +793,7 @@ bool NPC::Process() SetMana(GetMana() + mana_regen + npc_sitting_regen_bonus); } } - + SendHPUpdate(); if (zone->adv_data && !p_depop) { From 916c88939cd11143901cfebdbb51e215d74782b8 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 5 Feb 2020 21:05:51 -0500 Subject: [PATCH 32/60] Added missing 'return' --- utils/scripts/eqemu_server.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 6a130c852..41d673418 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -2180,6 +2180,7 @@ sub bots_db_management { if ($local_database_version > $binary_database_version) { print "[Update] Bots database version is ahead of current binaries...\n"; + return; } run_database_check(); From 8bcef6c2e72d8e7b25c2f8302e88029907a6515b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 6 Feb 2020 01:08:53 -0500 Subject: [PATCH 33/60] Fix BodyType bug in GlobalLoot --- zone/loottables.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 3b5ae520f..500af9d70 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -518,7 +518,7 @@ void ZoneDatabase::LoadGlobalLoot() auto bodytypes = SplitString(row[9], '|'); for (auto &b : bodytypes) - e.AddRule(GlobalLoot::RuleTypes::Class, std::stoi(b)); + e.AddRule(GlobalLoot::RuleTypes::BodyType, std::stoi(b)); } zone->AddGlobalLootEntry(e); From 501204a4d2e843433d35956868c6b0cfb918879d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 6 Feb 2020 01:52:35 -0500 Subject: [PATCH 34/60] Add hot_zone filtering for global loot We do this in GlobalLootEntry::PassesRules since we want to check if the hot zone status changes during run time Value can be null, if null it's not checked. If the value is 0 the zone must not be a hot zone (I guess one might want that) and if it's not 0, the zone must be a hot zone --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + utils/sql/git/required/2020_02_06_globalloot.sql | 2 ++ zone/global_loot_manager.cpp | 9 +++++++++ zone/global_loot_manager.h | 1 + zone/loottables.cpp | 7 ++++++- 6 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 utils/sql/git/required/2020_02_06_globalloot.sql diff --git a/common/version.h b/common/version.h index ca37150ec..13579c3d2 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9148 +#define CURRENT_BINARY_DATABASE_VERSION 9149 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 8e3172b87..fdaa4a946 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -402,6 +402,7 @@ 9146|2020_01_10_character_soft_deletes.sql|SHOW COLUMNS FROM `character_data` LIKE 'deleted_at'|empty| 9147|2020_01_24_grid_centerpoint_wp.sql|SHOW COLUMNS FROM `grid_entries` LIKE 'centerpoint'|empty| 9148|2020_01_28_corpse_guild_consent_id.sql|SHOW COLUMNS FROM `character_corpses` LIKE 'guild_consent_id'|empty| +9149|2020_02_06_globalloot.sql|SHOW COLUMNS FROM `global_loot` LIKE 'hot_zone'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2020_02_06_globalloot.sql b/utils/sql/git/required/2020_02_06_globalloot.sql new file mode 100644 index 000000000..83144bbb8 --- /dev/null +++ b/utils/sql/git/required/2020_02_06_globalloot.sql @@ -0,0 +1,2 @@ +ALTER TABLE `global_loot` ADD `hot_zone` TINYINT NULL; + diff --git a/zone/global_loot_manager.cpp b/zone/global_loot_manager.cpp index ccdf88840..706769f77 100644 --- a/zone/global_loot_manager.cpp +++ b/zone/global_loot_manager.cpp @@ -1,6 +1,9 @@ #include "global_loot_manager.h" #include "npc.h" #include "client.h" +#include "zone.h" + +extern Zone *zone; std::vector GlobalLootManager::GetGlobalLootTables(NPC *mob) const { @@ -78,6 +81,12 @@ bool GlobalLootEntry::PassesRules(NPC *mob) const if (mob->GetBodyType() == r.value) bPassesBodyType = true; break; + case GlobalLoot::RuleTypes::HotZone: // value == 0 must not be hot_zone, value != must be hot_zone + if (zone->IsHotzone() && !r.value) + return false; + if (!zone->IsHotzone() && r.value) + return false; + break; default: break; } diff --git a/zone/global_loot_manager.h b/zone/global_loot_manager.h index fec5a7215..1de6e7831 100644 --- a/zone/global_loot_manager.h +++ b/zone/global_loot_manager.h @@ -17,6 +17,7 @@ enum class RuleTypes { BodyType = 4, Rare = 5, Raid = 6, + HotZone = 7, Max }; diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 500af9d70..794883c1f 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -464,7 +464,7 @@ void NPC::CheckGlobalLootTables() void ZoneDatabase::LoadGlobalLoot() { auto query = StringFormat("SELECT id, loottable_id, description, min_level, max_level, rare, raid, race, " - "class, bodytype, zone FROM global_loot WHERE enabled = 1"); + "class, bodytype, zone, hot_zone FROM global_loot WHERE enabled = 1"); auto results = QueryDatabase(query); if (!results.Success() || results.RowCount() == 0) @@ -482,6 +482,7 @@ void ZoneDatabase::LoadGlobalLoot() continue; } + GlobalLootEntry e(atoi(row[0]), atoi(row[1]), row[2] ? row[2] : ""); auto min_level = atoi(row[3]); @@ -521,6 +522,10 @@ void ZoneDatabase::LoadGlobalLoot() e.AddRule(GlobalLoot::RuleTypes::BodyType, std::stoi(b)); } + // null is not used + if (row[11]) + e.AddRule(GlobalLoot::RuleTypes::HotZone, atoi(row[11])); + zone->AddGlobalLootEntry(e); } } From 16ac6f624bcec91f8e3aeb2e75c2f7a60305f5b3 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 6 Feb 2020 01:59:18 -0500 Subject: [PATCH 35/60] Remove extra whitespace --- zone/loottables.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 794883c1f..0e5ba8d60 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -482,7 +482,6 @@ void ZoneDatabase::LoadGlobalLoot() continue; } - GlobalLootEntry e(atoi(row[0]), atoi(row[1]), row[2] ? row[2] : ""); auto min_level = atoi(row[3]); From 2f5909d4cbf5ee4f4f87cd781e9d1803dab8cf80 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 6 Feb 2020 14:20:18 -0500 Subject: [PATCH 36/60] Implement AA timers reset on death This is a field in the packet, live only uses this for Lay on Hands Currently I didn't add this to the packet since it has 0 effect on the client. We could move this field to aa_ranks which would give more flexibility for custom servers, but no one said they wanted it there. --- common/patches/rof2_structs.h | 2 +- common/patches/rof_structs.h | 2 +- common/patches/sod_structs.h | 2 +- common/patches/sof_structs.h | 2 +- common/patches/uf_structs.h | 2 +- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../required/2020_02_06_aa_reset_on_death.sql | 2 ++ zone/aa.cpp | 23 +++++++++++++++++-- zone/aa_ability.h | 1 + zone/attack.cpp | 5 ++++ zone/client.h | 1 + 12 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 utils/sql/git/required/2020_02_06_aa_reset_on_death.sql diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index cecafb767..10ce35654 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4401,7 +4401,7 @@ struct SendAA_Struct { /*0104*/ uint32 special_category; /*0108*/ uint8 shroud; /*0109*/ uint8 unknown109; -/*0110*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0110*/ uint8 reset_on_death; // timer is reset on death /*0111*/ uint8 unknown111; /*0112*/ uint32 total_abilities; /*0116*/ AA_Ability abilities[0]; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 7f466f09b..03f1a951a 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4341,7 +4341,7 @@ struct SendAA_Struct { /*0104*/ uint32 special_category; /*0108*/ uint8 shroud; /*0109*/ uint8 unknown109; -/*0110*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0110*/ uint8 reset_on_death; // timer is reset on death /*0111*/ uint8 unknown111; /*0112*/ uint32 total_abilities; /*0116*/ AA_Ability abilities[0]; diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 0803432db..76a534e6a 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -3780,7 +3780,7 @@ struct SendAA_Struct { /*0092*/ uint32 special_category; /*0096*/ uint8 shroud; /*0097*/ uint8 unknown97; -/*0098*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0098*/ uint8 reset_on_death; // timer is reset on death /*0099*/ uint8 unknown99; /*0100*/ uint32 total_abilities; /*0104*/ AA_Ability abilities[0]; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 0b175b643..1507630bd 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -3704,7 +3704,7 @@ struct SendAA_Struct { /*0088*/ uint32 aa_expansion; /*0092*/ uint32 special_category; /*0096*/ uint8 shroud; -/*0097*/ uint8 unknown97; +/*0097*/ uint8 reset_on_death; // timer is reset on death -- guess /*0098*/ uint32 total_abilities; /*0102*/ AA_Ability abilities[0]; }; diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index e165da794..b6eb414e1 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -3835,7 +3835,7 @@ struct SendAA_Struct { /*0092*/ uint32 special_category; /*0096*/ uint8 shroud; /*0097*/ uint8 unknown97; -/*0098*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0098*/ uint8 reset_on_death; // timer is reset on death /*0099*/ uint8 unknown99; /*0100*/ uint32 total_abilities; /*0104*/ AA_Ability abilities[0]; diff --git a/common/version.h b/common/version.h index 13579c3d2..a950edbe0 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9149 +#define CURRENT_BINARY_DATABASE_VERSION 9150 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index fdaa4a946..9d0aa9231 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -403,6 +403,7 @@ 9147|2020_01_24_grid_centerpoint_wp.sql|SHOW COLUMNS FROM `grid_entries` LIKE 'centerpoint'|empty| 9148|2020_01_28_corpse_guild_consent_id.sql|SHOW COLUMNS FROM `character_corpses` LIKE 'guild_consent_id'|empty| 9149|2020_02_06_globalloot.sql|SHOW COLUMNS FROM `global_loot` LIKE 'hot_zone'|empty| +9150|2020_02_06_aa_reset_on_death.sql|SHOW COLUMNS FROM `aa_ability` LIKE 'reset_on_death'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2020_02_06_aa_reset_on_death.sql b/utils/sql/git/required/2020_02_06_aa_reset_on_death.sql new file mode 100644 index 000000000..91edb5d4a --- /dev/null +++ b/utils/sql/git/required/2020_02_06_aa_reset_on_death.sql @@ -0,0 +1,2 @@ +ALTER TABLE `aa_ability` ADD `reset_on_death` TINYINT(4) NOT NULL DEFAULT '0'; +UPDATE `aa_ability` SET `reset_on_death` = '1' WHERE `id` = 6001; diff --git a/zone/aa.cpp b/zone/aa.cpp index 1383ad2f9..1ccc6f656 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1023,6 +1023,24 @@ void Client::ResetAlternateAdvancementTimers() { safe_delete(outapp); } +void Client::ResetOnDeathAlternateAdvancement() { + for (const auto &aa : aa_ranks) { + auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first); + auto ability = ability_rank.first; + auto rank = ability_rank.second; + + if (!ability) + continue; + + if (!rank) + continue; + + // since they're dying, we just need to clear the DB + if (ability->reset_on_death) + p_timers.Clear(&database, rank->spell_type + pTimerAAStart); + } +} + void Client::PurchaseAlternateAdvancementRank(int rank_id) { AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id); if(!rank) { @@ -1646,7 +1664,7 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_maptype = atoi(row[8]); ability->charges = atoi(row[9]); ability->grant_only = atoi(row[10]) != 0 ? true : false; - ability->first_rank_id = atoi(row[11]); + ability->reset_on_death = atoi(row[11]) != 0 ? true : false; + ability->first_rank_id = atoi(row[12]); ability->first = nullptr; abilities[ability->id] = std::unique_ptr(ability); diff --git a/zone/aa_ability.h b/zone/aa_ability.h index 0d6a240c4..5ec08d986 100644 --- a/zone/aa_ability.h +++ b/zone/aa_ability.h @@ -50,6 +50,7 @@ public: int drakkin_heritage; int status; bool grant_only; + bool reset_on_death; int type; int charges; int first_rank_id; diff --git a/zone/attack.cpp b/zone/attack.cpp index 083176e39..bd169908c 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1852,6 +1852,11 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk BuffFadeDetrimental(); } + /* + Reset AA reuse timers that need to be, live-like this is only Lay on Hands + */ + ResetOnDeathAlternateAdvancement(); + /* Finally, send em home diff --git a/zone/client.h b/zone/client.h index a0655584b..030484229 100644 --- a/zone/client.h +++ b/zone/client.h @@ -838,6 +838,7 @@ public: void SendAlternateAdvancementTimers(); void ResetAlternateAdvancementTimer(int ability); void ResetAlternateAdvancementTimers(); + void ResetOnDeathAlternateAdvancement(); void SetAAPoints(uint32 points) { m_pp.aapoints = points; SendAlternateAdvancementStats(); } void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); } From 29fccd923924638613be36fb269e1672970dfe37 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 6 Feb 2020 16:44:46 -0500 Subject: [PATCH 37/60] LoH skill needs to be reset on death --- zone/attack.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zone/attack.cpp b/zone/attack.cpp index bd169908c..4da160608 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1857,6 +1857,12 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk */ ResetOnDeathAlternateAdvancement(); + /* + Reset reuse timer for classic skill based Lay on Hands (For tit I guess) + */ + if (GetClass() == PALADIN) // we could check if it's not expired I guess, but should be fine not to + p_timers.Clear(&database, pTimerLayHands); + /* Finally, send em home From 5f8d193d6a68eb5e7e0fb45451d2ec0125264a24 Mon Sep 17 00:00:00 2001 From: Xackery Xtal Date: Sat, 8 Feb 2020 18:01:46 -0800 Subject: [PATCH 38/60] Added EnableFoodRequirement --- common/ruletypes.h | 1 + zone/client.h | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 8e5793bc3..4284e0d0b 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -121,6 +121,7 @@ RULE_BOOL(Character, EnableAggroMeter, true, "Enable Aggro Meter, for users with RULE_BOOL(Character, KeepLevelOverMax, false, "Don't delevel a character that has somehow gone over the level cap") RULE_INT(Character, FoodLossPerUpdate, 32, "How much food/water you lose per stamina update") RULE_BOOL(Character, EnableHungerPenalties, false, "being hungry/thirsty has negative effects -- it does appear normal live servers do not have penalties") +RULE_BOOL(Character, EnableFoodRequirement, true, "if disabled, food is no longer required") RULE_INT(Character, BaseInstrumentSoftCap, 36, "Softcap for instrument mods, 36 commonly referred to as \"3.6\" as well") RULE_BOOL(Character, UseSpellFileSongCap, true, "When they removed the AA that increased the cap they removed the above and just use the spell field") RULE_INT(Character, BaseRunSpeedCap, 158, "Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225") diff --git a/zone/client.h b/zone/client.h index 030484229..52af842be 100644 --- a/zone/client.h +++ b/zone/client.h @@ -901,14 +901,14 @@ public: void BreakFeignDeathWhenCastOn(bool IsResisted); void LeaveGroup(); - bool Hungry() const {if (GetGM()) return false; return m_pp.hunger_level <= 3000;} - bool Thirsty() const {if (GetGM()) return false; return m_pp.thirst_level <= 3000;} + bool Hungry() const {if (GetGM() || !RuleB(Character, EnableFoodRequirement)) return false; return m_pp.hunger_level <= 3000;} + bool Thirsty() const {if (GetGM() || !RuleB(Character, EnableFoodRequirement)) return false; return m_pp.thirst_level <= 3000;} int32 GetHunger() const { return m_pp.hunger_level; } int32 GetThirst() const { return m_pp.thirst_level; } void SetHunger(int32 in_hunger); void SetThirst(int32 in_thirst); void SetConsumption(int32 in_hunger, int32 in_thirst); - bool IsStarved() const { if (GetGM() || !RuleB(Character, EnableHungerPenalties)) return false; return m_pp.hunger_level == 0 || m_pp.thirst_level == 0; } + bool IsStarved() const { if (GetGM() || !RuleB(Character, EnableFoodRequirement) || !RuleB(Character, EnableHungerPenalties)) return false; return m_pp.hunger_level == 0 || m_pp.thirst_level == 0; } bool CheckTradeLoreConflict(Client* other); bool CheckTradeNonDroppable(); From ed09d4ae54b35930ca947f01b997cc8c79e3fa51 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Wed, 12 Feb 2020 20:05:28 -0500 Subject: [PATCH 39/60] Send packet uncompressed if zlib deflates to a larger size than input It's not guaranteed that deflate output will be smaller than the input. In some cases zlib-ng (Z_BEST_SPEED) compression is causing packets to increase in size and exceed m_max_packet_size. This results in the packets never being fully received by the client. Currently this is most reproducible in the spell_book section of the OP_PlayerProfile message. After using #scribespells this portion of the player profile has a lot of incrementing spellids which may be affecting the compression algorithm. The client never processes the player profile (MSG_SEND_PC) message and times out on zone entry. This isn't necessarily a bug with zlib-ng since it inflates back to the original input and normal zlib could do this too, but the current netcode doesn't handle this. --- common/net/daybreak_connection.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/net/daybreak_connection.cpp b/common/net/daybreak_connection.cpp index 8448049f5..bd0c39489 100644 --- a/common/net/daybreak_connection.cpp +++ b/common/net/daybreak_connection.cpp @@ -1047,12 +1047,14 @@ void EQ::Net::DaybreakConnection::Compress(Packet &p, size_t offset, size_t leng uint8_t new_buffer[2048] = { 0 }; uint8_t *buffer = (uint8_t*)p.Data() + offset; uint32_t new_length = 0; + bool send_uncompressed = true; if (length > 30) { new_length = Deflate(buffer, (uint32_t)length, new_buffer + 1, 2048) + 1; new_buffer[0] = 0x5a; + send_uncompressed = (new_length > length); } - else { + if (send_uncompressed) { memcpy(new_buffer + 1, buffer, length); new_buffer[0] = 0xa5; new_length = length + 1; @@ -1380,7 +1382,7 @@ void EQ::Net::DaybreakConnection::InternalQueuePacket(Packet &p, int stream_id, } auto stream = &m_streams[stream_id]; - auto max_raw_size = m_max_packet_size - m_crc_bytes - DaybreakReliableHeader::size(); + auto max_raw_size = m_max_packet_size - m_crc_bytes - DaybreakReliableHeader::size() - 1; // -1 for compress flag size_t length = p.Length(); if (length > max_raw_size) { DaybreakReliableFragmentHeader first_header; From 709a25ba9e79652faa85c980c66e2e29fe885058 Mon Sep 17 00:00:00 2001 From: Joshua Packard Date: Sat, 15 Feb 2020 16:55:18 -0800 Subject: [PATCH 40/60] Add necro pet spell effect id to pet spell reagent check --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index a1e926a1c..1724f51b0 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1230,7 +1230,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // handle the components for traditional casters else { - if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) { + if (!RuleB(Character, PetsUseReagents) && (IsEffectInSpell(spell_id, SE_SummonPet) || IsEffectInSpell(spell_id, SE_NecPet))) { //bypass reagent cost } else if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found From 5901df44851a548acc428277b16540878adb0c23 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sun, 16 Feb 2020 11:41:08 -0500 Subject: [PATCH 41/60] Added Skills, RequireTomeHandin rule Default behavior will remain the same as current code. If set to true, tomes will need to be turned in to Guild Masters to learn, like in older times. --- common/ruletypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 4284e0d0b..a9f7a233f 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -88,7 +88,6 @@ RULE_INT(Character, ItemHealAmtCap, 250, "") RULE_INT(Character, ItemSpellDmgCap, 250, "") RULE_INT(Character, ItemClairvoyanceCap, 250, "") RULE_INT(Character, ItemDSMitigationCap, 50, "") -RULE_INT(Character, ItemEnduranceRegenCap, 15, "") RULE_INT(Character, ItemExtraDmgCap, 150, "Cap for bonuses to melee skills like Bash, Frenzy, etc") RULE_INT(Character, HasteCap, 100, "Haste cap for non-v3(overhaste) haste") RULE_INT(Character, SkillUpModifier, 100, "skill ups are at 100%") @@ -197,6 +196,7 @@ RULE_INT(Skills, SwimmingStartValue, 100, "") RULE_BOOL(Skills, TrainSenseHeading, false, "") RULE_INT(Skills, SenseHeadingStartValue, 200, "") RULE_BOOL(Skills, SelfLanguageLearning, true, "") +RULE_BOOL(Skills, RequireTomeHandin, false, "Disable click-to-learn and force turnin to Guild Master") RULE_CATEGORY_END() RULE_CATEGORY(Pets) From bd4fa4fb6b790b76b33573beb27770489fb5ca04 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sun, 16 Feb 2020 11:42:55 -0500 Subject: [PATCH 42/60] Implement RuleB(Skills, RequireTomeHandin) --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0ea11e8ae..7173dd342 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8653,7 +8653,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } else if (inst->IsClassCommon()) { - if (item->ItemType == EQEmu::item::ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) + if (!RuleB(Skills, RequireTomeHandin) && item->ItemType == EQEmu::item::ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) { DeleteItemInInventory(slot_id, 1, true); TrainDiscipline(item->ID); From 141ecca2bcae65f4a395574522e69adb4f18263d Mon Sep 17 00:00:00 2001 From: Joshua Packard Date: Sun, 16 Feb 2020 09:23:26 -0800 Subject: [PATCH 43/60] Add necromancer pet spell check to other reagent logic --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 1724f51b0..5b8ee33f8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1263,7 +1263,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo return; } } - else if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) { + else if (!RuleB(Character, PetsUseReagents) && (IsEffectInSpell(spell_id, SE_SummonPet) || IsEffectInSpell(spell_id, SE_NecPet))) { //bypass reagent cost } else if (!bard_song_mode) From 10e5f0e9496887d04246a6e9bc15318d60c38b7a Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sun, 16 Feb 2020 12:28:18 -0500 Subject: [PATCH 44/60] Update ruletypes.h --- common/ruletypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index a9f7a233f..b15d3fbf7 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -88,6 +88,7 @@ RULE_INT(Character, ItemHealAmtCap, 250, "") RULE_INT(Character, ItemSpellDmgCap, 250, "") RULE_INT(Character, ItemClairvoyanceCap, 250, "") RULE_INT(Character, ItemDSMitigationCap, 50, "") +RULE_INT(Character, ItemEnduranceRegenCap, 15, "") RULE_INT(Character, ItemExtraDmgCap, 150, "Cap for bonuses to melee skills like Bash, Frenzy, etc") RULE_INT(Character, HasteCap, 100, "Haste cap for non-v3(overhaste) haste") RULE_INT(Character, SkillUpModifier, 100, "skill ups are at 100%") From 0a4429c0c0c5bff8299a6d37abe65c3ced90662f Mon Sep 17 00:00:00 2001 From: Xackery Xtal Date: Tue, 18 Feb 2020 09:21:18 -0800 Subject: [PATCH 45/60] Added NPC::RecalculateSkills --- zone/lua_npc.cpp | 9 ++++++++- zone/lua_npc.h | 1 + zone/npc.cpp | 25 +++++++++++++++++++++++++ zone/npc.h | 2 ++ zone/perl_npc.cpp | 23 +++++++++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index 4a8bd12a9..bf52b9276 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -547,6 +547,12 @@ void Lua_NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_del self->SetSimpleRoamBox(box_size, move_distance, move_delay); } +void Lua_NPC::RecalculateSkills() +{ + Lua_Safe_Call_Void(); + self->RecalculateSkills(); +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") .def(luabind::constructor<>()) @@ -657,7 +663,8 @@ luabind::scope lua_register_npc() { .def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop) .def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop) .def("GetRawAC", (int(Lua_NPC::*)(void))&Lua_NPC::GetRawAC) - .def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating); + .def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating) + .def("RecalculateSkills", (void(Lua_NPC::*)(void))&Lua_NPC::RecalculateSkills); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 92ad73cd7..1c7c879f7 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -134,6 +134,7 @@ public: void SetSimpleRoamBox(float box_size); void SetSimpleRoamBox(float box_size, float move_distance); void SetSimpleRoamBox(float box_size, float move_distance, int move_delay); + void RecalculateSkills(); }; #endif diff --git a/zone/npc.cpp b/zone/npc.cpp index 96efc83a8..17e3c5957 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3209,3 +3209,28 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) } } + +void NPC::RecalculateSkills() +{ + int r; + for (r = 0; r <= EQEmu::skills::HIGHEST_SKILL; r++) { + skills[r] = database.GetSkillCap(GetClass(), (EQEmu::skills::SkillType)r, level); + } + + // some overrides -- really we need to be able to set skills for mobs in the DB + // There are some known low level SHM/BST pets that do not follow this, which supports + // the theory of needing to be able to set skills for each mob separately + if (!IsBot()) { + if (level > 50) { + skills[EQEmu::skills::SkillDoubleAttack] = 250; + skills[EQEmu::skills::SkillDualWield] = 250; + } + else if (level > 3) { + skills[EQEmu::skills::SkillDoubleAttack] = level * 5; + skills[EQEmu::skills::SkillDualWield] = skills[EQEmu::skills::SkillDoubleAttack]; + } + else { + skills[EQEmu::skills::SkillDoubleAttack] = level * 5; + } + } +} \ No newline at end of file diff --git a/zone/npc.h b/zone/npc.h index d0ecdf1ca..e545ffb38 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -476,6 +476,8 @@ public: inline bool IsSkipAutoScale() const { return skip_auto_scale; } + void RecalculateSkills(); + protected: const NPCType* NPCTypedata; diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 2dae9e776..1d40474f3 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -2451,6 +2451,28 @@ XS(XS_NPC_SetSimpleRoamBox) { XSRETURN_EMPTY; } + +XS(XS_NPC_RecalculateSkills); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_RecalculateSkills) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::RecalculateSkills(THIS)"); + { + NPC *THIS; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV *) SvRV(ST(0))); + THIS = INT2PTR(NPC *, tmp); + } else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + THIS->RecalculateSkills(); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -2565,6 +2587,7 @@ XS(boot_NPC) { newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$"); newXSproto(strcpy(buf, "GetCombatState"), XS_NPC_GetCombatState, file, "$"); newXSproto(strcpy(buf, "SetSimpleRoamBox"), XS_NPC_SetSimpleRoamBox, file, "$$;$$"); + newXSproto(strcpy(buf, "RecalculateSkills"), XS_NPC_RecalculateSkills, file, "$"); XSRETURN_YES; } From 6fb0042e3f5c33ee1b2915f9fbb93328b4d33221 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 18 Feb 2020 15:50:01 -0500 Subject: [PATCH 46/60] Add packet struct and ops for AdvancedLoreText Just stuff if people want to start working on it --- common/eq_packet_structs.h | 7 +++++++ utils/patches/patch_RoF.conf | 2 +- utils/patches/patch_RoF2.conf | 2 ++ utils/patches/patch_SoD.conf | 2 +- utils/patches/patch_SoF.conf | 2 +- utils/patches/patch_UF.conf | 2 +- 6 files changed, 13 insertions(+), 4 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 3fe2430db..e9e7c5ef3 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -36,6 +36,7 @@ static const uint32 MAX_MERC_GRADES = 10; static const uint32 MAX_MERC_STANCES = 10; static const uint32 BLOCKED_BUFF_COUNT = 20; static const uint32 QUESTREWARD_COUNT = 8; +static const uint32 ADVANCED_LORE_LENGTH = 8192; /* @@ -2966,6 +2967,12 @@ struct ItemViewRequest_Struct { /*046*/ char unknown046[2]; }; +struct ItemAdvancedLoreText_Struct { + int32 item_id; + char item_name[64]; + char advanced_lore[ADVANCED_LORE_LENGTH]; +}; + struct LDONItemViewRequest_Struct { uint32 item_id; uint8 unknown004[4]; diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 9467ab337..31b091ff8 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -331,7 +331,7 @@ OP_LDoNButton=0x596e OP_SetStartCity=0x7936 # Was 0x2d1b OP_VoiceMacroIn=0x202e OP_VoiceMacroOut=0x3920 -OP_ItemViewUnknown=0x0b64 +OP_ItemAdvancedLoreText=0x0b64 OP_VetRewardsAvaliable=0x05d9 OP_VetClaimRequest=0xcdde OP_VetClaimReply=0x361b diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index f410a3193..a39fde999 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -448,6 +448,8 @@ OP_FinishWindow2=0x40ef OP_ItemVerifyRequest=0x189c OP_ItemVerifyReply=0x097b +OP_ItemAdvancedLoreText=0x023b + # merchant stuff OP_ShopPlayerSell=0x791b OP_ShopRequest=0x4fed diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index d836c6360..619f986c1 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -327,7 +327,7 @@ OP_LDoNButton=0x41b5 # C OP_SetStartCity=0x7bf6 # C OP_VoiceMacroIn=0x31b1 # C OP_VoiceMacroOut=0x7880 # C -OP_ItemViewUnknown=0x21c7 # C +OP_ItemAdvancedLoreText=0x21c7 # C OP_VetRewardsAvaliable=0x4e4e # C OP_VetClaimRequest=0x771f # C OP_VetClaimReply=0x2f95 # C diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index a10715918..6a5d41fb4 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -576,7 +576,7 @@ OP_QueryResponseThing=0x0000 # # realityincarnate: these are just here to stop annoying several thousand byte packet dumps OP_LoginUnknown1=0x22cf OP_LoginUnknown2=0x43ba -OP_ItemViewUnknown=0x4db4 +OP_ItemAdvancedLoreText=0x4db4 #Petition Opcodes OP_PetitionSearch=0x0000 #search term for petition diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index 71966dcbb..69d6bb1e5 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -336,7 +336,7 @@ OP_LDoNButton=0x1031 # C OP_SetStartCity=0x68f0 # C OP_VoiceMacroIn=0x1524 # C OP_VoiceMacroOut=0x1d99 # C -OP_ItemViewUnknown=0x4eb3 # C +OP_ItemAdvancedLoreText=0x4eb3 # C OP_VetRewardsAvaliable=0x0baa # C Mispelled? OP_VetClaimRequest=0x34f8 # C OP_VetClaimReply=0x6a5d # C From fdc38315d3ac53f9c3dc1b26154499ed08d2e230 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 19 Feb 2020 07:52:10 -0500 Subject: [PATCH 47/60] Updated eqemu_server.pl console messages for Applying/Has update messages [skip ci] --- utils/scripts/eqemu_server.pl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 41d673418..a5ae9e7b8 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -2302,21 +2302,21 @@ sub run_database_check { #::: Match type update if ($match_type eq "contains") { if (trim(get_mysql_result($query_check)) =~ /$match_text/i) { - print "[Database] running update: " . $val . " '" . $file_name . "' \n"; + print "[Database] Applying update [" . $val . "]:[" . $file_name . "]\n"; print get_mysql_result_from_file("db_update/$file_name"); } else { - print "[Database] has update (" . $val . ") '" . $file_name . "' \n"; + print "[Database] Has update [" . $val . "]:[" . $file_name . "]\n"; } print_match_debug(); print_break(); } if ($match_type eq "missing") { if (get_mysql_result($query_check) =~ /$match_text/i) { - print "[Database] has update (" . $val . ") '" . $file_name . "' \n"; + print "[Database] Has update [" . $val . "]:[" . $file_name . "]\n"; } else { - print "[Database] running update: " . $val . " '" . $file_name . "' \n"; + print "[Database] Applying update [" . $val . "]:[" . $file_name . "]\n"; print get_mysql_result_from_file("db_update/$file_name"); } print_match_debug(); @@ -2324,22 +2324,22 @@ sub run_database_check { } if ($match_type eq "empty") { if (get_mysql_result($query_check) eq "") { - print "[Database] running update: " . $val . " '" . $file_name . "' \n"; + print "[Database] Applying update [" . $val . "]:[" . $file_name . "]\n"; print get_mysql_result_from_file("db_update/$file_name"); } else { - print "[Database] has update (" . $val . ") '" . $file_name . "' \n"; + print "[Database] Has update [" . $val . "]:[" . $file_name . "' \n"; } print_match_debug(); print_break(); } if ($match_type eq "not_empty") { if (get_mysql_result($query_check) ne "") { - print "[Database] running update: " . $val . " '" . $file_name . "' \n"; + print "[Database] Applying update [" . $val . "]:[" . $file_name . "]\n"; print get_mysql_result_from_file("db_update/$file_name"); } else { - print "[Database] has update (" . $val . ") '" . $file_name . "' \n"; + print "[Database] Has update [" . $val . "]:[" . $file_name . "]\n"; } print_match_debug(); print_break(); @@ -2362,10 +2362,10 @@ sub run_database_check { } if ($bots_db_management == 1) { - print "[Database] Bots database update cycle complete at version: " . get_bots_db_version() . "\n"; + print "[Database] Bots database update cycle complete at version [" . get_bots_db_version() . "]\n"; } else { - print "[Database] Database update cycle complete at version: " . get_main_db_version() . "\n"; + print "[Database] Mainstream database update cycle complete at version [" . get_main_db_version() . "]\n"; } } From 6ddc5ea8e8cce49b60f4475bce285a33c32be684 Mon Sep 17 00:00:00 2001 From: kentai Date: Thu, 20 Feb 2020 14:23:06 +1100 Subject: [PATCH 48/60] CheckIncreaseSkill() from Client::ThrowingAttack() to DoThrowingAttackDmg() so it gets called during combat abilities(zerker volley), as well as regular throws(monk ranged). --- zone/special_attacks.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index b7cdebf02..6a073fc38 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1408,6 +1408,9 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQEmu::ItemInstance *RangeWeapon else TrySkillProc(other, EQEmu::skills::SkillThrowing, 0, false, EQEmu::invslot::slotRange); } + if (IsClient()) { + CastToClient()->CheckIncreaseSkill(EQEmu::skills::SkillThrowing, GetTarget()); + } } void Mob::SendItemAnimation(Mob *to, const EQEmu::ItemData *item, EQEmu::skills::SkillType skillInUse, float velocity) { From 0043f569843059eacbfffb0390084cc02a60ac04 Mon Sep 17 00:00:00 2001 From: kentai Date: Thu, 20 Feb 2020 14:37:26 +1100 Subject: [PATCH 49/60] ... --- zone/special_attacks.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 6a073fc38..42ec94616 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1294,7 +1294,6 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 //consume ammo DeleteItemInInventory(ammo_slot, 1, true); - CheckIncreaseSkill(EQEmu::skills::SkillThrowing, GetTarget()); CommonBreakInvisibleFromCombat(); } From fe4a0cfdbabd666453815848a738392c352e14ab Mon Sep 17 00:00:00 2001 From: Xackery Xtal Date: Thu, 20 Feb 2020 16:50:34 -0800 Subject: [PATCH 50/60] Added vscode environment files --- .devcontainer/Dockerfile | 44 +++++++++++++++++++++ .devcontainer/devcontainer.json | 28 ++++++++++++++ .vscode/c_cpp_properties.json | 16 ++++++++ .vscode/tasks.json | 68 +++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/tasks.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..ee959f3d1 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,44 @@ +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +# https://github.com/microsoft/vscode-dev-containers/tree/v0.101.1/containers/ubuntu-18.04-git/.devcontainer/Dockerfile +FROM ubuntu:18.04 + + ENV DEBIAN_FRONTEND=noninteractive + RUN apt-get update \ + && apt-get -y install --no-install-recommends build-essential \ + gcc-5 g++-5 libtool cmake curl debconf-utils \ + git git-core libio-stringy-perl liblua5.1 \ + liblua5.1-dev libluabind-dev libmysql++ \ + libperl-dev libperl5i-perl libsodium-dev \ + libsodium23 libmysqlclient-dev lua5.1 \ + minizip make mariadb-client \ +# optional, mariadb server + mariadb-server \ +# optional, debugging tools + gdb valgrind \ +# + nano open-vm-tools unzip uuid-dev \ + zlibc wget \ +# # +# # Clean up + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* +ENV DEBIAN_FRONTEND=dialog +EXPOSE 3306 +EXPOSE 5558 +EXPOSE 5559 +EXPOSE 7000 +EXPOSE 7001 +EXPOSE 7002 +EXPOSE 7003 +EXPOSE 7004 +EXPOSE 7005 +EXPOSE 7778 +EXPOSE 9000 +EXPOSE 9001 +EXPOSE 9080 +EXPOSE 9081 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..1723407c3 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,28 @@ +// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.101.1/containers/ubuntu-18.04-git +{ + "name": "Ubuntu 18.04 EQEMU", + "dockerFile": "Dockerfile", + + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [], + "appPort": [3306, 5558, 5559, 7000, 7001, 7002, 7003, 7004, 7005, 7778, 9000, 9001, 9080, 9081], + // "forwardPorts": [3306, 5558, 5559, 7000, 7001, 7002, 7003, 7004, 7005, 7778, 9000, 9001, 9080, 9081], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "uname -a", + + // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-in-docker. + // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], + + // Uncomment when using a ptrace-based debugger like C++, Go, and Rust + // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], + + // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. + // "remoteUser": "eqemu" +} \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 000000000..4aee577ae --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/usr/include/mysql" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "c11", + "cppStandard": "c++17" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..429b59492 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,68 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "make", + "type": "shell", + "command": "cd build && make", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "cmake", + "type": "shell", + "command": "mkdir -p build && cd build && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' ..", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher":{ + "owner": "cpp", + "fileLocation": "relative", + "pattern":[ + { + "regexp": "([\\w+|\\\\]*\\.\\w+)\\((\\d+)\\)\\: (warning|error) (.*)$", + "file": 1, + "location": 2, + "severity": 3, + "message": 4 + } + ] + } + }, + { + "label": "zone", + "type": "shell", + "command": "cd build/bin && ./zone", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "loginserver", + "type": "shell", + "command": "cd build/bin && ./loginserver", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "world", + "type": "shell", + "command": "cd build/bin && ./world", + "group": { + "kind": "test", + "isDefault": true + } + } + ] +} \ No newline at end of file From 8376ed5d3f7d2941430c7d0542b312bd74727562 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 22 Feb 2020 16:17:18 -0600 Subject: [PATCH 51/60] PR adjustments --- common/ruletypes.h | 2 +- zone/zone_reload.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 3e2c56c03..c584f37a7 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -766,7 +766,7 @@ RULE_CATEGORY_END() RULE_CATEGORY(HotReload) RULE_BOOL(HotReload, QuestsRepopWithReload, true, "When a hot reload is triggered, the zone will repop") RULE_BOOL(HotReload, QuestsRepopWhenPlayersNotInCombat, true, "When a hot reload is triggered, the zone will repop when no clients are in combat") -RULE_BOOL(HotReload, QuestsResetTimersWithReload, true, "When a hot reload is triggered, the zone will repop") +RULE_BOOL(HotReload, QuestsResetTimersWithReload, true, "When a hot reload is triggered, quest timers will be reset") RULE_CATEGORY_END() #undef RULE_CATEGORY diff --git a/zone/zone_reload.cpp b/zone/zone_reload.cpp index 42424dae2..3dca89d71 100644 --- a/zone/zone_reload.cpp +++ b/zone/zone_reload.cpp @@ -24,7 +24,6 @@ void ZoneReload::HotReloadQuests() { BenchTimer timer; - timer.reset(); entity_list.ClearAreas(); @@ -33,6 +32,7 @@ void ZoneReload::HotReloadQuests() if (RuleB(HotReload, QuestsRepopWithReload)) { zone->Repop(0); } + zone->SetQuestHotReloadQueued(false); LogHotReload( From 08002eacea2f1278fa5c8c7b26c5b219cefa2f53 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 24 Feb 2020 16:51:05 -0500 Subject: [PATCH 52/60] restore some code unintentionally removed Checked with @KimLS. --- zone/pathfinder_nav_mesh.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/pathfinder_nav_mesh.cpp b/zone/pathfinder_nav_mesh.cpp index 63e76f486..f4a5a036d 100644 --- a/zone/pathfinder_nav_mesh.cpp +++ b/zone/pathfinder_nav_mesh.cpp @@ -179,6 +179,10 @@ IPathfinder::IPath PathfinderNavmesh::FindPath(const glm::vec3 &start, const glm if (path[npoly - 1] != end_ref) { m_impl->query->closestPointOnPoly(path[npoly - 1], &dest_location[0], &epos[0], 0); partial = true; + + auto dist = DistanceSquared(epos, current_location); + if (dist < 10000.0f) { + stuck = true; } int n_straight_polys; From 0ffb5c46a5f0bbac14e19ec2386c7c6afa34472d Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 24 Feb 2020 15:00:12 -0800 Subject: [PATCH 53/60] Missing bracket --- zone/pathfinder_nav_mesh.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/pathfinder_nav_mesh.cpp b/zone/pathfinder_nav_mesh.cpp index f4a5a036d..998515a35 100644 --- a/zone/pathfinder_nav_mesh.cpp +++ b/zone/pathfinder_nav_mesh.cpp @@ -183,6 +183,7 @@ IPathfinder::IPath PathfinderNavmesh::FindPath(const glm::vec3 &start, const glm auto dist = DistanceSquared(epos, current_location); if (dist < 10000.0f) { stuck = true; + } } int n_straight_polys; From bec33e22da3c43dc37d658791f5256431972dfa4 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 28 Feb 2020 21:38:39 -0500 Subject: [PATCH 54/60] Update Readme [skip ci] Updated Windows Installer Link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e4f919fe..f3b829732 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ |:---:|:---:|:---:| |**Install Count**|![Windows Install Count](http://analytics.akkadius.com/?install_count&windows_count)|![Linux Install Count](http://analytics.akkadius.com/?install_count&linux_count)| ### > Windows -* [Install](https://github.com/EQEmu/Server/wiki/Windows-Server) +* [Install](https://eqemu.gitbook.io/server/categories/how-to-guides/installation/server-installation-windows) ### > Debian/Ubuntu/CentOS/Fedora * You can use curl or wget to kick off the installer (whichever your OS has) From eb104e40dee509545bd1b59064af5e5f97712e71 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 28 Feb 2020 21:39:46 -0500 Subject: [PATCH 55/60] Update link [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e4f919fe..f3b829732 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ |:---:|:---:|:---:| |**Install Count**|![Windows Install Count](http://analytics.akkadius.com/?install_count&windows_count)|![Linux Install Count](http://analytics.akkadius.com/?install_count&linux_count)| ### > Windows -* [Install](https://github.com/EQEmu/Server/wiki/Windows-Server) +* [Install](https://eqemu.gitbook.io/server/categories/how-to-guides/installation/server-installation-windows) ### > Debian/Ubuntu/CentOS/Fedora * You can use curl or wget to kick off the installer (whichever your OS has) From 81e91d795662008acb69ec2b9126f553e0aaf8c5 Mon Sep 17 00:00:00 2001 From: Xackery Xtal Date: Sat, 29 Feb 2020 21:34:38 -0800 Subject: [PATCH 56/60] Improved vscode devcontainer setup --- .devcontainer/Dockerfile | 44 -------------- .devcontainer/devcontainer.json | 29 ++++----- .gitignore | 2 + .vscode/tasks.json | 101 +++++++++++++++++++++++++++++--- 4 files changed, 107 insertions(+), 69 deletions(-) delete mode 100644 .devcontainer/Dockerfile diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index ee959f3d1..000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- - -# https://github.com/microsoft/vscode-dev-containers/tree/v0.101.1/containers/ubuntu-18.04-git/.devcontainer/Dockerfile -FROM ubuntu:18.04 - - ENV DEBIAN_FRONTEND=noninteractive - RUN apt-get update \ - && apt-get -y install --no-install-recommends build-essential \ - gcc-5 g++-5 libtool cmake curl debconf-utils \ - git git-core libio-stringy-perl liblua5.1 \ - liblua5.1-dev libluabind-dev libmysql++ \ - libperl-dev libperl5i-perl libsodium-dev \ - libsodium23 libmysqlclient-dev lua5.1 \ - minizip make mariadb-client \ -# optional, mariadb server - mariadb-server \ -# optional, debugging tools - gdb valgrind \ -# - nano open-vm-tools unzip uuid-dev \ - zlibc wget \ -# # -# # Clean up - && apt-get autoremove -y \ - && apt-get clean -y \ - && rm -rf /var/lib/apt/lists/* -ENV DEBIAN_FRONTEND=dialog -EXPOSE 3306 -EXPOSE 5558 -EXPOSE 5559 -EXPOSE 7000 -EXPOSE 7001 -EXPOSE 7002 -EXPOSE 7003 -EXPOSE 7004 -EXPOSE 7005 -EXPOSE 7778 -EXPOSE 9000 -EXPOSE 9001 -EXPOSE 9080 -EXPOSE 9081 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1723407c3..589bb2ed4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,28 +1,21 @@ // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.101.1/containers/ubuntu-18.04-git { - "name": "Ubuntu 18.04 EQEMU", - "dockerFile": "Dockerfile", + "name": "Ubuntu 18.04 EQEMU", + // Moved from dockerfile to image so it builds faster + "image": "eqemu/devcontainer:0.0.2", // Set *default* container specific settings.json values on container create. "settings": { "terminal.integrated.shell.linux": "/bin/bash" }, + "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], + // Add the IDs of extensions you want installed when the container is created. - "extensions": [], - "appPort": [3306, 5558, 5559, 7000, 7001, 7002, 7003, 7004, 7005, 7778, 9000, 9001, 9080, 9081], - // "forwardPorts": [3306, 5558, 5559, 7000, 7001, 7002, 7003, 7004, 7005, 7778, 9000, 9001, 9080, 9081], - - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "uname -a", - - // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-in-docker. - // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], - - // Uncomment when using a ptrace-based debugger like C++, Go, and Rust - // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], - - // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. - // "remoteUser": "eqemu" -} \ No newline at end of file + "extensions": ["ms-vscode.cpptools", "ms-azuretools.vscode-docker"], + "mounts": ["source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"], + "remoteEnv": { + "HOST_PROJECT_PATH": "${localWorkspaceFolder}" + } +} diff --git a/.gitignore b/.gitignore index 89dd521c5..287c07412 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ *.out *.app +.bash_history + # CMake CMakeCache.txt CMakeFiles diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 429b59492..1c6e5c871 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -15,10 +15,22 @@ "$gcc" ] }, + { + "label": "make clean", + "type": "shell", + "command": "cd build && make clean", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, { "label": "cmake", "type": "shell", - "command": "mkdir -p build && cd build && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' ..", + "command": "mkdir -p build && cd build && rm CMakeCache.txt && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' ..", "group": { "kind": "build", "isDefault": true @@ -38,9 +50,66 @@ } }, { - "label": "zone", + "label": "download maps", "type": "shell", - "command": "cd build/bin && ./zone", + "command": "mkdir -p build/bin && cd build/bin && wget https://codeload.github.com/Akkadius/EQEmuMaps/zip/master -O maps.zip && unzip -o maps.zip && rm ./maps -rf && mv EQEmuMaps-master maps && rm maps.zip", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "download quests", + "type": "shell", + "command": "mkdir -p build/bin && cd build/bin && cd server && git -C ./quests pull 2> /dev/null || git clone https://github.com/ProjectEQ/projecteqquests.git quests", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "download eqemu_config", + "type": "shell", + "command": "mkdir -p build/bin && cd build/bin && wget --no-check-certificate https://raw.githubusercontent.com/Akkadius/EQEmuInstall/master/eqemu_config_docker.json -O eqemu_config.json", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "rebuild database (mariadb must be started)", + "type": "shell", + "command": "mkdir -p build/bin && cd build/bin && docker run -i --rm --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --network=eqemu -it eqemu/server:0.0.3 bash -c './eqemu_server.pl source_peq_db && ./eqemu_server.pl check_db_updates && ./eqemu_server.pl linux_login_server_setup'", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "zone 7000", + "type": "shell", + "command": "docker stop zone7000 | true && docker network create eqemu | true && docker run -i --rm --name zone7000 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu -p 7000:7000/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7000:7000", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "zone 7001", + "type": "shell", + "command": "docker stop zone7001 | true && docker network create eqemu | true && docker run -i --rm --name zone7001 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu -p 7001:7001/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7001:7001", "group": { "kind": "test", "isDefault": true @@ -49,20 +118,38 @@ { "label": "loginserver", "type": "shell", - "command": "cd build/bin && ./loginserver", + "command": "docker stop loginserver | true && docker network create eqemu | true && docker run -i --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu --name loginserver -p 5999:5999/udp -p 5998:5998/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./loginserver", "group": { "kind": "test", "isDefault": true } }, { - "label": "world", + "label": "shared_memory, world", "type": "shell", - "command": "cd build/bin && ./world", + "command": "docker stop sharedmemory | true && docker stop world | true && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --network=eqemu --name sharedmemory eqemu/server:0.0.3 ./shared_memory && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name world -p 9000:9000 -p 9000:9000/udp -p 9001:9001 -p 9080:9080 eqemu/server:0.0.3 gdb -ex run ./world", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "queryserv", + "type": "shell", + "command": "docker stop queryserv | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name queryserv eqemu/server:0.0.3 gdb -ex run ./queryserv", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "mariadb", + "type": "shell", + "command": "docker stop mariadb | true && cd build/bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin/db:/bitnami/mariadb -p 3306:3306 -e MARIADB_DATABASE=peq -e MARIADB_USER=eqemu -e MARIADB_PASSWORD=eqemupass -e ALLOW_EMPTY_PASSWORD=yes --name mariadb --network=eqemu bitnami/mariadb:latest", "group": { "kind": "test", "isDefault": true } } ] -} \ No newline at end of file +} From 7f6414d685805e8c6ce8332d5a3229f4a2df8871 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 2 Mar 2020 12:09:55 -0500 Subject: [PATCH 57/60] Some bot-related changes (Master Wu's Technique, Tiger Claw timer) --- zone/bot.cpp | 183 ++++++++++++++++++++++++++++++++------- zone/bot_command.cpp | 44 ++++++++-- zone/bot_database.cpp | 1 + zone/client.cpp | 1 + zone/client.h | 1 + zone/npc.cpp | 13 ++- zone/npc.h | 1 + zone/special_attacks.cpp | 30 ++++--- 8 files changed, 221 insertions(+), 53 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 5579b4bc5..6d14ce740 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6421,32 +6421,52 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { bool taunt_time = taunt_timer.Check(); bool ca_time = classattack_timer.Check(false); + bool ma_time = monkattack_timer.Check(false); bool ka_time = knightattack_timer.Check(false); - if((taunt_time || ca_time || ka_time) && !IsAttackAllowed(target)) + + if (taunt_time) { + + // Bots without this skill shouldn't be 'checking' on this timer..let's just disable it and avoid the extra IsAttackAllowed() checks + // Note: this is done here instead of NPC::ctor() because taunt skill can be acquired during level ups (the timer is re-enabled in CalcBotStats()) + if (!GetSkill(EQEmu::skills::SkillTaunt)) { + + taunt_timer.Disable(); + return; + } + + if (!IsAttackAllowed(target)) { + return; + } + } + + if ((ca_time || ma_time || ka_time) && !IsAttackAllowed(target)) { return; + } if(ka_time){ - int knightreuse = 1000; + switch(GetClass()){ - case SHADOWKNIGHT: - case SHADOWKNIGHTGM: { + case SHADOWKNIGHT: { CastSpell(SPELL_NPC_HARM_TOUCH, target->GetID()); - knightreuse = (HarmTouchReuseTime * 1000); + knightattack_timer.Start(HarmTouchReuseTime * 1000); + break; } - case PALADIN: - case PALADINGM: { + case PALADIN: { if(GetHPRatio() < 20) { CastSpell(SPELL_LAY_ON_HANDS, GetID()); - knightreuse = (LayOnHandsReuseTime * 1000); + knightattack_timer.Start(LayOnHandsReuseTime * 1000); + } + else { + knightattack_timer.Start(2000); } - else - knightreuse = 2000; break; } + default: { + break; + } } - knightattack_timer.Start(knightreuse); } if(taunting && target && target->IsNPC() && taunt_time) { @@ -6457,8 +6477,66 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if(!ca_time) + if (ma_time) { + switch (GetClass()) { + case MONK: { + int reuse = (MonkSpecialAttack(target, EQEmu::skills::SkillTigerClaw) - 1); + + // Live AA - Technique of Master Wu + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + + if (wuchance) { + const int MonkSPA[5] = { + EQEmu::skills::SkillFlyingKick, + EQEmu::skills::SkillDragonPunch, + EQEmu::skills::SkillEagleStrike, + EQEmu::skills::SkillTigerClaw, + EQEmu::skills::SkillRoundKick + }; + int extra = 0; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + while (wuchance > 0) { + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { + break; + } + wuchance /= 4; + } + + Mob* bo = GetBotOwner(); + if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { + + bo->Message( + GENERIC_EMOTE, + "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", + GetCleanName(), + GetCleanName(), + extra + ); + } + + auto classic = RuleB(Combat, ClassicMasterWu); + while (extra) { + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : EQEmu::skills::SkillTigerClaw)); + --extra; + } + } + + float HasteModifier = (GetHaste() * 0.01f); + monkattack_timer.Start((reuse * 1000) / HasteModifier); + + break; + } + default: + break;; + } + } + + if (!ca_time) { return; + } float HasteModifier = (GetHaste() * 0.01f); uint16 skill_to_use = -1; @@ -6493,18 +6571,22 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } break; case MONK: - if(GetLevel() >= 30) + if (GetLevel() >= 30) { skill_to_use = EQEmu::skills::SkillFlyingKick; - else if(GetLevel() >= 25) + } + else if (GetLevel() >= 25) { skill_to_use = EQEmu::skills::SkillDragonPunch; - else if(GetLevel() >= 20) + } + else if (GetLevel() >= 20) { skill_to_use = EQEmu::skills::SkillEagleStrike; - else if(GetLevel() >= 10) - skill_to_use = EQEmu::skills::SkillTigerClaw; - else if(GetLevel() >= 5) + } + else if (GetLevel() >= 5) { skill_to_use = EQEmu::skills::SkillRoundKick; - else + } + else { skill_to_use = EQEmu::skills::SkillKick; + } + break; case ROGUE: skill_to_use = EQEmu::skills::SkillBackstab; @@ -6555,19 +6637,54 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if (skill_to_use == EQEmu::skills::SkillFlyingKick || skill_to_use == EQEmu::skills::SkillDragonPunch || skill_to_use == EQEmu::skills::SkillEagleStrike || skill_to_use == EQEmu::skills::SkillTigerClaw || skill_to_use == EQEmu::skills::SkillRoundKick) { + if ( + skill_to_use == EQEmu::skills::SkillFlyingKick || + skill_to_use == EQEmu::skills::SkillDragonPunch || + skill_to_use == EQEmu::skills::SkillEagleStrike || + skill_to_use == EQEmu::skills::SkillRoundKick + ) { reuse = (MonkSpecialAttack(target, skill_to_use) - 1); - MonkSpecialAttack(target, skill_to_use); - uint32 bDoubleSpecialAttack = (itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack); - if(bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > zone->random.Int(0, 100))) { - int MonkSPA[5] = { EQEmu::skills::SkillFlyingKick, EQEmu::skills::SkillDragonPunch, EQEmu::skills::SkillEagleStrike, EQEmu::skills::SkillTigerClaw, EQEmu::skills::SkillRoundKick }; - MonkSpecialAttack(target, MonkSPA[zone->random.Int(0, 4)]); - int TripleChance = 25; - if (bDoubleSpecialAttack > 100) - TripleChance += (TripleChance * (100 - bDoubleSpecialAttack) / 100); + + // Live AA - Technique of Master Wu + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if(TripleChance > zone->random.Int(0,100)) - MonkSpecialAttack(target, MonkSPA[zone->random.Int(0, 4)]); + if (wuchance) { + const int MonkSPA[5] = { + EQEmu::skills::SkillFlyingKick, + EQEmu::skills::SkillDragonPunch, + EQEmu::skills::SkillEagleStrike, + EQEmu::skills::SkillTigerClaw, + EQEmu::skills::SkillRoundKick + }; + int extra = 0; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + while (wuchance > 0) { + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { + break; + } + wuchance /= 4; + } + + Mob* bo = GetBotOwner(); + if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { + + bo->Message( + GENERIC_EMOTE, + "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", + GetCleanName(), + GetCleanName(), + extra + ); + } + + auto classic = RuleB(Combat, ClassicMasterWu); + while (extra) { + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : skill_to_use)); + --extra; + } } reuse *= 1000; @@ -8966,6 +9083,12 @@ void Bot::CalcBotStats(bool showtext) { skills[sindex] = database.GetSkillCap(GetClass(), (EQEmu::skills::SkillType)sindex, GetLevel()); } + taunt_timer.Start(1000); + + if (GetClass() == MONK && GetLevel() >= 10) { + monkattack_timer.Start(1000); + } + LoadAAs(); GenerateSpecialAttacks(); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index dc9d40aab..9c202fed8 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -3928,6 +3928,16 @@ void bot_command_owner_option(Client *c, const Seperator *sep) "null" "(toggles)" "" + "" + "monkwumessage" + "enable | disable" + "displays monk wu trigger messages" + "" + "" + "" + "null" + "(toggles)" + "" "" "current" "" @@ -4103,6 +4113,22 @@ void bot_command_owner_option(Client *c, const Seperator *sep) c->Message(m_action, "Bot 'buff counter' is now %s.", (c->GetBotOption(Client::booBuffCounter) == true ? "enabled" : "disabled")); } + else if (!owner_option.compare("monkwumessage")) { + + if (!argument.compare("enable")) { + c->SetBotOption(Client::booMonkWuMessage, true); + } + else if (!argument.compare("disable")) { + c->SetBotOption(Client::booMonkWuMessage, false); + } + else { + c->SetBotOption(Client::booMonkWuMessage, !c->GetBotOption(Client::booMonkWuMessage)); + } + + database.botdb.SaveOwnerOption(c->CharacterID(), Client::booMonkWuMessage, c->GetBotOption(Client::booMonkWuMessage)); + + c->Message(m_action, "Bot 'monk wu message' is now %s.", (c->GetBotOption(Client::booMonkWuMessage) == true ? "enabled" : "disabled")); + } else if (!owner_option.compare("current")) { std::string window_title = "Current Bot Owner Options Settings"; @@ -4112,13 +4138,14 @@ void bot_command_owner_option(Client *c, const Seperator *sep) "Option
------" "Argument
-------" "" - "" "deathmarquee" "{}" "" - "" "statsupdate" "{}" "" - "" "spawnmessage" "{}" "" - "" "spawnmessage" "{}" "" - "" "altcombat" "{}" "" - "" "autodefend" "{}" "" - "" "buffcounter" "{}" "" + "" "deathmarquee" "{}" "" + "" "statsupdate" "{}" "" + "" "spawnmessage" "{}" "" + "" "spawnmessage" "{}" "" + "" "altcombat" "{}" "" + "" "autodefend" "{}" "" + "" "buffcounter" "{}" "" + "" "monkwumessage" "{}" "" "", (c->GetBotOption(Client::booDeathMarquee) ? "enabled" : "disabled"), (c->GetBotOption(Client::booStatsUpdate) ? "enabled" : "disabled"), @@ -4126,7 +4153,8 @@ void bot_command_owner_option(Client *c, const Seperator *sep) (c->GetBotOption(Client::booSpawnMessageClassSpecific) ? "class" : "default"), (RuleB(Bots, AllowOwnerOptionAltCombat) ? (c->GetBotOption(Client::booAltCombat) ? "enabled" : "disabled") : "restricted"), (RuleB(Bots, AllowOwnerOptionAutoDefend) ? (c->GetBotOption(Client::booAutoDefend) ? "enabled" : "disabled") : "restricted"), - (c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled") + (c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled"), + (c->GetBotOption(Client::booMonkWuMessage) ? "enabled" : "disabled") ); c->SendPopupToClient(window_title.c_str(), window_text.c_str()); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 2f6f9c1c6..28b5288e0 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2257,6 +2257,7 @@ bool BotDatabase::SaveOwnerOption(const uint32 owner_id, size_t type, const bool case Client::booAltCombat: case Client::booAutoDefend: case Client::booBuffCounter: + case Client::booMonkWuMessage: { query = fmt::format( "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')", diff --git a/zone/client.cpp b/zone/client.cpp index 2398db500..6bd9941f8 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -358,6 +358,7 @@ Client::Client(EQStreamInterface* ieqs) bot_owner_options[booAltCombat] = RuleB(Bots, AllowOwnerOptionAltCombat); bot_owner_options[booAutoDefend] = RuleB(Bots, AllowOwnerOptionAutoDefend); bot_owner_options[booBuffCounter] = false; + bot_owner_options[booMonkWuMessage] = false; SetBotPulling(false); SetBotPrecombat(false); diff --git a/zone/client.h b/zone/client.h index 52af842be..58b0d9af8 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1646,6 +1646,7 @@ public: booAltCombat, booAutoDefend, booBuffCounter, + booMonkWuMessage, _booCount }; diff --git a/zone/npc.cpp b/zone/npc.cpp index 17e3c5957..0b22bab3a 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -118,6 +118,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi attacked_timer(CombatEventTimer_expire), swarm_timer(100), classattack_timer(1000), + monkattack_timer(1000), knightattack_timer(1000), assist_timer(AIassistcheck_delay), qglobal_purge_timer(30000), @@ -307,7 +308,15 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi // some overrides -- really we need to be able to set skills for mobs in the DB // There are some known low level SHM/BST pets that do not follow this, which supports // the theory of needing to be able to set skills for each mob separately - if (!IsBot()) { + if (IsBot()) { + if (GetClass() != PALADIN && GetClass() != SHADOWKNIGHT) { + knightattack_timer.Disable(); + } + else if (GetClass() != MONK || GetLevel() < 10) { + monkattack_timer.Disable(); + } + } + else { if (moblevel > 50) { skills[EQEmu::skills::SkillDoubleAttack] = 250; skills[EQEmu::skills::SkillDualWield] = 250; @@ -3233,4 +3242,4 @@ void NPC::RecalculateSkills() skills[EQEmu::skills::SkillDoubleAttack] = level * 5; } } -} \ No newline at end of file +} diff --git a/zone/npc.h b/zone/npc.h index e545ffb38..2b8edc9b9 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -499,6 +499,7 @@ protected: Timer attacked_timer; //running while we are being attacked (damaged) Timer swarm_timer; + Timer monkattack_timer; //additional timer for tiger claw usage Timer classattack_timer; Timer knightattack_timer; Timer assist_timer; //ask for help from nearby mobs diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 42ec94616..9334e4f6c 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -379,31 +379,35 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk) ReuseTime = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction; // Live AA - Technique of Master Wu - int wuchance = - itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + if (wuchance) { - const int MonkSPA[5] = {EQEmu::skills::SkillFlyingKick, EQEmu::skills::SkillDragonPunch, - EQEmu::skills::SkillEagleStrike, EQEmu::skills::SkillTigerClaw, - EQEmu::skills::SkillRoundKick}; + const int MonkSPA[5] = { + EQEmu::skills::SkillFlyingKick, + EQEmu::skills::SkillDragonPunch, + EQEmu::skills::SkillEagleStrike, + EQEmu::skills::SkillTigerClaw, + EQEmu::skills::SkillRoundKick + }; int extra = 0; // always 1/4 of the double attack chance, 25% at rank 5 (100/4) while (wuchance > 0) { - if (zone->random.Roll(wuchance)) - extra++; - else + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { break; + } wuchance /= 4; } // They didn't add a string ID for this. - std::string msg = StringFormat( - "The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); + std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); // live uses 400 here -- not sure if it's the best for all clients though SendColoredText(400, msg); auto classic = RuleB(Combat, ClassicMasterWu); while (extra) { - MonkSpecialAttack(GetTarget(), - classic ? MonkSPA[zone->random.Int(0, 4)] : ca_atk->m_skill); - extra--; + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : ca_atk->m_skill)); + --extra; } } From ac3b4ade10ba9bc68b96d42e36db546f866ae828 Mon Sep 17 00:00:00 2001 From: Noudess Date: Thu, 5 Mar 2020 10:44:01 -0500 Subject: [PATCH 58/60] Implement new always_aggro_foes field and functionality for mobs. --- utils/sql/db_update_manifest.txt | 1 + .../git/required/2020_03_05_npc_always_aggro.sql | 1 + zone/aggro.cpp | 8 +++++--- zone/beacon.cpp | 2 +- zone/client.cpp | 3 ++- zone/corpse.cpp | 10 ++++++---- zone/encounter.cpp | 2 +- zone/mob.cpp | 4 +++- zone/mob.h | 5 ++++- zone/npc.cpp | 3 ++- zone/zonedb.cpp | 14 ++++++++------ zone/zonedump.h | 1 + 12 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 utils/sql/git/required/2020_03_05_npc_always_aggro.sql diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 9d0aa9231..ca7a29f25 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -404,6 +404,7 @@ 9148|2020_01_28_corpse_guild_consent_id.sql|SHOW COLUMNS FROM `character_corpses` LIKE 'guild_consent_id'|empty| 9149|2020_02_06_globalloot.sql|SHOW COLUMNS FROM `global_loot` LIKE 'hot_zone'|empty| 9150|2020_02_06_aa_reset_on_death.sql|SHOW COLUMNS FROM `aa_ability` LIKE 'reset_on_death'|empty| +9151|2020_03_05_npc_always_aggro.sql|SHOW COLUMNS FROM `npc_types` LIKE 'always_aggros_foes'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2020_03_05_npc_always_aggro.sql b/utils/sql/git/required/2020_03_05_npc_always_aggro.sql new file mode 100644 index 000000000..d9e6351a3 --- /dev/null +++ b/utils/sql/git/required/2020_03_05_npc_always_aggro.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_types` ADD COLUMN `always_aggros_foes` tinyint(4) NOT NULL DEFAULT 0; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index ff0077353..fac15457e 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -139,7 +139,7 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { if (RuleB(Aggro, UseLevelAggro)) { - if (GetLevel() < RuleI(Aggro, MinAggroLevel) && mob->GetLevelCon(GetLevel()) == CON_GRAY && GetBodyType() != 3) + if (GetLevel() < RuleI(Aggro, MinAggroLevel) && mob->GetLevelCon(GetLevel()) == CON_GRAY && GetBodyType() != 3 && !AlwaysAggrosFoes()) { towho->Message(Chat::White, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2); return; @@ -147,7 +147,7 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { } else { - if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GRAY ) { + if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GRAY && !AlwaysAggrosFoes()) { towho->Message(Chat::White, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2); return; @@ -318,7 +318,7 @@ bool Mob::CheckWillAggro(Mob *mob) { //old InZone check taken care of above by !mob->CastToClient()->Connected() ( ( GetLevel() >= RuleI(Aggro, MinAggroLevel)) - ||(GetBodyType() == 3) + ||(GetBodyType() == 3) || AlwaysAggrosFoes() ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GRAY) @@ -352,6 +352,7 @@ bool Mob::CheckWillAggro(Mob *mob) { //old InZone check taken care of above by !mob->CastToClient()->Connected() ( ( GetINT() <= RuleI(Aggro, IntAggroThreshold) ) + || AlwaysAggrosFoes() ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GRAY) @@ -383,6 +384,7 @@ bool Mob::CheckWillAggro(Mob *mob) { LogAggro("Dist^2: [{}]\n", dist2); LogAggro("Range^2: [{}]\n", iAggroRange2); LogAggro("Faction: [{}]\n", fv); + LogAggro("AlwaysAggroFlag: [{}]\n", AlwaysAggrosFoes()); LogAggro("Int: [{}]\n", GetINT()); LogAggro("Con: [{}]\n", GetLevelCon(mob->GetLevel())); diff --git a/zone/beacon.cpp b/zone/beacon.cpp index 81b964290..ac4beaacc 100644 --- a/zone/beacon.cpp +++ b/zone/beacon.cpp @@ -56,7 +56,7 @@ Beacon::Beacon(Mob *at_mob, int lifetime) :Mob ( nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, at_mob->GetPosition(), 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQEmu::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQEmu::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false ), remove_timer(lifetime), spell_timer(0) diff --git a/zone/client.cpp b/zone/client.cpp index 6bd9941f8..8b60b391a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -121,7 +121,8 @@ Client::Client(EQStreamInterface* ieqs) 0, 0, 0, - 0 + 0, + false ), hpupdate_timer(2000), camp_timer(29000), diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 0ac100997..5858d5acc 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -154,7 +154,7 @@ Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NP in_npc->GetPosition(), in_npc->GetInnateLightType(), in_npc->GetTexture(),in_npc->GetHelmTexture(), 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,EQEmu::TintProfile(),0xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - (*in_npctypedata)->use_model), + (*in_npctypedata)->use_model, false), corpse_decay_timer(in_decaytime), corpse_rez_timer(0), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), @@ -260,8 +260,9 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( 0, // uint8 in_bracertexture, 0, // uint8 in_handtexture, 0, // uint8 in_legtexture, - 0, - 0 // uint8 in_feettexture, + 0, // uint8 in_feettexture, + 0, // uint8 in_usemodel, + 0 // bool in_always_aggros_foes ), corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), @@ -500,7 +501,8 @@ EQEmu::TintProfile(), 0, 0, 0, -0), +0, +false), corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), diff --git a/zone/encounter.cpp b/zone/encounter.cpp index e0ec83d90..7d37844b6 100644 --- a/zone/encounter.cpp +++ b/zone/encounter.cpp @@ -36,7 +36,7 @@ Encounter::Encounter(const char* enc_name) :Mob ( nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, glm::vec4(0,0,0,0), 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQEmu::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQEmu::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false ) { encounter_name[0] = 0; diff --git a/zone/mob.cpp b/zone/mob.cpp index c7b7559da..2e3d3d290 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -93,7 +93,8 @@ Mob::Mob( uint8 in_handtexture, uint8 in_legtexture, uint8 in_feettexture, - uint16 in_usemodel + uint16 in_usemodel, + bool in_always_aggros_foes ) : attack_timer(2000), attack_dw_timer(2000), @@ -275,6 +276,7 @@ Mob::Mob( qglobal = 0; spawned = false; rare_spawn = false; + always_aggros_foes = in_always_aggros_foes; InitializeBuffSlots(); diff --git a/zone/mob.h b/zone/mob.h index 7c095a001..2a89ba384 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -161,7 +161,8 @@ public: uint8 in_handtexture, uint8 in_legtexture, uint8 in_feettexture, - uint16 in_usemodel + uint16 in_usemodel, + bool in_always_aggros_foes ); virtual ~Mob(); @@ -578,6 +579,7 @@ public: inline const GravityBehavior GetFlyMode() const { return flymode; } bool IsBoat() const; bool IsControllableBoat() const; + inline const bool AlwaysAggrosFoes() const { return always_aggros_foes; } //Group virtual bool HasRaid() = 0; @@ -1389,6 +1391,7 @@ protected: Timer ranged_timer; float attack_speed; //% increase/decrease in attack speed (not haste) int attack_delay; //delay between attacks in 10ths of seconds + bool always_aggros_foes; int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; diff --git a/zone/npc.cpp b/zone/npc.cpp index 0b22bab3a..307e7a926 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -113,7 +113,8 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi npc_type_data->handtexture, npc_type_data->legtexture, npc_type_data->feettexture, - npc_type_data->use_model + npc_type_data->use_model, + npc_type_data->always_aggros_foes ), attacked_timer(CombatEventTimer_expire), swarm_timer(100), diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index ea4287f9d..3b03d2563 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2506,7 +2506,8 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load "npc_types.rare_spawn, " "npc_types.stuck_behavior, " "npc_types.model, " - "npc_types.flymode " + "npc_types.flymode, " + "npc_types.always_aggros_foes " "FROM npc_types %s", where_condition.c_str() ); @@ -2703,11 +2704,12 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load temp_npctype_data->charm_avoidance_rating = atoi(row[105]); temp_npctype_data->charm_atk = atoi(row[106]); - temp_npctype_data->skip_global_loot = atoi(row[107]) != 0; - temp_npctype_data->rare_spawn = atoi(row[108]) != 0; - temp_npctype_data->stuck_behavior = atoi(row[109]); - temp_npctype_data->use_model = atoi(row[110]); - temp_npctype_data->flymode = atoi(row[111]); + temp_npctype_data->skip_global_loot = atoi(row[107]) != 0; + temp_npctype_data->rare_spawn = atoi(row[108]) != 0; + temp_npctype_data->stuck_behavior = atoi(row[109]); + temp_npctype_data->use_model = atoi(row[110]); + temp_npctype_data->flymode = atoi(row[111]); + temp_npctype_data->always_aggros_foes = atoi(row[112]); temp_npctype_data->skip_auto_scale = false; // hardcoded here for now diff --git a/zone/zonedump.h b/zone/zonedump.h index 6010c9480..80508f233 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -147,6 +147,7 @@ struct NPCType int8 stuck_behavior; uint16 use_model; int8 flymode; + bool always_aggros_foes; }; namespace player_lootitem { From d85469dff938132d35dd894fed36642268c051f8 Mon Sep 17 00:00:00 2001 From: Noudess Date: Thu, 5 Mar 2020 13:04:21 -0500 Subject: [PATCH 59/60] Update version for always_aggros_foes db change. --- common/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/version.h b/common/version.h index a950edbe0..359c27576 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9150 +#define CURRENT_BINARY_DATABASE_VERSION 9151 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026 From 4915e86abaee2a35b38a035a350b2a8833f2c538 Mon Sep 17 00:00:00 2001 From: JJ Date: Thu, 5 Mar 2020 22:31:54 -0500 Subject: [PATCH 60/60] Slight tweak (field name) to #983 "Override LEVEL and INT aggro blocking". If you already ran the previous SQL, use this instead: ALTER TABLE `npc_types` CHANGE `always_aggros_foes` `always_aggro` TINYINT(1) NOT NULL DEFAULT '0'; --- utils/sql/db_update_manifest.txt | 2 +- utils/sql/git/required/2020_03_05_npc_always_aggro.sql | 2 +- zone/aggro.cpp | 10 +++++----- zone/corpse.cpp | 2 +- zone/mob.cpp | 4 ++-- zone/mob.h | 4 ++-- zone/npc.cpp | 2 +- zone/zonedb.cpp | 4 ++-- zone/zonedump.h | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index ca7a29f25..9be222856 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -404,7 +404,7 @@ 9148|2020_01_28_corpse_guild_consent_id.sql|SHOW COLUMNS FROM `character_corpses` LIKE 'guild_consent_id'|empty| 9149|2020_02_06_globalloot.sql|SHOW COLUMNS FROM `global_loot` LIKE 'hot_zone'|empty| 9150|2020_02_06_aa_reset_on_death.sql|SHOW COLUMNS FROM `aa_ability` LIKE 'reset_on_death'|empty| -9151|2020_03_05_npc_always_aggro.sql|SHOW COLUMNS FROM `npc_types` LIKE 'always_aggros_foes'|empty| +9151|2020_03_05_npc_always_aggro.sql|SHOW COLUMNS FROM `npc_types` LIKE 'always_aggro'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2020_03_05_npc_always_aggro.sql b/utils/sql/git/required/2020_03_05_npc_always_aggro.sql index d9e6351a3..83641998c 100644 --- a/utils/sql/git/required/2020_03_05_npc_always_aggro.sql +++ b/utils/sql/git/required/2020_03_05_npc_always_aggro.sql @@ -1 +1 @@ -ALTER TABLE `npc_types` ADD COLUMN `always_aggros_foes` tinyint(4) NOT NULL DEFAULT 0; +ALTER TABLE `npc_types` ADD COLUMN `always_aggro` tinyint(1) NOT NULL DEFAULT 0; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index fac15457e..87b98d599 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -139,7 +139,7 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { if (RuleB(Aggro, UseLevelAggro)) { - if (GetLevel() < RuleI(Aggro, MinAggroLevel) && mob->GetLevelCon(GetLevel()) == CON_GRAY && GetBodyType() != 3 && !AlwaysAggrosFoes()) + if (GetLevel() < RuleI(Aggro, MinAggroLevel) && mob->GetLevelCon(GetLevel()) == CON_GRAY && GetBodyType() != 3 && !AlwaysAggro()) { towho->Message(Chat::White, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2); return; @@ -147,7 +147,7 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { } else { - if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GRAY && !AlwaysAggrosFoes()) { + if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GRAY && !AlwaysAggro()) { towho->Message(Chat::White, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2); return; @@ -318,7 +318,7 @@ bool Mob::CheckWillAggro(Mob *mob) { //old InZone check taken care of above by !mob->CastToClient()->Connected() ( ( GetLevel() >= RuleI(Aggro, MinAggroLevel)) - ||(GetBodyType() == 3) || AlwaysAggrosFoes() + ||(GetBodyType() == 3) || AlwaysAggro() ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GRAY) @@ -352,7 +352,7 @@ bool Mob::CheckWillAggro(Mob *mob) { //old InZone check taken care of above by !mob->CastToClient()->Connected() ( ( GetINT() <= RuleI(Aggro, IntAggroThreshold) ) - || AlwaysAggrosFoes() + || AlwaysAggro() ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GRAY) @@ -384,7 +384,7 @@ bool Mob::CheckWillAggro(Mob *mob) { LogAggro("Dist^2: [{}]\n", dist2); LogAggro("Range^2: [{}]\n", iAggroRange2); LogAggro("Faction: [{}]\n", fv); - LogAggro("AlwaysAggroFlag: [{}]\n", AlwaysAggrosFoes()); + LogAggro("AlwaysAggroFlag: [{}]\n", AlwaysAggro()); LogAggro("Int: [{}]\n", GetINT()); LogAggro("Con: [{}]\n", GetLevelCon(mob->GetLevel())); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 5858d5acc..fd4174868 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -262,7 +262,7 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( 0, // uint8 in_legtexture, 0, // uint8 in_feettexture, 0, // uint8 in_usemodel, - 0 // bool in_always_aggros_foes + 0 // bool in_always_aggro ), corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), diff --git a/zone/mob.cpp b/zone/mob.cpp index 2e3d3d290..5d25c42f0 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -94,7 +94,7 @@ Mob::Mob( uint8 in_legtexture, uint8 in_feettexture, uint16 in_usemodel, - bool in_always_aggros_foes + bool in_always_aggro ) : attack_timer(2000), attack_dw_timer(2000), @@ -276,7 +276,7 @@ Mob::Mob( qglobal = 0; spawned = false; rare_spawn = false; - always_aggros_foes = in_always_aggros_foes; + always_aggro = in_always_aggro; InitializeBuffSlots(); diff --git a/zone/mob.h b/zone/mob.h index 2a89ba384..f1c128080 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -579,7 +579,7 @@ public: inline const GravityBehavior GetFlyMode() const { return flymode; } bool IsBoat() const; bool IsControllableBoat() const; - inline const bool AlwaysAggrosFoes() const { return always_aggros_foes; } + inline const bool AlwaysAggro() const { return always_aggro; } //Group virtual bool HasRaid() = 0; @@ -1391,7 +1391,7 @@ protected: Timer ranged_timer; float attack_speed; //% increase/decrease in attack speed (not haste) int attack_delay; //delay between attacks in 10ths of seconds - bool always_aggros_foes; + bool always_aggro; int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; diff --git a/zone/npc.cpp b/zone/npc.cpp index 307e7a926..6df237d5d 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -114,7 +114,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi npc_type_data->legtexture, npc_type_data->feettexture, npc_type_data->use_model, - npc_type_data->always_aggros_foes + npc_type_data->always_aggro ), attacked_timer(CombatEventTimer_expire), swarm_timer(100), diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 3b03d2563..cad4e1df8 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2507,7 +2507,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load "npc_types.stuck_behavior, " "npc_types.model, " "npc_types.flymode, " - "npc_types.always_aggros_foes " + "npc_types.always_aggro " "FROM npc_types %s", where_condition.c_str() ); @@ -2709,7 +2709,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load temp_npctype_data->stuck_behavior = atoi(row[109]); temp_npctype_data->use_model = atoi(row[110]); temp_npctype_data->flymode = atoi(row[111]); - temp_npctype_data->always_aggros_foes = atoi(row[112]); + temp_npctype_data->always_aggro = atoi(row[112]); temp_npctype_data->skip_auto_scale = false; // hardcoded here for now diff --git a/zone/zonedump.h b/zone/zonedump.h index 80508f233..0a1bcd07d 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -147,7 +147,7 @@ struct NPCType int8 stuck_behavior; uint16 use_model; int8 flymode; - bool always_aggros_foes; + bool always_aggro; }; namespace player_lootitem {