From 05b3c8972928c6c4a1bafd7f7fa226a2b50e6743 Mon Sep 17 00:00:00 2001 From: Trust Date: Wed, 13 Nov 2019 22:51:33 +0000 Subject: [PATCH 001/157] Mobs will not trade anymore if they are currently in combat. --- zone/client_packet.cpp | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0bb319a20..24e1d35c9 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -14099,16 +14099,25 @@ void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) #else else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { #endif - //npcs always accept - trade->Start(msg->to_mob_id); + // If the NPC is engaged, we cannot trade with it. + // Note that this work as intended, if the NPC is charmed + // you can still trade with it. + if (tradee->IsEngaged()) { + Message(0, "Your target cannot trade with you at this moment."); + } + // If it not engaged, it will automatically accept the trade. + else { + //npcs always accept + trade->Start(msg->to_mob_id); - auto outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); - TradeRequest_Struct* acc = (TradeRequest_Struct*)outapp->pBuffer; - acc->from_mob_id = msg->to_mob_id; - acc->to_mob_id = msg->from_mob_id; - FastQueuePacket(&outapp); - safe_delete(outapp); - } + EQApplicationPacket *outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); + TradeRequest_Struct *acc = (TradeRequest_Struct *) outapp->pBuffer; + acc->from_mob_id = msg->to_mob_id; + acc->to_mob_id = msg->from_mob_id; + FastQueuePacket(&outapp); + safe_delete(outapp); + } + } return; } From 0fcaf82038937f958543d3e3913c23c49553104b Mon Sep 17 00:00:00 2001 From: Trust Date: Wed, 13 Nov 2019 23:46:36 +0000 Subject: [PATCH 002/157] Per comment, removing message and simplified. --- zone/client_packet.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 24e1d35c9..03b6a9c58 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -14099,17 +14099,8 @@ void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) #else else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { #endif - // If the NPC is engaged, we cannot trade with it. - // Note that this work as intended, if the NPC is charmed - // you can still trade with it. - if (tradee->IsEngaged()) { - Message(0, "Your target cannot trade with you at this moment."); - } - // If it not engaged, it will automatically accept the trade. - else { - //npcs always accept + if (tradee->!IsEngaged()) { trade->Start(msg->to_mob_id); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); TradeRequest_Struct *acc = (TradeRequest_Struct *) outapp->pBuffer; acc->from_mob_id = msg->to_mob_id; From b567e57971a8e0240bc5d5e676512dc4d68a7b6f Mon Sep 17 00:00:00 2001 From: Trust Date: Thu, 14 Nov 2019 02:15:35 +0000 Subject: [PATCH 003/157] Fixed incorrect logic --- 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 03b6a9c58..4b9fce1b3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -14099,7 +14099,7 @@ void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) #else else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { #endif - if (tradee->!IsEngaged()) { + if (!tradee->IsEngaged()) { trade->Start(msg->to_mob_id); EQApplicationPacket *outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); TradeRequest_Struct *acc = (TradeRequest_Struct *) outapp->pBuffer; From a325a9978bb6d585ceae2466ba2d1f9eefff3f63 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 18 Nov 2019 19:39:03 -0500 Subject: [PATCH 004/157] Added EVENT_COMBINE_VALIDATE to facilitate special case tradeskill combines --- zone/embparser.cpp | 19 +++++++++++++++++++ zone/event_codes.h | 1 + zone/lua_parser.cpp | 4 +++- zone/lua_parser_events.cpp | 25 +++++++++++++++++++++++++ zone/lua_parser_events.h | 2 ++ zone/tradeskills.cpp | 9 +++++++++ 6 files changed, 59 insertions(+), 1 deletion(-) diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 4ccb0747e..8d089b182 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -118,6 +118,7 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_SPAWN_ZONE", "EVENT_DEATH_ZONE", "EVENT_USE_SKILL", + "EVENT_COMBINE_VALIDATE", }; PerlembParser::PerlembParser() : perl(nullptr) { @@ -1440,6 +1441,24 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID ExportVar(package_name.c_str(), "skill_level", sep.arg[1]); break; } + case EVENT_COMBINE_VALIDATE: { + Seperator sep(data); + ExportVar(package_name.c_str(), "recipe_id", extradata); + ExportVar(package_name.c_str(), "validate_type", sep.arg[0]); + + std::string zone_id = "-1"; + std::string tradeskill_id = "-1"; + if (strcmp(sep.arg[0], "check_zone") == 0) { + zone_id = sep.arg[1]; + } + else if (strcmp(sep.arg[0], "check_tradeskill") == 0) { + tradeskill_id = sep.arg[1]; + } + + ExportVar(package_name.c_str(), "zone_id", zone_id.c_str()); + ExportVar(package_name.c_str(), "tradeskill_id", tradeskill_id.c_str()); + break; + } default: { break; diff --git a/zone/event_codes.h b/zone/event_codes.h index bb623f041..4560cc767 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -86,6 +86,7 @@ typedef enum { EVENT_SPAWN_ZONE, EVENT_DEATH_ZONE, EVENT_USE_SKILL, + EVENT_COMBINE_VALIDATE, _LargestEventID } QuestEventID; diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 7c7242e4c..437bc5121 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -123,7 +123,8 @@ const char *LuaEvents[_LargestEventID] = { "event_tick", "event_spawn_zone", "event_death_zone", - "event_use_skill" + "event_use_skill", + "event_combine_validate" }; extern Zone *zone; @@ -206,6 +207,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_RESPAWN] = handle_player_respawn; PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet; PlayerArgumentDispatch[EVENT_USE_SKILL] = handle_player_use_skill; + PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate; ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index e70c5de9f..f4f1dd3af 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -514,6 +514,31 @@ void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client lua_setfield(L, -2, "skill_level"); } +void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers) { + Seperator sep(data.c_str()); + lua_pushinteger(L, extra_data); + lua_setfield(L, -2, "recipe_id"); + + lua_pushstring(L, sep.arg[0]); + lua_setfield(L, -2, "validate_type"); + + int zone_id = -1; + int tradeskill_id = -1; + if (strcmp(sep.arg[0], "check_zone") == 0) { + zone_id = std::stoi(sep.arg[1]); + } + else if (strcmp(sep.arg[0], "check_tradeskill") == 0) { + tradeskill_id = std::stoi(sep.arg[1]); + } + + lua_pushinteger(L, zone_id); + lua_setfield(L, -2, "zone_id"); + + lua_pushinteger(L, tradeskill_id); + lua_setfield(L, -2, "tradeskill_id"); +} + //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers) { diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 44ba9b72f..0054b31e1 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -97,6 +97,8 @@ void handle_player_null(QuestInterface *parse, lua_State* L, Client* client, std std::vector *extra_pointers); void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector *extra_pointers); +void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers); //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 5113ad4b6..b4451119f 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -392,6 +392,15 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob } } + // final check for any additional quest requirements .. "check_zone" in this case - exported as variable [validate_type] + if (parse->EventPlayer(EVENT_COMBINE_VALIDATE, user, fmt::format("check_zone {}", zone->GetZoneID()), spec.recipe_id) != 0) { + user->Message(Chat::Emote, "You cannot make this combine because the location requirement has not been met."); + auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + // Send acknowledgement packets to client auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0); user->QueuePacket(outapp); From b8623bf6df096e525a26b8f1489a368329b62ae6 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 25 Nov 2019 19:56:29 -0500 Subject: [PATCH 005/157] Added command 'nudge' --- zone/command.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ zone/command.h | 1 + 2 files changed, 77 insertions(+) diff --git a/zone/command.cpp b/zone/command.cpp index 150750494..3d71e4ab4 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -295,6 +295,7 @@ int command_init(void) command_add("npcstats", "- Show stats about target NPC", 80, command_npcstats) || command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) || command_add("npctypespawn", "[npctypeid] [factionid] - Spawn an NPC from the db", 10, command_npctypespawn) || + command_add("nudge", "- Nudge your target's current position by specific values", 80, command_nudge) || command_add("nukebuffs", "- Strip all buffs on you or your target", 50, command_nukebuffs) || command_add("nukeitem", "[itemid] - Remove itemid from your player target's inventory", 150, command_nukeitem) || command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", 100, command_object) || @@ -3127,6 +3128,81 @@ void command_npctypespawn(Client *c, const Seperator *sep) } +void command_nudge(Client* c, const Seperator* sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #nudge [x=f] [y=f] [z=f] [h=f] (partial/mixed arguments allowed)"); + } + else { + + auto target = c->GetTarget(); + if (!target) { + + c->Message(Chat::Yellow, "This command requires a target."); + return; + } + if (target->IsMoving()) { + + c->Message(Chat::Yellow, "This command requires a stationary target."); + return; + } + + glm::vec4 position_offset(0.0f, 0.0f, 0.0f, 0.0f); + for (auto index = 1; index <= 4; ++index) { + + if (!sep->arg[index]) { + continue; + } + + Seperator argsep(sep->arg[index], '='); + if (!argsep.arg[1][0]) { + continue; + } + + switch (argsep.arg[0][0]) { + case 'x': + position_offset.x = atof(argsep.arg[1]); + break; + case 'y': + position_offset.y = atof(argsep.arg[1]); + break; + case 'z': + position_offset.z = atof(argsep.arg[1]); + break; + case 'h': + position_offset.w = atof(argsep.arg[1]); + break; + default: + break; + } + } + + const auto& current_position = target->GetPosition(); + glm::vec4 new_position( + (current_position.x + position_offset.x), + (current_position.y + position_offset.y), + (current_position.z + position_offset.z), + (current_position.w + position_offset.w) + ); + + target->GMMove(new_position.x, new_position.y, new_position.z, new_position.w); + + c->Message( + Chat::White, + "Nudging '%s' to {%1.3f, %1.3f, %1.3f, %1.2f} (adjustment: {%1.3f, %1.3f, %1.3f, %1.2f})", + target->GetName(), + new_position.x, + new_position.y, + new_position.z, + new_position.w, + position_offset.x, + position_offset.y, + position_offset.z, + position_offset.w + ); + } +} + void command_heal(Client *c, const Seperator *sep) { if (c->GetTarget()==0) diff --git a/zone/command.h b/zone/command.h index 3a7dcc400..1c9d8fe7c 100644 --- a/zone/command.h +++ b/zone/command.h @@ -194,6 +194,7 @@ void command_npcspecialattk(Client *c, const Seperator *sep); void command_npcstats(Client *c, const Seperator *sep); void command_npctype_cache(Client *c, const Seperator *sep); void command_npctypespawn(Client *c, const Seperator *sep); +void command_nudge(Client* c, const Seperator* sep); void command_nukebuffs(Client *c, const Seperator *sep); void command_nukeitem(Client *c, const Seperator *sep); void command_numauths(Client *c, const Seperator *sep); From 86593798a9d9c6cd99d9365f816ecfd679e1a912 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 1 Dec 2019 17:59:38 -0500 Subject: [PATCH 006/157] Added safety check for zone dereference in ServerOP_ReloadRules handler --- zone/worldserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 4f7e35ace..a5180963d 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1806,8 +1806,8 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) worldserver.SendEmoteMessage( 0, 0, 100, 15, "Rules reloaded for Zone: '%s' Instance ID: %u", - zone->GetLongName(), - zone->GetInstanceID() + (zone ? zone->GetLongName() : StringFormat("Null zone pointer [pid]:[%i]", getpid()).c_str()), + (zone ? zone->GetInstanceID() : 0xFFFFFFFFF) ); RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true); break; From 35fe27eb5d659575a5d4ea3e72dc73dbfe70e1f2 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 3 Dec 2019 22:01:13 -0500 Subject: [PATCH 007/157] Added bot commands 'applypoison' and 'applypotion' .. new bot owner option 'buffcounter' --- common/ruletypes.h | 3 + zone/bot.cpp | 169 +++++++++++++++++++++++++++++++++- zone/bot_command.cpp | 202 ++++++++++++++++++++++++++++++++++++++++- zone/bot_command.h | 3 + zone/bot_database.cpp | 1 + zone/client.cpp | 1 + zone/client.h | 1 + zone/entity.h | 1 + zone/spell_effects.cpp | 56 ++++++++++++ 9 files changed, 433 insertions(+), 4 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 0a388b4eb..6dafb82c2 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -599,6 +599,9 @@ RULE_INT(Bots, AllowedGenders, 0x3, "Bitmask of allowed bot genders") RULE_BOOL(Bots, AllowOwnerOptionAltCombat, true, "When option is enabled, bots will use an auto-/shared-aggro combat model") RULE_BOOL(Bots, AllowOwnerOptionAutoDefend, true, "When option is enabled, bots will defend their owner on enemy aggro") RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel from leash owner before being pulled back (squared value)") +RULE_BOOL(Bots, AllowApplyPoisonCommand, true, "Allows the use of the bot command 'applypoison'") +RULE_BOOL(Bots, AllowApplyPotionCommand, true, "Allows the use of the bot command 'applypotion'") +RULE_BOOL(Bots, RestrictApplyPotionToRogue, true, "Restricts the bot command 'applypotion' to rogue-usable potions (i.e., poisons)") RULE_CATEGORY_END() #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index b9e329292..50620cdd5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -236,8 +236,157 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to LoadAAs(); - if (!database.botdb.LoadBuffs(this) && bot_owner) + // copied from client CompleteConnect() handler - watch for problems + // (may have to move to post-spawn location if certain buffs still don't process correctly) + if (database.botdb.LoadBuffs(this) && bot_owner) { + + //reapply some buffs + uint32 buff_count = GetMaxTotalSlots(); + for (uint32 j1 = 0; j1 < buff_count; j1++) { + if (!IsValidSpell(buffs[j1].spellid)) + continue; + + const SPDat_Spell_Struct& spell = spells[buffs[j1].spellid]; + + int NimbusEffect = GetNimbusEffect(buffs[j1].spellid); + if (NimbusEffect) { + if (!IsNimbusEffectActive(NimbusEffect)) + SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true); + } + + for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { + switch (spell.effectid[x1]) { + case SE_IllusionCopy: + case SE_Illusion: { + if (spell.base[x1] == -1) { + if (gender == 1) + gender = 0; + else if (gender == 0) + gender = 1; + SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); + } + else if (spell.base[x1] == -2) // WTF IS THIS + { + if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) + SendIllusionPacket(GetRace(), GetGender(), spell.base2[x1], spell.max[x1]); + } + else if (spell.max[x1] > 0) + { + SendIllusionPacket(spell.base[x1], 0xFF, spell.base2[x1], spell.max[x1]); + } + else + { + SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); + } + switch (spell.base[x1]) { + case OGRE: + SendAppearancePacket(AT_Size, 9); + break; + case TROLL: + SendAppearancePacket(AT_Size, 8); + break; + case VAHSHIR: + case BARBARIAN: + SendAppearancePacket(AT_Size, 7); + break; + case HALF_ELF: + case WOOD_ELF: + case DARK_ELF: + case FROGLOK: + SendAppearancePacket(AT_Size, 5); + break; + case DWARF: + SendAppearancePacket(AT_Size, 4); + break; + case HALFLING: + case GNOME: + SendAppearancePacket(AT_Size, 3); + break; + default: + SendAppearancePacket(AT_Size, 6); + break; + } + break; + } + //case SE_SummonHorse: { + // SummonHorse(buffs[j1].spellid); + // //hasmount = true; //this was false, is that the correct thing? + // break; + //} + case SE_Silence: + { + Silence(true); + break; + } + case SE_Amnesia: + { + Amnesia(true); + break; + } + case SE_DivineAura: + { + invulnerable = true; + break; + } + case SE_Invisibility2: + case SE_Invisibility: + { + invisible = true; + SendAppearancePacket(AT_Invis, 1); + break; + } + case SE_Levitate: + { + if (!zone->CanLevitate()) + { + //if (!GetGM()) + //{ + SendAppearancePacket(AT_Levitate, 0); + BuffFadeByEffect(SE_Levitate); + //Message(Chat::Red, "You can't levitate in this zone."); + //} + } + else { + SendAppearancePacket(AT_Levitate, 2); + } + break; + } + case SE_InvisVsUndead2: + case SE_InvisVsUndead: + { + invisible_undead = true; + break; + } + case SE_InvisVsAnimals: + { + invisible_animals = true; + break; + } + case SE_AddMeleeProc: + case SE_WeaponProc: + { + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid, buffs[j1].casterlevel); + break; + } + case SE_DefensiveProc: + { + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + case SE_RangedProc: + { + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + } + } + } + + + } + else { bot_owner->Message(Chat::Red, "&s for '%s'", BotDatabase::fail::LoadBuffs(), GetCleanName()); + } CalcBotStats(false); hp_regen = CalcHPRegen(); @@ -4726,9 +4875,9 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQEmu::skills::Sk Mob *my_owner = GetBotOwner(); if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOption(Client::booDeathMarquee)) { if (killerMob) - my_owner->CastToClient()->SendMarqueeMessage(Chat::Yellow, 510, 0, 1000, 3000, StringFormat("%s has been slain by %s", GetCleanName(), killerMob->GetCleanName())); + my_owner->CastToClient()->SendMarqueeMessage(Chat::Red, 510, 0, 1000, 3000, StringFormat("%s has been slain by %s", GetCleanName(), killerMob->GetCleanName())); else - my_owner->CastToClient()->SendMarqueeMessage(Chat::Yellow, 510, 0, 1000, 3000, StringFormat("%s has been slain", GetCleanName())); + my_owner->CastToClient()->SendMarqueeMessage(Chat::Red, 510, 0, 1000, 3000, StringFormat("%s has been slain", GetCleanName())); } Mob *give_exp = hate_list.GetDamageTopOnHateList(this); @@ -8989,6 +9138,20 @@ Bot* EntityList::GetBotByBotName(std::string botName) { return Result; } +Client* EntityList::GetBotOwnerByBotEntityID(uint16 entityID) { + Client* Result = nullptr; + if (entityID > 0) { + for (std::list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) { + Bot* tempBot = *botListItr; + if (tempBot && tempBot->GetID() == entityID) { + Result = tempBot->GetBotOwner()->CastToClient(); + break; + } + } + } + return Result; +} + void EntityList::AddBot(Bot *newBot, bool SendSpawnPacket, bool dontqueue) { if(newBot) { newBot->SetID(GetFreeID()); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 6a0ae2ff0..0f469a03c 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1321,6 +1321,8 @@ int bot_command_init(void) if ( bot_command_add("actionable", "Lists actionable command arguments and use descriptions", 0, bot_command_actionable) || bot_command_add("aggressive", "Orders a bot to use a aggressive discipline", 0, bot_command_aggressive) || + bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", 0, bot_command_apply_poison) || + bot_command_add("applypotion", "Applies cursor-held potion to a bot's effects", 0, bot_command_apply_potion) || bot_command_add("attack", "Orders bots to attack a designated target", 0, bot_command_attack) || bot_command_add("bindaffinity", "Orders a bot to attempt an affinity binding", 0, bot_command_bind_affinity) || bot_command_add("bot", "Lists the available bot management [subcommands]", 0, bot_command_bot) || @@ -2579,6 +2581,166 @@ void bot_command_aggressive(Client *c, const Seperator *sep) c->Message(m_action, "%i of %i bots have used aggressive disciplines", success_count, candidate_count); } +void bot_command_apply_poison(Client *c, const Seperator *sep) +{ + if (helper_command_disabled(c, RuleB(Bots, AllowApplyPoisonCommand), "applypoison")) { + return; + } + if (helper_command_alias_fail(c, "bot_command_apply_poison", sep->arg[0], "applypoison")) { + return; + } + if (helper_is_help_or_usage(sep->arg[1])) { + + c->Message(m_usage, "usage: %s", sep->arg[0]); + return; + } + + Bot *my_rogue_bot = nullptr; + if (c->GetTarget() && c->GetTarget()->IsBot() && c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID() && c->GetTarget()->CastToBot()->GetClass() == ROGUE) { + my_rogue_bot = c->GetTarget()->CastToBot(); + } + if (!my_rogue_bot) { + + c->Message(m_fail, "You must target a rogue bot that you own to use this command!"); + return; + } + if (my_rogue_bot->GetLevel() < 18) { + + c->Message(m_fail, "Your rogue bot must be level 18 before %s can apply poison!", (my_rogue_bot->GetGender() == 1 ? "she" : "he")); + return; + } + + const auto poison_instance = c->GetInv().GetItem(EQEmu::invslot::slotCursor); + if (!poison_instance) { + + c->Message(m_fail, "No item found on cursor!"); + return; + } + + auto poison_data = poison_instance->GetItem(); + if (!poison_data) { + + c->Message(m_fail, "No data found for cursor item!"); + return; + } + + if (poison_data->ItemType == EQEmu::item::ItemTypePoison) { + + if ((~poison_data->Races) & GetPlayerRaceBit(my_rogue_bot->GetRace())) { + + c->Message(m_fail, "Invalid race for weapon poison!"); + return; + } + + if (poison_data->Proc.Level2 > my_rogue_bot->GetLevel()) { + + c->Message(m_fail, "This poison is too powerful for your intended target!"); + return; + } + + // generalized from client ApplyPoison handler + double ChanceRoll = zone->random.Real(0, 1); + uint16 poison_skill = 95 + ((my_rogue_bot->GetLevel() - 18) * 5); + if (poison_skill > 200) { + poison_skill = 200; + } + bool apply_poison_chance = (ChanceRoll < (.75 + poison_skill / 1000)); + + if (apply_poison_chance && my_rogue_bot->AddProcToWeapon(poison_data->Proc.Effect, false, (my_rogue_bot->GetDEX() / 100) + 103, POISON_PROC)) { + c->Message(m_action, "Successfully applied %s to %s's weapon.", poison_data->Name, my_rogue_bot->GetCleanName()); + } + else { + c->Message(m_fail, "Failed to apply %s to %s's weapon.", poison_data->Name, my_rogue_bot->GetCleanName()); + } + + c->DeleteItemInInventory(EQEmu::invslot::slotCursor, 1, true); + } + else { + + c->Message(m_fail, "Item on cursor is not a weapon poison!"); + return; + } +} + +void bot_command_apply_potion(Client* c, const Seperator* sep) +{ + if (helper_command_disabled(c, RuleB(Bots, AllowApplyPotionCommand), "applypotion")) { + return; + } + if (helper_command_alias_fail(c, "bot_command_apply_potion", sep->arg[0], "applypotion")) { + return; + } + if (helper_is_help_or_usage(sep->arg[1])) { + + c->Message(m_usage, "usage: %s", sep->arg[0]); + return; + } + + Bot* my_bot = nullptr; + if (c->GetTarget() && c->GetTarget()->IsBot() && c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) { + my_bot = c->GetTarget()->CastToBot(); + } + if (!my_bot) { + + c->Message(m_fail, "You must target a bot that you own to use this command!"); + return; + } + + const auto potion_instance = c->GetInv().GetItem(EQEmu::invslot::slotCursor); + if (!potion_instance) { + + c->Message(m_fail, "No item found on cursor!"); + return; + } + + auto potion_data = potion_instance->GetItem(); + if (!potion_data) { + + c->Message(m_fail, "No data found for cursor item!"); + return; + } + + if (potion_data->ItemType == EQEmu::item::ItemTypePotion && potion_data->Click.Effect > 0) { + + if (RuleB(Bots, RestrictApplyPotionToRogue) && potion_data->Classes != PLAYER_CLASS_ROGUE_BIT) { + + c->Message(m_fail, "This command is restricted to rogue poison potions only!"); + return; + } + if ((~potion_data->Races) & GetPlayerRaceBit(my_bot->GetRace())) { + + c->Message(m_fail, "Invalid race for potion!"); + return; + } + if ((~potion_data->Classes) & GetPlayerClassBit(my_bot->GetClass())) { + + c->Message(m_fail, "Invalid class for potion!"); + return; + } + + if (potion_data->Click.Level2 > my_bot->GetLevel()) { + + c->Message(m_fail, "This potion is too powerful for your intended target!"); + return; + } + + // TODO: figure out best way to handle casting time/animation + if (my_bot->SpellFinished(potion_data->Click.Effect, my_bot, EQEmu::spells::CastingSlot::Item, 0)) { + c->Message(m_action, "Successfully applied %s to %s's buff effects.", potion_data->Name, my_bot->GetCleanName()); + } + else { + c->Message(m_fail, "Failed to apply %s to %s's buff effects.", potion_data->Name, my_bot->GetCleanName()); + } + + c->DeleteItemInInventory(EQEmu::invslot::slotCursor, 1, true); + } + else { + + c->Message(m_fail, "Item on cursor is not a potion!"); + return; + } +} + void bot_command_attack(Client *c, const Seperator *sep) { if (helper_command_alias_fail(c, "bot_command_attack", sep->arg[0], "attack")) { @@ -3637,6 +3799,16 @@ void bot_command_owner_option(Client *c, const Seperator *sep) "null" "(toggles)" "" + "" + "buffcounter" + "enable | disable" + "marquee message on buff counter change" + "" + "" + "" + "null" + "(toggles)" + "" "" "current" "" @@ -3796,6 +3968,22 @@ void bot_command_owner_option(Client *c, const Seperator *sep) c->Message(m_fail, "Bot owner option 'autodefend' is not allowed on this server."); } } + else if (!owner_option.compare("buffcounter")) { + + if (!argument.compare("enable")) { + c->SetBotOption(Client::booBuffCounter, true); + } + else if (!argument.compare("disable")) { + c->SetBotOption(Client::booBuffCounter, false); + } + else { + c->SetBotOption(Client::booBuffCounter, !c->GetBotOption(Client::booBuffCounter)); + } + + database.botdb.SaveOwnerOption(c->CharacterID(), Client::booBuffCounter, c->GetBotOption(Client::booBuffCounter)); + + c->Message(m_action, "Bot 'buff counter' is now %s.", (c->GetBotOption(Client::booBuffCounter) == true ? "enabled" : "disabled")); + } else if (!owner_option.compare("current")) { std::string window_title = "Current Bot Owner Options Settings"; @@ -3811,13 +3999,15 @@ void bot_command_owner_option(Client *c, const Seperator *sep) "" "spawnmessage" "{}" "" "" "altcombat" "{}" "" "" "autodefend" "{}" "" + "" "buffcounter" "{}" "" "", (c->GetBotOption(Client::booDeathMarquee) ? "enabled" : "disabled"), (c->GetBotOption(Client::booStatsUpdate) ? "enabled" : "disabled"), (c->GetBotOption(Client::booSpawnMessageSay) ? "say" : (c->GetBotOption(Client::booSpawnMessageTell) ? "tell" : "silent")), (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") + (RuleB(Bots, AllowOwnerOptionAutoDefend) ? (c->GetBotOption(Client::booAutoDefend) ? "enabled" : "disabled") : "restricted"), + (c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled") ); c->SendPopupToClient(window_title.c_str(), window_text.c_str()); @@ -8444,6 +8634,16 @@ bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQEmu::spells::CastingSlot::Gem2, -1, -1, dont_root_before); } +bool helper_command_disabled(Client* bot_owner, bool rule_value, const char* command) +{ + if (rule_value == false) { + bot_owner->Message(m_fail, "Bot command %s is not enabled on this server.", command); + return true; + } + + return false; +} + bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command) { auto alias_iter = bot_command_aliases.find(&alias[1]); diff --git a/zone/bot_command.h b/zone/bot_command.h index d710e106f..b3f328a33 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -553,6 +553,8 @@ void bot_command_log_command(Client *c, const char *message); // bot commands void bot_command_actionable(Client *c, const Seperator *sep); void bot_command_aggressive(Client *c, const Seperator *sep); +void bot_command_apply_poison(Client *c, const Seperator *sep); +void bot_command_apply_potion(Client* c, const Seperator* sep); void bot_command_attack(Client *c, const Seperator *sep); void bot_command_bind_affinity(Client *c, const Seperator *sep); void bot_command_bot(Client *c, const Seperator *sep); @@ -671,6 +673,7 @@ void helper_bot_appearance_form_update(Bot *my_bot); uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_class, uint16 bot_race, uint8 bot_gender); void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot); bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast = true, uint32* dont_root_before = nullptr); +bool helper_command_disabled(Client *bot_owner, bool rule_value, const char *command); bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command); void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_bot, bcst_list* local_list, bool single_flag = false); bool helper_is_help_or_usage(const char* arg); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 8496dfcd8..2f6f9c1c6 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2256,6 +2256,7 @@ bool BotDatabase::SaveOwnerOption(const uint32 owner_id, size_t type, const bool case Client::booSpawnMessageClassSpecific: case Client::booAltCombat: case Client::booAutoDefend: + case Client::booBuffCounter: { 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 dae6c8912..911c090dc 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -356,6 +356,7 @@ Client::Client(EQStreamInterface* ieqs) bot_owner_options[booSpawnMessageClassSpecific] = true; bot_owner_options[booAltCombat] = RuleB(Bots, AllowOwnerOptionAltCombat); bot_owner_options[booAutoDefend] = RuleB(Bots, AllowOwnerOptionAutoDefend); + bot_owner_options[booBuffCounter] = false; SetBotPulling(false); SetBotPrecombat(false); diff --git a/zone/client.h b/zone/client.h index 6fa4835c9..82b8c7475 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1640,6 +1640,7 @@ public: booSpawnMessageClassSpecific, booAltCombat, booAutoDefend, + booBuffCounter, _booCount }; diff --git a/zone/entity.h b/zone/entity.h index 6b22f1a4c..7631f0532 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -553,6 +553,7 @@ private: Mob* GetMobByBotID(uint32 botID); Bot* GetBotByBotID(uint32 botID); Bot* GetBotByBotName(std::string botName); + Client* GetBotOwnerByBotEntityID(uint16 entityID); std::list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 406a801d0..2014a643a 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5662,11 +5662,24 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) bool bDepleted = false; int buff_max = GetMaxTotalSlots(); +#ifdef BOTS + std::string buff_name; + size_t buff_counter = 0; + bool buff_update = false; +#endif + //Spell specific procs [Type 7,10,11] if (IsValidSpell(spell_id)) { for (int d = 0; d < buff_max; d++) { if (buffs[d].spellid == spell_id && buffs[d].numhits > 0 && spells[buffs[d].spellid].numhitstype == static_cast(type)) { + +#ifdef BOTS + buff_name = spells[buffs[d].spellid].name; + buff_counter = (buffs[d].numhits - 1); + buff_update = true; +#endif + if (--buffs[d].numhits == 0) { CastOnNumHitFade(buffs[d].spellid); if (!TryFadeEffect(d)) @@ -5679,6 +5692,13 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) } else if (type == NumHit::MatchingSpells) { if (buff_slot >= 0) { if (--buffs[buff_slot].numhits == 0) { + +#ifdef BOTS + buff_name = spells[buffs[buff_slot].spellid].name; + buff_counter = (buffs[buff_slot].numhits - 1); + buff_update = true; +#endif + CastOnNumHitFade(buffs[buff_slot].spellid); if (!TryFadeEffect(buff_slot)) BuffFadeBySlot(buff_slot , true); @@ -5691,6 +5711,13 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) continue; if (IsValidSpell(buffs[d].spellid) && m_spellHitsLeft[d] == buffs[d].spellid) { + +#ifdef BOTS + buff_name = spells[buffs[d].spellid].name; + buff_counter = (buffs[d].numhits - 1); + buff_update = true; +#endif + if (--buffs[d].numhits == 0) { CastOnNumHitFade(buffs[d].spellid); m_spellHitsLeft[d] = 0; @@ -5706,6 +5733,13 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) for (int d = 0; d < buff_max; d++) { if (IsValidSpell(buffs[d].spellid) && buffs[d].numhits > 0 && spells[buffs[d].spellid].numhitstype == static_cast(type)) { + +#ifdef BOTS + buff_name = spells[buffs[d].spellid].name; + buff_counter = (buffs[d].numhits - 1); + buff_update = true; +#endif + if (--buffs[d].numhits == 0) { CastOnNumHitFade(buffs[d].spellid); if (!TryFadeEffect(d)) @@ -5716,6 +5750,28 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) } } } + +#ifdef BOTS + if (IsBot() && buff_update) { + auto bot_owner = entity_list.GetBotOwnerByBotEntityID(GetID()); + if (bot_owner && bot_owner->GetBotOption(Client::booBuffCounter)) { + bot_owner->CastToClient()->SendMarqueeMessage( + Chat::Yellow, + 510, + 0, + 1000, + 3000, + StringFormat( + "%s has [%u] hit%s remaining on '%s'", + GetCleanName(), + buff_counter, + (buff_counter == 1 ? "" : "s"), + buff_name.c_str() + ) + ); + } + } +#endif } //for some stupid reason SK procs return theirs one base off... From 9910b07a4e9744444654443b7c895adaab6f6e59 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 3 Dec 2019 23:39:46 -0500 Subject: [PATCH 008/157] Changed `Bots:AAExpansion` to `Bots:BotExpansionSettings` and converted it bitmask use; Fix for bots not honoring aa expansion setting --- common/ruletypes.h | 2 +- zone/aa.cpp | 16 ++++++++++++---- zone/bot.cpp | 8 +++++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 6dafb82c2..82eddca7f 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -572,7 +572,7 @@ RULE_CATEGORY_END() #ifdef BOTS RULE_CATEGORY(Bots) -RULE_INT(Bots, AAExpansion, 8, "Bots get AAs through this expansion") +RULE_INT(Bots, BotExpansionSettings, 16383, "Sets the expansion settings for bot use. Defaults to all expansions enabled up to TSS") RULE_BOOL(Bots, AllowCamelCaseNames, false, "Allows the use of 'MyBot' type names") RULE_INT(Bots, CommandSpellRank, 1, "Filters bot command spells by rank (1, 2 and 3 are valid filters - any other number allows all ranks)") RULE_INT(Bots, CreationLimit, 150, "Number of bots that each account can create") diff --git a/zone/aa.cpp b/zone/aa.cpp index 09a954331..1383ad2f9 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1467,12 +1467,20 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) { } } - if(IsClient()) { - if(rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) { + if (IsClient()) { + if (rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) { return false; } - } else { - if(rank->expansion && !(RuleI(World, ExpansionSettings) & (1 << (rank->expansion - 1)))) { + } +#ifdef BOTS + else if (IsBot()) { + if (rank->expansion && !(RuleI(Bots, BotExpansionSettings) & (1 << (rank->expansion - 1)))) { + return false; + } + } +#endif + else { + if (rank->expansion && !(RuleI(World, ExpansionSettings) & (1 << (rank->expansion - 1)))) { return false; } } diff --git a/zone/bot.cpp b/zone/bot.cpp index 50620cdd5..5f16cc15b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1606,7 +1606,7 @@ int32 Bot::GenerateBaseHitPoints() { } void Bot::LoadAAs() { - int maxAAExpansion = RuleI(Bots, AAExpansion); //get expansion to get AAs up to + aa_ranks.clear(); int id = 0; @@ -8852,15 +8852,17 @@ void Bot::CalcBotStats(bool showtext) { GetBotOwner()->Message(Chat::Yellow, "Updating %s...", GetCleanName()); } - if(!IsValidRaceClassCombo()) { + // this code is annoying since many classes change their name and illusions change the race id + /*if(!IsValidRaceClassCombo()) { GetBotOwner()->Message(Chat::Yellow, "A %s - %s bot was detected. Is this Race/Class combination allowed?.", GetRaceIDName(GetRace()), GetClassIDName(GetClass(), GetLevel())); GetBotOwner()->Message(Chat::Yellow, "Previous Bots Code releases did not check Race/Class combinations during create."); GetBotOwner()->Message(Chat::Yellow, "Unless you are experiencing heavy lag, you should delete and remake this bot."); - } + }*/ if(GetBotOwner()->GetLevel() != GetLevel()) SetLevel(GetBotOwner()->GetLevel()); + LoadAAs(); GenerateSpecialAttacks(); if(showtext) { From e306e9ad0c39292e081871da89d40892ce49c6a8 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 8 Dec 2019 23:54:51 -0500 Subject: [PATCH 009/157] Added bot command option 'follow chain' to allow linear assignment of follow ids to eligible bots --- zone/bot_command.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++-- zone/bot_command.h | 1 + zone/mob.cpp | 4 ++ zone/mob.h | 9 ++++- 4 files changed, 98 insertions(+), 4 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 0f469a03c..971ce647d 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1371,7 +1371,7 @@ int bot_command_init(void) bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", 0, bot_command_depart) || bot_command_add("escape", "Orders a bot to send a target group to a safe location within the zone", 0, bot_command_escape) || bot_command_add("findaliases", "Find available aliases for a bot command", 0, bot_command_find_aliases) || - bot_command_add("follow", "Orders bots to follow a designated target", 0, bot_command_follow) || + bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", 0, bot_command_follow) || bot_command_add("guard", "Orders bots to guard their current positions", 0, bot_command_guard) || bot_command_add("healrotation", "Lists the available bot heal rotation [subcommands]", 0, bot_command_heal_rotation) || bot_command_add("healrotationadaptivetargeting", "Enables or disables adaptive targeting within the heal rotation instance", 0, bot_subcommand_heal_rotation_adaptive_targeting) || @@ -3213,6 +3213,7 @@ void bot_command_follow(Client *c, const Seperator *sep) return; if (helper_is_help_or_usage(sep->arg[1])) { c->Message(m_usage, "usage: () %s ([option: reset]) [actionable: byname | ownergroup | botgroup | namesgroup | healrotation | spawned] ([actionable_name])", sep->arg[0]); + c->Message(m_usage, "usage: %s chain", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_Type2; @@ -3222,8 +3223,15 @@ void bot_command_follow(Client *c, const Seperator *sep) int name_arg = 2; Mob* target_mob = nullptr; - std::string reset_arg = sep->arg[1]; - if (!reset_arg.compare("reset")) { + std::string optional_arg = sep->arg[1]; + if (!optional_arg.compare("chain")) { + + auto chain_count = helper_bot_follow_option_chain(c); + c->Message(m_action, "%i of your bot%s are now chain following", chain_count, (chain_count == 1 ? "" : "s")); + + return; + } + else if (!optional_arg.compare("reset")) { reset = true; ab_arg = 2; name_arg = 3; @@ -3250,16 +3258,21 @@ void bot_command_follow(Client *c, const Seperator *sep) bot_iter->SetFollowID(c->GetID()); else bot_iter->SetFollowID(my_group->GetLeader()->GetID()); + + bot_iter->SetManualFollow(false); } else { if (bot_iter == target_mob) bot_iter->SetFollowID(c->GetID()); else bot_iter->SetFollowID(target_mob->GetID()); + + bot_iter->SetManualFollow(true); } } else { bot_iter->SetFollowID(0); + bot_iter->SetManualFollow(false); } if (!bot_iter->GetPet()) continue; @@ -8622,6 +8635,75 @@ void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot) } } +int helper_bot_follow_option_chain(Client* bot_owner) +{ + if (!bot_owner) { + return 0; + } + + std::list sbl; + MyBots::PopulateSBL_BySpawnedBots(bot_owner, sbl); + if (sbl.empty()) { + return 0; + } + + int chain_follow_count = 0; + Mob* followee = bot_owner; + + // only add groups that do not belong to bot_owner + std::map bot_group_map; + for (auto bot_iter : sbl) { + + if (!bot_iter || bot_iter->GetManualFollow() || bot_iter->GetGroup() == bot_owner->GetGroup()) { + continue; + } + + Group* bot_group = bot_iter->GetGroup(); + if (!bot_iter->GetGroup()) { + continue; + } + + bot_group_map[bot_group->GetID()] = bot_group; + } + + std::list bot_member_list; + if (bot_owner->GetGroup()) { + + bot_owner->GetGroup()->GetBotList(bot_member_list); + for (auto bot_member_iter : bot_member_list) { + + if (!bot_member_iter || bot_member_iter->GetBotOwnerCharacterID() != bot_owner->CharacterID() || bot_member_iter == followee || bot_member_iter->GetManualFollow()) { + continue; + } + + bot_member_iter->SetFollowID(followee->GetID()); + followee = bot_member_iter; + ++chain_follow_count; + } + } + + for (auto bot_group_iter : bot_group_map) { + + if (!bot_group_iter.second) { + continue; + } + + bot_group_iter.second->GetBotList(bot_member_list); + for (auto bot_member_iter : bot_member_list) { + + if (!bot_member_iter || bot_member_iter->GetBotOwnerCharacterID() != bot_owner->CharacterID() || bot_member_iter == followee || bot_member_iter->GetManualFollow()) { + continue; + } + + bot_member_iter->SetFollowID(followee->GetID()); + followee = bot_member_iter; + ++chain_follow_count; + } + } + + return chain_follow_count; +} + bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast, uint32* dont_root_before) { if (!casting_bot || !target_mob) diff --git a/zone/bot_command.h b/zone/bot_command.h index b3f328a33..b7377d28b 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -672,6 +672,7 @@ void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot); void helper_bot_appearance_form_update(Bot *my_bot); uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_class, uint16 bot_race, uint8 bot_gender); void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot); +int helper_bot_follow_option_chain(Client *bot_owner); bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast = true, uint32* dont_root_before = nullptr); bool helper_command_disabled(Client *bot_owner, bool rule_value, const char *command); bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command); diff --git a/zone/mob.cpp b/zone/mob.cpp index b12df39c1..44ce45d93 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -453,6 +453,10 @@ Mob::Mob( PrimaryAggro = false; AssistAggro = false; npc_assist_cap = 0; + +#ifdef BOTS + m_manual_follow = false; +#endif } Mob::~Mob() diff --git a/zone/mob.h b/zone/mob.h index 2578db0c8..ab3df4277 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1188,8 +1188,8 @@ public: int32 GetManaRegen() const; - // Bots HealRotation methods #ifdef BOTS + // Bots HealRotation methods bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); } bool JoinHealRotationTargetPool(std::shared_ptr* heal_rotation); bool LeaveHealRotationTargetPool(); @@ -1200,6 +1200,11 @@ public: float HealRotationExtendedHealFrequency(); const std::shared_ptr* TargetOfHealRotation() const { return &m_target_of_heal_rotation; } + + + // not Bots HealRotation methods + void SetManualFollow(bool flag) { m_manual_follow = flag; } + bool GetManualFollow() const { return m_manual_follow; } #endif protected: @@ -1581,6 +1586,8 @@ private: #ifdef BOTS std::shared_ptr m_target_of_heal_rotation; + + bool m_manual_follow; #endif }; From ab35c3ed90cb05499b75a33bd4f03e505bd7d7bf Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 9 Dec 2019 22:01:50 -0500 Subject: [PATCH 010/157] Tweaked a few bot things... --- zone/bot.cpp | 15 ++++++++------- zone/bot_command.cpp | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 5f16cc15b..32d66780a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2456,15 +2456,16 @@ void Bot::SetHoldMode() { } // AI Processing for the Bot object -void Bot::AI_Process() -{ - constexpr float MAX_CASTER_DISTANCE[PLAYER_CLASS_COUNT] = { - 0, (34 * 34), (24 * 24), (28 * 28), (26 * 26), (42 * 42), 0, 0, 0, (38 * 38), (54 * 54), (48 * 48), (52 * 52), (50 * 50), (30 * 30), 0 - // W C P R S D M B R S N W M E B B - // A L A N H R N R O H E I A N S E - // R R L G D U K D G M C Z G C T R + +constexpr float MAX_CASTER_DISTANCE[PLAYER_CLASS_COUNT] = { + 0, (34 * 34), (24 * 24), (28 * 28), (26 * 26), (42 * 42), 0, 0, 0, (38 * 38), (54 * 54), (48 * 48), (52 * 52), (50 * 50), (30 * 30), 0 +// W C P R S D M B R S N W M E B B +// A L A N H R N R O H E I A N S E +// R R L G D U K D G M C Z G C T R }; +void Bot::AI_Process() +{ #define TEST_COMBATANTS() if (!GetTarget() || GetAppearance() == eaDead) { return; } #define PULLING_BOT (GetPullingFlag() || GetReturningFlag()) #define NOT_PULLING_BOT (!GetPullingFlag() && !GetReturningFlag()) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 971ce647d..e4fc629b4 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -3227,7 +3227,7 @@ void bot_command_follow(Client *c, const Seperator *sep) if (!optional_arg.compare("chain")) { auto chain_count = helper_bot_follow_option_chain(c); - c->Message(m_action, "%i of your bot%s are now chain following", chain_count, (chain_count == 1 ? "" : "s")); + c->Message(m_action, "%i of your bots %s now chain following you", chain_count, (chain_count == 1 ? "is" : "are")); return; } From efd710855e93dbe993f67e41d980142a78710572 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 10 Dec 2019 02:02:42 -0500 Subject: [PATCH 011/157] Added bot command 'itemuse' --- zone/bot.h | 1 + zone/bot_command.cpp | 108 +++++++++++++++++++++++++++++++++++++++++++ zone/bot_command.h | 1 + 3 files changed, 110 insertions(+) diff --git a/zone/bot.h b/zone/bot.h index 63195e23b..3ee85d57f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -383,6 +383,7 @@ public: void EquipBot(std::string* errorMessage); bool CheckLoreConflict(const EQEmu::ItemData* item); 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]); } + const EQEmu::InventoryProfile& GetBotInv() const { return m_inv; } // Static Class Methods //static void DestroyBotRaidObjects(Client* client); // Can be removed after bot raids are dumped diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index e4fc629b4..07c2e6b06 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1403,6 +1403,7 @@ int bot_command_init(void) bot_command_add("inventoryremove", "Removes an item from a bot's inventory", 0, bot_subcommand_inventory_remove) || bot_command_add("inventorywindow", "Displays all items in a bot's inventory in a pop-up window", 0, bot_subcommand_inventory_window) || bot_command_add("invisibility", "Orders a bot to cast a cloak of invisibility, or allow them to be seen", 0, bot_command_invisibility) || + bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", 0, bot_command_item_use) || bot_command_add("levitation", "Orders a bot to cast a levitation spell", 0, bot_command_levitation) || bot_command_add("lull", "Orders a bot to cast a pacification spell", 0, bot_command_lull) || bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", 0, bot_command_mesmerize) || @@ -3578,6 +3579,113 @@ void bot_command_invisibility(Client *c, const Seperator *sep) helper_no_available_bots(c, my_bot); } +void bot_command_item_use(Client* c, const Seperator* sep) +{ + if (helper_is_help_or_usage(sep->arg[1])) { + + c->Message(m_usage, "usage: %s ([empty])", sep->arg[0]); + return; + } + + bool empty_only = false; + std::string arg1 = sep->arg[1]; + if (arg1.compare("empty") == 0) { + empty_only = true; + } + + const auto item_instance = c->GetInv().GetItem(EQEmu::invslot::slotCursor); + if (!item_instance) { + + c->Message(m_fail, "No item found on cursor!"); + return; + } + + auto item_data = item_instance->GetItem(); + if (!item_data) { + + c->Message(m_fail, "No data found for cursor item!"); + return; + } + + if (item_data->ItemClass != EQEmu::item::ItemClassCommon || item_data->Slots == 0) { + + c->Message(m_fail, "'%s' is not an equipable item!", item_data->Name); + return; + } + + std::list equipable_slot_list; + for (int16 equipable_slot = EQEmu::invslot::EQUIPMENT_BEGIN; equipable_slot <= EQEmu::invslot::EQUIPMENT_END; ++equipable_slot) { + if (item_data->Slots & (1 << equipable_slot)) { + equipable_slot_list.push_back(equipable_slot); + } + } + + std::string msg; + std::string text_link; + + EQEmu::SayLinkEngine linker; + linker.SetLinkType(EQEmu::saylink::SayLinkItemInst); + + std::list sbl; + MyBots::PopulateSBL_BySpawnedBots(c, sbl); + + for (auto bot_iter : sbl) { + + if (!bot_iter) { + continue; + } + + if (((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace())) || ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass()))) { + continue; + } + + msg = StringFormat("%cinventorygive byname %s", BOT_COMMAND_CHAR, bot_iter->GetCleanName()); + text_link = bot_iter->CreateSayLink(c, msg.c_str(), bot_iter->GetCleanName()); + + for (auto slot_iter : equipable_slot_list) { + + auto equipped_item = bot_iter->GetBotInv()[slot_iter]; + if (empty_only) { + if (!equipped_item) { + + c->Message( + Chat::Say, + "[%s] says, 'I can use that for my %s!", + text_link.c_str(), + EQEmu::invslot::GetInvPossessionsSlotName(slot_iter) + ); + bot_iter->DoAnim(29); + } + } + else { + if (equipped_item) { + + linker.SetItemInst(equipped_item); + + c->Message( + Chat::Say, + "[%s] says, 'I can use that for my %s! (replaces: [%s])", + text_link.c_str(), + EQEmu::invslot::GetInvPossessionsSlotName(slot_iter), + linker.GenerateLink().c_str() + ); + bot_iter->DoAnim(29); + } + else { + + c->Message( + Chat::Say, + "[%s] says, 'I can use that for my %s!", + text_link.c_str(), + EQEmu::invslot::GetInvPossessionsSlotName(slot_iter) + ); + bot_iter->DoAnim(29); + } + } + } + } +} + void bot_command_levitation(Client *c, const Seperator *sep) { bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Levitation]; diff --git a/zone/bot_command.h b/zone/bot_command.h index b7377d28b..84a56f239 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -573,6 +573,7 @@ void bot_command_hold(Client *c, const Seperator *sep); void bot_command_identify(Client *c, const Seperator *sep); void bot_command_inventory(Client *c, const Seperator *sep); void bot_command_invisibility(Client *c, const Seperator *sep); +void bot_command_item_use(Client *c, const Seperator *sep); void bot_command_levitation(Client *c, const Seperator *sep); void bot_command_lull(Client *c, const Seperator *sep); void bot_command_mesmerize(Client *c, const Seperator *sep); From 1196abfda89875a6a0642d6c42939deaa0b8ce05 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 10 Dec 2019 20:19:43 -0500 Subject: [PATCH 012/157] Fix for bot classes having skills they shouldn't. Please report any abnormalities. Tweaked bot command 'itemuse' to exclude invalid dual-wield reporting --- zone/bot.cpp | 4 +++ zone/bot_command.cpp | 60 +++++++++++++++++++------------------------- zone/npc.cpp | 24 ++++++++++-------- 3 files changed, 43 insertions(+), 45 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 32d66780a..f80f91c6f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -8863,6 +8863,10 @@ void Bot::CalcBotStats(bool showtext) { if(GetBotOwner()->GetLevel() != GetLevel()) SetLevel(GetBotOwner()->GetLevel()); + for (int sindex = 0; sindex <= EQEmu::skills::HIGHEST_SKILL; ++sindex) { + skills[sindex] = database.GetSkillCap(GetClass(), (EQEmu::skills::SkillType)sindex, GetLevel()); + } + LoadAAs(); GenerateSpecialAttacks(); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 07c2e6b06..c60aa7810 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -3644,43 +3644,35 @@ void bot_command_item_use(Client* c, const Seperator* sep) for (auto slot_iter : equipable_slot_list) { - auto equipped_item = bot_iter->GetBotInv()[slot_iter]; - if (empty_only) { - if (!equipped_item) { - - c->Message( - Chat::Say, - "[%s] says, 'I can use that for my %s!", - text_link.c_str(), - EQEmu::invslot::GetInvPossessionsSlotName(slot_iter) - ); - bot_iter->DoAnim(29); - } + // needs more failure criteria - this should cover the bulk for now + if (slot_iter == EQEmu::invslot::slotSecondary && item_data->Damage && !bot_iter->CanThisClassDualWield()) { + continue; } - else { - if (equipped_item) { - linker.SetItemInst(equipped_item); - - c->Message( - Chat::Say, - "[%s] says, 'I can use that for my %s! (replaces: [%s])", - text_link.c_str(), - EQEmu::invslot::GetInvPossessionsSlotName(slot_iter), - linker.GenerateLink().c_str() - ); - bot_iter->DoAnim(29); - } - else { + auto equipped_item = bot_iter->GetBotInv()[slot_iter]; - c->Message( - Chat::Say, - "[%s] says, 'I can use that for my %s!", - text_link.c_str(), - EQEmu::invslot::GetInvPossessionsSlotName(slot_iter) - ); - bot_iter->DoAnim(29); - } + if (equipped_item && !empty_only) { + + linker.SetItemInst(equipped_item); + + c->Message( + Chat::Say, + "[%s] says, 'I can use that for my %s! (replaces: [%s])'", + text_link.c_str(), + EQEmu::invslot::GetInvPossessionsSlotName(slot_iter), + linker.GenerateLink().c_str() + ); + bot_iter->DoAnim(29); + } + else if (!equipped_item) { + + c->Message( + Chat::Say, + "[%s] says, 'I can use that for my %s!'", + text_link.c_str(), + EQEmu::invslot::GetInvPossessionsSlotName(slot_iter) + ); + bot_iter->DoAnim(29); } } } diff --git a/zone/npc.cpp b/zone/npc.cpp index 46c2e54a7..efbe98be1 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -307,16 +307,18 @@ 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 (moblevel > 50) { - skills[EQEmu::skills::SkillDoubleAttack] = 250; - skills[EQEmu::skills::SkillDualWield] = 250; - } - else if (moblevel > 3) { - skills[EQEmu::skills::SkillDoubleAttack] = moblevel * 5; - skills[EQEmu::skills::SkillDualWield] = skills[EQEmu::skills::SkillDoubleAttack]; - } - else { - skills[EQEmu::skills::SkillDoubleAttack] = moblevel * 5; + if (!IsBot()) { + if (moblevel > 50) { + skills[EQEmu::skills::SkillDoubleAttack] = 250; + skills[EQEmu::skills::SkillDualWield] = 250; + } + else if (moblevel > 3) { + skills[EQEmu::skills::SkillDoubleAttack] = moblevel * 5; + skills[EQEmu::skills::SkillDualWield] = skills[EQEmu::skills::SkillDoubleAttack]; + } + else { + skills[EQEmu::skills::SkillDoubleAttack] = moblevel * 5; + } } ldon_trapped = false; @@ -2983,4 +2985,4 @@ void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay) GetY() - box_size, move_delay ); -} \ No newline at end of file +} From 4e6018e3e889a6579ef4f793392e9c8202472e6f Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 10 Dec 2019 22:06:51 -0500 Subject: [PATCH 013/157] Fix for pets breaking mez after initial aggro --- zone/mob_ai.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 4546b5c45..5958fe18a 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1138,6 +1138,17 @@ void Mob::AI_Process() { return; } + if (target->IsMezzed() && IsPet()) { + + auto pet_owner = GetOwner(); + if (pet_owner && pet_owner->IsClient()) { + pet_owner->MessageString(Chat::NPCQuestSay, CANNOT_WAKE, GetCleanName(), target->GetCleanName()); + } + + RemoveFromHateList(target); + return; + } + #ifdef BOTS if (IsPet() && GetOwner() && GetOwner()->IsBot() && target == GetOwner()) { From ed67b461eaaf4552d287fb26d12b7d660e067db1 Mon Sep 17 00:00:00 2001 From: Uleat Date: Thu, 12 Dec 2019 20:38:28 -0500 Subject: [PATCH 014/157] Added 'skip mez' ability to certain entity functions --- zone/hate_list.cpp | 71 ++++++++++++++++++++++++++++++++++++---------- zone/hate_list.h | 10 +++---- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 478c9ac50..6b26cba8a 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -155,13 +155,18 @@ Mob* HateList::GetDamageTopOnHateList(Mob* hater) return current; } -Mob* HateList::GetClosestEntOnHateList(Mob *hater) { +Mob* HateList::GetClosestEntOnHateList(Mob *hater, bool skip_mezzed) { Mob* close_entity = nullptr; float close_distance = 99999.9f; float this_distance; auto iterator = list.begin(); while (iterator != list.end()) { + if (skip_mezzed && (*iterator)->entity_on_hatelist->IsMezzed()) { + ++iterator; + continue; + } + this_distance = DistanceSquaredNoZ((*iterator)->entity_on_hatelist->GetPosition(), hater->GetPosition()); if ((*iterator)->entity_on_hatelist != nullptr && this_distance <= close_distance) { close_distance = this_distance; @@ -297,7 +302,7 @@ int HateList::GetHateRatio(Mob *top, Mob *other) // skip is used to ignore a certain mob on the list // Currently used for getting 2nd on list for aggro meter -Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip) +Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip, bool skip_mezzed) { // hack fix for zone shutdown crashes on some servers if (!zone->IsLoaded()) @@ -335,6 +340,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip) continue; } + if (skip_mezzed && cur->entity_on_hatelist->IsMezzed()) { + ++iterator; + continue; + } + if (cur->entity_on_hatelist->Sanctuary()) { if (hate == -1) { @@ -465,6 +475,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip) continue; } + if (skip_mezzed && cur->entity_on_hatelist->IsMezzed()) { + ++iterator; + continue; + } + if (cur->entity_on_hatelist != nullptr && ((cur->stored_hate_amount > hate) || cur->is_entity_frenzy)) { top_hate = cur->entity_on_hatelist; @@ -480,7 +495,7 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip) return nullptr; } -Mob *HateList::GetEntWithMostHateOnList(){ +Mob *HateList::GetEntWithMostHateOnList(bool skip_mezzed){ Mob* top = nullptr; int64 hate = -1; @@ -490,8 +505,10 @@ Mob *HateList::GetEntWithMostHateOnList(){ struct_HateList *cur = (*iterator); if (cur && cur->entity_on_hatelist != nullptr && (cur->stored_hate_amount > hate)) { - top = cur->entity_on_hatelist; - hate = cur->stored_hate_amount; + if (!skip_mezzed || !cur->entity_on_hatelist->IsMezzed()) { + top = cur->entity_on_hatelist; + hate = cur->stored_hate_amount; + } } ++iterator; } @@ -499,26 +516,50 @@ Mob *HateList::GetEntWithMostHateOnList(){ } -Mob *HateList::GetRandomEntOnHateList() +Mob *HateList::GetRandomEntOnHateList(bool skip_mezzed) { int count = list.size(); - if (count == 0) //If we don't have any entries it'll crash getting a random 0, -1 position. - return NULL; + if (count <= 0) //If we don't have any entries it'll crash getting a random 0, -1 position. + return nullptr; if (count == 1) //No need to do all that extra work if we only have one hate entry { - if (*list.begin()) // Just in case tHateEntry is invalidated somehow... + if (*list.begin() && (!skip_mezzed || !(*list.begin())->entity_on_hatelist->IsMezzed())) // Just in case tHateEntry is invalidated somehow... return (*list.begin())->entity_on_hatelist; - return NULL; + return nullptr; } - auto iterator = list.begin(); - int random = zone->random.Int(0, count - 1); - for (int i = 0; i < random; i++) - ++iterator; + if (skip_mezzed) { - return (*iterator)->entity_on_hatelist; + for (auto iter : list) { + if (iter->entity_on_hatelist->IsMezzed()) { + --count; + } + } + if (count <= 0) { + return nullptr; + } + } + + int random = zone->random.Int(0, count - 1); + int counter = 0; + + for (auto iter : list) { + + if (skip_mezzed && iter->entity_on_hatelist->IsMezzed()) { + continue; + } + if (counter < random) { + + ++counter; + continue; + } + + return iter->entity_on_hatelist; + } + + return nullptr; } Mob *HateList::GetEscapingEntOnHateList() { diff --git a/zone/hate_list.h b/zone/hate_list.h index 96d8ed067..44613bdea 100644 --- a/zone/hate_list.h +++ b/zone/hate_list.h @@ -41,11 +41,11 @@ public: HateList(); ~HateList(); - Mob *GetClosestEntOnHateList(Mob *hater); - Mob *GetDamageTopOnHateList(Mob *hater); - Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr); - Mob *GetRandomEntOnHateList(); - Mob *GetEntWithMostHateOnList(); + Mob *GetClosestEntOnHateList(Mob *hater, bool skip_mezzed = false); + Mob *GetDamageTopOnHateList(Mob *hater); // didn't add 'skip_mezzed' due to calls being in ::Death() + Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr, bool skip_mezzed = false); + Mob *GetRandomEntOnHateList(bool skip_mezzed = false); + Mob *GetEntWithMostHateOnList(bool skip_mezzed = false); Mob *GetEscapingEntOnHateList(); // returns first eligble entity Mob *GetEscapingEntOnHateList(Mob *center, float range = 0.0f, bool first = false); From bd6e06aadb84623d1207900dee82549c965405dd Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 13 Dec 2019 16:50:42 -0500 Subject: [PATCH 015/157] Few tweaks to bot ai --- zone/bot.cpp | 22 ++++++++++++++++------ zone/bot.h | 1 + zone/bot_command.cpp | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index f80f91c6f..4322f150f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -83,6 +83,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm SetPauseAI(false); m_alt_combat_hate_timer.Start(250); + m_auto_defend_timer.Disable(); //m_combat_jitter_timer.Disable(); //SetCombatJitterFlag(false); SetGuardFlag(false); @@ -180,6 +181,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to SetPauseAI(false); m_alt_combat_hate_timer.Start(250); + m_auto_defend_timer.Disable(); //m_combat_jitter_timer.Disable(); //SetCombatJitterFlag(false); SetGuardFlag(false); @@ -2462,7 +2464,7 @@ constexpr float MAX_CASTER_DISTANCE[PLAYER_CLASS_COUNT] = { // W C P R S D M B R S N W M E B B // A L A N H R N R O H E I A N S E // R R L G D U K D G M C Z G C T R - }; +}; void Bot::AI_Process() { @@ -2861,14 +2863,14 @@ void Bot::AI_Process() if (find_target) { if (IsRooted()) { - SetTarget(hate_list.GetClosestEntOnHateList(this)); + SetTarget(hate_list.GetClosestEntOnHateList(this, true)); } else { // This will keep bots on target for now..but, future updates will allow for rooting/stunning SetTarget(hate_list.GetEscapingEntOnHateList(leash_owner, leash_distance)); if (!GetTarget()) { - SetTarget(hate_list.GetEntWithMostHateOnList(this)); + SetTarget(hate_list.GetEntWithMostHateOnList(this, nullptr, true)); } } } @@ -3550,9 +3552,15 @@ void Bot::AI_Process() // This is as close as I could get without modifying the aggro mechanics and making it an expensive process... // 'class Client' doesn't make use of hate_list... - if (bot_owner->GetAggroCount() && bot_owner->GetBotOption(Client::booAutoDefend)) { + if (RuleB(Bots, AllowOwnerOptionAutoDefend) && bot_owner->GetBotOption(Client::booAutoDefend)) { - if (RuleB(Bots, AllowOwnerOptionAutoDefend)) { + if (!m_auto_defend_timer.Enabled()) { + + m_auto_defend_timer.Start(zone->random.Int(250, 1250)); // random timer to simulate 'awareness' (cuts down on scanning overhead) + return; + } + + if (m_auto_defend_timer.Check() && bot_owner->GetAggroCount()) { if (NOT_HOLDING && NOT_PASSIVE) { @@ -3570,7 +3578,7 @@ void Bot::AI_Process() } auto hater = entity_list.GetMob(hater_iter.spawn_id); - if (hater && DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) { + if (hater && !hater->IsMezzed() && DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) { // This is roughly equivilent to npc attacking a client pet owner AddToHateList(hater, 1); @@ -3582,6 +3590,8 @@ void Bot::AI_Process() GetPet()->SetTarget(hater); } + m_auto_defend_timer.Disable(); + return; } } diff --git a/zone/bot.h b/zone/bot.h index 3ee85d57f..97bb6892c 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -693,6 +693,7 @@ private: Timer m_evade_timer; // can be moved to pTimers at some point Timer m_alt_combat_hate_timer; + Timer m_auto_defend_timer; //Timer m_combat_jitter_timer; //bool m_combat_jitter_flag; bool m_guard_flag; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index c60aa7810..baa4818ac 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -5758,7 +5758,7 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) } Bot * botCheckNotOnline = entity_list.GetBotByBotName(bots_iter.Name); std::string botspawn_saylink = StringFormat("^botspawn %s", bots_iter.Name); - c->Message(Chat::White, "%s is a level %u %s %s %s who is owned by %s", + c->Message(Chat::White, "[%s] is a level %u %s %s %s who is owned by %s", ((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQEmu::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)), bots_iter.Level, Bot::RaceIdToString(bots_iter.Race).c_str(), From 0cb425b6ddc68051821dea1f650b0c4d860aac82 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 13 Dec 2019 20:59:10 -0500 Subject: [PATCH 016/157] Tweaked bot title and suffix code a little --- zone/bot.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++-- zone/bot_command.cpp | 19 +++-------------- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 4322f150f..42adfddac 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -433,15 +433,61 @@ void Bot::SetBotSpellID(uint32 newSpellID) { } void Bot::SetSurname(std::string bot_surname) { + _surname = bot_surname.substr(0, 31); + + if (spawned) { + + auto outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct)); + GMLastName_Struct* gmn = (GMLastName_Struct*)outapp->pBuffer; + + strcpy(gmn->name, GetCleanName()); + strcpy(gmn->gmname, GetCleanName()); + strcpy(gmn->lastname, GetSurname().c_str()); + gmn->unknown[0] = 1; + gmn->unknown[1] = 1; + gmn->unknown[2] = 1; + gmn->unknown[3] = 1; + + entity_list.QueueClients(this, outapp); + safe_delete(outapp); + } } void Bot::SetTitle(std::string bot_title) { - _title = bot_title.substr(0, 31); + + _title = bot_title.substr(0, 31); + + if (spawned) { + + auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); + SetTitleReply_Struct* strs = (SetTitleReply_Struct*)outapp->pBuffer; + + strs->is_suffix = 0; + strn0cpy(strs->title, _title.c_str(), sizeof(strs->title)); + strs->entity_id = GetID(); + + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + } } void Bot::SetSuffix(std::string bot_suffix) { - _suffix = bot_suffix.substr(0, 31); + + _suffix = bot_suffix.substr(0, 31); + + if (spawned) { + + auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); + SetTitleReply_Struct* strs = (SetTitleReply_Struct*)outapp->pBuffer; + + strs->is_suffix = 1; + strn0cpy(strs->title, _suffix.c_str(), sizeof(strs->title)); + strs->entity_id = GetID(); + + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + } } uint32 Bot::GetBotArcheryRange() { diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index baa4818ac..1a0c415db 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -5839,23 +5839,12 @@ void bot_subcommand_bot_surname(Client *c, const Seperator *sep) std::string bot_surname = sep->arg[1]; bot_surname = (bot_surname == "-remove") ? "" : bot_surname; std::replace(bot_surname.begin(), bot_surname.end(), '_', ' '); + my_bot->SetSurname(bot_surname); if (!database.botdb.SaveBot(my_bot)) { c->Message(Chat::Red, BotDatabase::fail::SaveBot()); - return; } else { - auto outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct)); - GMLastName_Struct * gmn = (GMLastName_Struct*)outapp->pBuffer; - strcpy(gmn->name, my_bot->GetCleanName()); - strcpy(gmn->gmname, my_bot->GetCleanName()); - strcpy(gmn->lastname, my_bot->GetSurname().c_str()); - gmn->unknown[0] = 1; - gmn->unknown[1] = 1; - gmn->unknown[2] = 1; - gmn->unknown[3] = 1; - entity_list.QueueClients(my_bot->CastToClient(), outapp); - safe_delete(outapp); c->Message(Chat::Yellow, "Bot Surname Saved."); } } @@ -5878,13 +5867,12 @@ void bot_subcommand_bot_title(Client *c, const Seperator *sep) std::string bot_title = sep->arg[1]; bot_title = (bot_title == "-remove") ? "" : bot_title; std::replace(bot_title.begin(), bot_title.end(), '_', ' '); + my_bot->SetTitle(bot_title); if (!database.botdb.SaveBot(my_bot)) { c->Message(Chat::Red, BotDatabase::fail::SaveBot()); - return; } else { - my_bot->CastToClient()->SetAATitle(my_bot->GetTitle().c_str()); c->Message(Chat::Yellow, "Bot Title Saved."); } } @@ -5907,13 +5895,12 @@ void bot_subcommand_bot_suffix(Client *c, const Seperator *sep) std::string bot_suffix = sep->arg[1]; bot_suffix = (bot_suffix == "-remove") ? "" : bot_suffix; std::replace(bot_suffix.begin(), bot_suffix.end(), '_', ' '); + my_bot->SetSuffix(bot_suffix); if (!database.botdb.SaveBot(my_bot)) { c->Message(Chat::Red, BotDatabase::fail::SaveBot()); - return; } else { - my_bot->CastToClient()->SetTitleSuffix(my_bot->GetSuffix().c_str()); c->Message(Chat::Yellow, "Bot Suffix Saved."); } } From 2c8b51fcda17ca2d2063a5310da1683f86906ff7 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 15 Dec 2019 23:40:20 -0500 Subject: [PATCH 017/157] Delete 'staged_updates' after updating quests [skip ci] --- utils/scripts/eqemu_server.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 95d7b4ad7..ec3505c43 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -1821,6 +1821,8 @@ sub quest_files_fetch { if ($fc == 0) { print "[Update] No Quest Updates found... \n\n"; } + + rmtree("updates_staged/"); } sub lua_modules_fetch { From e0505343787245345bb7fefd9ecdbe630c3b37ae Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 17 Dec 2019 21:02:36 -0500 Subject: [PATCH 018/157] Added BARD to available class Bot stop_melee_level criteria --- common/classes.cpp | 14 ++++++++++++++ common/classes.h | 1 + utils/scripts/eqemu_server.pl | 2 +- zone/bot.cpp | 12 ++++++------ zone/bot_command.cpp | 6 +++--- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/common/classes.cpp b/common/classes.cpp index be683fbd1..3aaeeded3 100644 --- a/common/classes.cpp +++ b/common/classes.cpp @@ -573,6 +573,20 @@ bool IsNonSpellFighterClass(uint8 class_id) } } +bool IsHybridClass(uint8 class_id) +{ + switch (class_id) { + case PALADIN: + case RANGER: + case SHADOWKNIGHT: + case BARD: + case BEASTLORD: + return true; + default: + return false; + } +} + bool IsCasterClass(uint8 class_id) { switch (class_id) { diff --git a/common/classes.h b/common/classes.h index 2ca9a3c4d..f63758937 100644 --- a/common/classes.h +++ b/common/classes.h @@ -135,6 +135,7 @@ uint8 GetClassIDFromPlayerClassBit(uint32 player_class_bit); bool IsFighterClass(uint8 class_id); bool IsSpellFighterClass(uint8 class_id); bool IsNonSpellFighterClass(uint8 class_id); +bool IsHybridClass(uint8 class_id); bool IsCasterClass(uint8 class_id); bool IsINTCasterClass(uint8 class_id); bool IsWISCasterClass(uint8 class_id); diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index ec3505c43..e06408bd9 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -1822,7 +1822,7 @@ sub quest_files_fetch { print "[Update] No Quest Updates found... \n\n"; } - rmtree("updates_staged/"); + rmtree("updates_staged/"); } sub lua_modules_fetch { diff --git a/zone/bot.cpp b/zone/bot.cpp index 42adfddac..c695d7169 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2481,7 +2481,7 @@ void Bot::SetTarget(Mob* mob) { } void Bot::SetStopMeleeLevel(uint8 level) { - if (IsCasterClass(GetClass()) || IsSpellFighterClass(GetClass())) + if (IsCasterClass(GetClass()) || IsHybridClass(GetClass())) _stopMeleeLevel = level; else _stopMeleeLevel = 255; @@ -2506,10 +2506,10 @@ void Bot::SetHoldMode() { // AI Processing for the Bot object constexpr float MAX_CASTER_DISTANCE[PLAYER_CLASS_COUNT] = { - 0, (34 * 34), (24 * 24), (28 * 28), (26 * 26), (42 * 42), 0, 0, 0, (38 * 38), (54 * 54), (48 * 48), (52 * 52), (50 * 50), (30 * 30), 0 -// W C P R S D M B R S N W M E B B -// A L A N H R N R O H E I A N S E -// R R L G D U K D G M C Z G C T R + 0, (34 * 34), (24 * 24), (28 * 28), (26 * 26), (42 * 42), 0, (30 * 30), 0, (38 * 38), (54 * 54), (48 * 48), (52 * 52), (50 * 50), (32 * 32), 0 +// W C P R S D M B R S N W M E B B +// A L A N H R N R O H E I A N S E +// R R L G D U K D G M C Z G C T R }; void Bot::AI_Process() @@ -3390,7 +3390,7 @@ void Bot::AI_Process() BotRangedAttack(tar); } } - else if (!IsBotArcher() && (IsBotNonSpellFighter() || GetLevel() < GetStopMeleeLevel())) { + else if (!IsBotArcher() && GetLevel() < GetStopMeleeLevel()) { // We can't fight if we don't have a target, are stun/mezzed or dead.. // Stop attacking if the target is enraged diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 1a0c415db..51fa44ced 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -6145,7 +6145,7 @@ void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep) return; if (helper_is_help_or_usage(sep->arg[1])) { c->Message(m_usage, "usage: %s [current | reset | sync | value: 0-255]", sep->arg[0]); - c->Message(m_note, "note: Only caster and spell-casting fighter class bots may be modified"); + c->Message(m_note, "note: Only caster or hybrid class bots may be modified"); c->Message(m_note, "note: Use [reset] to set stop melee level to server rule"); c->Message(m_note, "note: Use [sync] to set stop melee level to current bot level"); return; @@ -6156,8 +6156,8 @@ void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep) c->Message(m_fail, "You must a bot that you own to use this command"); return; } - if (!IsCasterClass(my_bot->GetClass()) && !IsSpellFighterClass(my_bot->GetClass())) { - c->Message(m_fail, "You must a caster or spell-casting fighter class bot to use this command"); + if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) { + c->Message(m_fail, "You must a caster or hybrid class bot to use this command"); return; } From c87b4f2ad4fdd23e81e685548cfb1534e89bbc3e Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 22 Dec 2019 21:36:13 -0500 Subject: [PATCH 019/157] Added rule_bool 'Character:UseNoJunkFishing' --- common/ruletypes.h | 1 + zone/forage.cpp | 56 ++++++++++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 82eddca7f..5207b6cbf 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -157,6 +157,7 @@ RULE_BOOL(Character, OPClientUpdateVisualDebug, false, "Shows a pulse and forwar RULE_BOOL(Character, AllowCrossClassTrainers, false, "") 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_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/forage.cpp b/zone/forage.cpp index 3c69dce19..24c4641e4 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -309,40 +309,42 @@ void Client::GoFish() if(food_id == 0) { int index = zone->random.Int(0, MAX_COMMON_FISH_IDS-1); - food_id = common_fish_ids[index]; + food_id = (RuleB(Character, UseNoJunkFishing) ? 13019 : common_fish_ids[index]); } const EQEmu::ItemData* food_item = database.GetItem(food_id); + if (food_item) { - if (food_item->ItemType != EQEmu::item::ItemTypeFood) { - MessageString(Chat::Skills, FISHING_SUCCESS); - } - else { - MessageString(Chat::Skills, FISHING_SUCCESS_FISH_NAME, food_item->Name); - } - - EQEmu::ItemInstance* inst = database.CreateItem(food_item, 1); - if(inst != nullptr) { - if(CheckLoreConflict(inst->GetItem())) - { - MessageString(Chat::White, DUP_LORE); - safe_delete(inst); + if (food_item->ItemType != EQEmu::item::ItemTypeFood) { + MessageString(Chat::Skills, FISHING_SUCCESS); } - else - { - PushItemOnCursor(*inst); - SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketLimbo); - if(RuleB(TaskSystem, EnableTaskSystem)) - UpdateTasksForItem(ActivityFish, food_id); - - safe_delete(inst); - inst = m_inv.GetItem(EQEmu::invslot::slotCursor); + else { + MessageString(Chat::Skills, FISHING_SUCCESS_FISH_NAME, food_item->Name); } - if(inst) { - std::vector args; - args.push_back(inst); - parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args); + EQEmu::ItemInstance* inst = database.CreateItem(food_item, 1); + if (inst != nullptr) { + if (CheckLoreConflict(inst->GetItem())) + { + MessageString(Chat::White, DUP_LORE); + safe_delete(inst); + } + else + { + PushItemOnCursor(*inst); + SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketLimbo); + if (RuleB(TaskSystem, EnableTaskSystem)) + UpdateTasksForItem(ActivityFish, food_id); + + safe_delete(inst); + inst = m_inv.GetItem(EQEmu::invslot::slotCursor); + } + + if (inst) { + std::vector args; + args.push_back(inst); + parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args); + } } } } From 81acd797b4df988717b95f2e0c4e8727c94bcd13 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 00:38:50 -0600 Subject: [PATCH 020/157] Add initial world server CLI handler interface --- world/CMakeLists.txt | 2 + world/world_server_command_handler.cpp | 105 +++++++++++++++++++++++++ world/world_server_command_handler.h | 34 ++++++++ 3 files changed, 141 insertions(+) create mode 100644 world/world_server_command_handler.cpp create mode 100644 world/world_server_command_handler.h diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index c042a7364..e60b3d666 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -22,6 +22,7 @@ SET(world_sources wguild_mgr.cpp world_config.cpp world_console_connection.cpp + world_server_command_handler.cpp worlddb.cpp zonelist.cpp zoneserver.cpp @@ -51,6 +52,7 @@ SET(world_headers world_config.h world_console_connection.h world_tcp_connection.h + world_server_command_handler.h worlddb.h zonelist.h zoneserver.h diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp new file mode 100644 index 000000000..ed6a3e844 --- /dev/null +++ b/world/world_server_command_handler.cpp @@ -0,0 +1,105 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 "world_server_command_handler.h" +#include "../common/eqemu_logsys.h" +#include "../common/json/json.h" +#include "../common/version.h" + +namespace WorldserverCommandHandler { + + /** + * @param argc + * @param argv + */ + void CommandHandler(int argc, char **argv) + { + if (argc == 1) { return; } + + argh::parser cmd; + cmd.parse(argc, argv, argh::parser::PREFER_PARAM_FOR_UNREG_OPTION); + EQEmuCommand::DisplayDebug(cmd); + + /** + * Declare command mapping + */ + auto function_map = EQEmuCommand::function_map; + + /** + * Register commands + */ + function_map["test:hello-world"] = &WorldserverCommandHandler::HelloWorld; + function_map["database:version"] = &WorldserverCommandHandler::DatabaseVersion; + + EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void HelloWorld(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Test command"; + + if (cmd[{"-h", "--help"}]) { + return; + } + + std::vector arguments = {}; + std::vector options = { + "--hello", + "--write" + }; + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + LogInfo("hello world!"); + + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Shows database version"; + + if (cmd[{"-h", "--help"}]) { + return; + } + + Json::Value database_version; + + database_version["database_version"] = CURRENT_BINARY_DATABASE_VERSION; + database_version["bots_database_version"] = CURRENT_BINARY_BOTS_DATABASE_VERSION; + + std::stringstream payload; + payload << database_version; + + std::cout << payload.str() << std::endl; + } + +} \ No newline at end of file diff --git a/world/world_server_command_handler.h b/world/world_server_command_handler.h new file mode 100644 index 000000000..a9b14cc58 --- /dev/null +++ b/world/world_server_command_handler.h @@ -0,0 +1,34 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 "iostream" +#include "../common/cli/eqemu_command_handler.h" + +#ifndef EQEMU_WORLD_SERVER_COMMAND_HANDLER_H +#define EQEMU_WORLD_SERVER_COMMAND_HANDLER_H + +namespace WorldserverCommandHandler { + void CommandHandler(int argc, char **argv); + void HelloWorld(int argc, char **argv, argh::parser &cmd, std::string &description); + void DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description); +}; + + +#endif //EQEMU_WORLD_SERVER_COMMAND_HANDLER_H From bc4e09cea4a9ed4f7424eef630ac21b906a8528e Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 00:38:58 -0600 Subject: [PATCH 021/157] Code cleanup --- loginserver/encryption.cpp | 23 +++++++++++++++++++++++ loginserver/login_server.h | 1 + loginserver/main.cpp | 3 +++ 3 files changed, 27 insertions(+) diff --git a/loginserver/encryption.cpp b/loginserver/encryption.cpp index ed98eae5e..7080f1996 100644 --- a/loginserver/encryption.cpp +++ b/loginserver/encryption.cpp @@ -21,6 +21,10 @@ #endif +/** + * @param mode + * @return + */ std::string GetEncryptionByModeId(uint32 mode) { switch (mode) { @@ -57,6 +61,13 @@ std::string GetEncryptionByModeId(uint32 mode) } } +/** + * @param buffer_in + * @param buffer_in_sz + * @param buffer_out + * @param enc + * @return + */ const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buffer_out, bool enc) { #ifdef EQEMU_USE_MBEDTLS @@ -125,6 +136,10 @@ const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buff return buffer_out; } +/** + * @param msg + * @return + */ std::string eqcrypt_md5(const std::string &msg) { std::string ret; @@ -159,6 +174,10 @@ std::string eqcrypt_md5(const std::string &msg) return ret; } +/** + * @param msg + * @return + */ std::string eqcrypt_sha1(const std::string &msg) { std::string ret; @@ -193,6 +212,10 @@ std::string eqcrypt_sha1(const std::string &msg) return ret; } +/** + * @param msg + * @return + */ std::string eqcrypt_sha512(const std::string &msg) { std::string ret; diff --git a/loginserver/login_server.h b/loginserver/login_server.h index e71cc7da0..c130c41d2 100644 --- a/loginserver/login_server.h +++ b/loginserver/login_server.h @@ -38,6 +38,7 @@ struct LoginServer { public: + LoginServer() : db(nullptr), server_manager(nullptr) { } diff --git a/loginserver/main.cpp b/loginserver/main.cpp index 6941ad7e6..67b06387c 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -195,6 +195,9 @@ int main(int argc, char **argv) LoginserverWebserver::RegisterRoutes(api); } + /** + * Command handler + */ if (argc > 1) { LogSys.LoadLogSettingsDefaults(); LogSys.log_settings[Logs::Debug].log_to_console = static_cast(Logs::General); From 8ef6feac9f4b10d640cb353e1fa69b0c841c7734 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 00:39:09 -0600 Subject: [PATCH 022/157] Change JsonPP default indent to two spaces --- common/json/jsoncpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/json/jsoncpp.cpp b/common/json/jsoncpp.cpp index 00c48700d..759e68fa6 100644 --- a/common/json/jsoncpp.cpp +++ b/common/json/jsoncpp.cpp @@ -5277,7 +5277,7 @@ void StreamWriterBuilder::setDefaults(Json::Value* settings) { //! [StreamWriterBuilderDefaults] (*settings)["commentStyle"] = "All"; - (*settings)["indentation"] = "\t"; + (*settings)["indentation"] = " "; (*settings)["enableYAMLCompatibility"] = false; (*settings)["dropNullPlaceholders"] = false; (*settings)["useSpecialFloats"] = false; From 2ab0ce19a7e1253dc8199742fa096a690e7b16d7 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 00:39:39 -0600 Subject: [PATCH 023/157] Add console helpers to EQEmu::LogSys cleanup world main --- common/cli/eqemu_command_handler.cpp | 4 - common/eqemu_logsys.cpp | 22 +++ common/eqemu_logsys.h | 10 ++ world/net.cpp | 214 +++++++++++++-------------- 4 files changed, 135 insertions(+), 115 deletions(-) diff --git a/common/cli/eqemu_command_handler.cpp b/common/cli/eqemu_command_handler.cpp index ef46e0d9f..b19c92e9c 100644 --- a/common/cli/eqemu_command_handler.cpp +++ b/common/cli/eqemu_command_handler.cpp @@ -123,10 +123,6 @@ namespace EQEmuCommand { bool ran_command = false; for (auto &it: in_function_map) { if (it.first == argv[1]) { - std::cout << std::endl; - std::cout << "> " << termcolor::cyan << "Executing CLI Command" << termcolor::reset << std::endl; - std::cout << std::endl; - (it.second)(argc, argv, cmd, description); ran_command = true; } diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index c2d786d94..f5d58a5fd 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -579,3 +579,25 @@ void EQEmuLogSys::StartFileLogs(const std::string &log_name) ); } } + +/** + * Silence console logging + */ +void EQEmuLogSys::SilenceConsoleLogging() +{ + for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { + log_settings[log_index].log_to_console = 0; + log_settings[log_index].is_category_enabled = 0; + } +} + +/** + * Enables console logging + */ +void EQEmuLogSys::EnableConsoleLogging() +{ + for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { + log_settings[log_index].log_to_console = Logs::General; + log_settings[log_index].is_category_enabled = 1; + } +} diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 11645c6fa..d2d860101 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -279,6 +279,16 @@ public: */ void SetConsoleHandler(std::function f) { on_log_console_hook = f; } + /** + * Silence console logging + */ + void SilenceConsoleLogging(); + + /** + * Turn on all console logging + */ + void EnableConsoleLogging(); + private: /** diff --git a/world/net.cpp b/world/net.cpp index aeb9d8b4d..13f824583 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -86,6 +86,7 @@ union semun { #include "../common/net/servertalk_server.h" #include "../zone/data_bucket.h" +#include "world_server_command_handler.h" ClientList client_list; GroupLFPList LFPGroupList; @@ -113,14 +114,30 @@ inline void UpdateWindowTitle(std::string new_title) { #endif } -int main(int argc, char** argv) { - RegisterExecutablePlatform(ExePlatformWorld); - LogSys.LoadLogSettingsDefaults(); - set_exception_handler(); +void LoadDatabaseConnections() +{ + LogInfo( + "Connecting to MySQL [{}]@[{}]:[{}]", + Config->DatabaseUsername.c_str(), + Config->DatabaseHost.c_str(), + Config->DatabasePort + ); - /** - * Auto convert json config from xml - */ + if (!database.Connect( + Config->DatabaseHost.c_str(), + Config->DatabaseUsername.c_str(), + Config->DatabasePassword.c_str(), + Config->DatabaseDB.c_str(), + Config->DatabasePort + )) { + LogError("Cannot continue without a database connection"); + + std::exit(1); + } +} + +void CheckForXMLConfigUpgrade() +{ if (!std::ifstream("eqemu_config.json") && std::ifstream("eqemu_config.xml")) { CheckForServerScript(true); if(system("perl eqemu_server.pl convert_xml")); @@ -128,50 +145,19 @@ int main(int argc, char** argv) { else { CheckForServerScript(); } +} - /** - * Database version - */ - uint32 Database_Version = CURRENT_BINARY_DATABASE_VERSION; - uint32 Bots_Database_Version = CURRENT_BINARY_BOTS_DATABASE_VERSION; - if (argc >= 2) { - if (strcasecmp(argv[1], "db_version") == 0) { - std::cout << "Binary Database Version: " << Database_Version << " : " << Bots_Database_Version << std::endl; - return 0; - } - } - - // Load server configuration +void LoadServerConfig() +{ LogInfo("Loading server configuration"); if (!WorldConfig::LoadConfig()) { LogError("Loading server configuration failed"); - return 1; + std::exit(1); } +} - Config = WorldConfig::get(); - - LogInfo("CURRENT_VERSION: [{}]", CURRENT_VERSION); - - if (signal(SIGINT, CatchSignal) == SIG_ERR) { - LogError("Could not set signal handler"); - return 1; - } - - if (signal(SIGTERM, CatchSignal) == SIG_ERR) { - LogError("Could not set signal handler"); - return 1; - } - -#ifndef WIN32 - if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - LogError("Could not set signal handler"); - return 1; - } -#endif - - /** - * Add Loginserver - */ +void RegisterLoginservers() +{ if (Config->LoginCount == 0) { if (Config->LoginHost.length()) { loginserverlist.Add( @@ -201,23 +187,85 @@ int main(int argc, char** argv) { iterator.Advance(); } } +} - LogInfo("Connecting to MySQL [{}]@[{}]:[{}]", Config->DatabaseUsername.c_str(), Config->DatabaseHost.c_str(), Config->DatabasePort); - if (!database.Connect( - Config->DatabaseHost.c_str(), - Config->DatabaseUsername.c_str(), - Config->DatabasePassword.c_str(), - Config->DatabaseDB.c_str(), - Config->DatabasePort)) { - LogError("Cannot continue without a database connection"); +int main(int argc, char** argv) { + RegisterExecutablePlatform(ExePlatformWorld); + LogSys.LoadLogSettingsDefaults(); + set_exception_handler(); + + /** + * Database version + */ + uint32 database_version = CURRENT_BINARY_DATABASE_VERSION; + uint32 bots_database_version = CURRENT_BINARY_BOTS_DATABASE_VERSION; + if (argc >= 2) { + if (strcasecmp(argv[1], "db_version") == 0) { + std::cout << "Binary Database Version: " << database_version << " : " << bots_database_version << std::endl; + return 0; + } + } + + /** + * Command handler + */ + if (argc > 1) { + LogSys.SilenceConsoleLogging(); + + /** + * Get Config + */ + WorldConfig::LoadConfig(); + Config = WorldConfig::get(); + + /** + * Load database + */ + LoadDatabaseConnections(); + + LogSys.EnableConsoleLogging(); + + WorldserverCommandHandler::CommandHandler(argc, argv); + } + + CheckForXMLConfigUpgrade(); + LoadServerConfig(); + + Config = WorldConfig::get(); + + LogInfo("CURRENT_VERSION: [{}]", CURRENT_VERSION); + + if (signal(SIGINT, CatchSignal) == SIG_ERR) { + LogError("Could not set signal handler"); return 1; } + + if (signal(SIGTERM, CatchSignal) == SIG_ERR) { + LogError("Could not set signal handler"); + return 1; + } + +#ifndef WIN32 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + LogError("Could not set signal handler"); + return 1; + } +#endif + + RegisterLoginservers(); + LoadDatabaseConnections(); + guild_mgr.SetDatabase(&database); - /* Register Log System and Settings */ + /** + * Logging + */ database.LoadLogSettings(LogSys.log_settings); LogSys.StartFileLogs(); + /** + * Parse simple CLI passes + */ bool ignore_db = false; if (argc >= 2) { std::string tmp; @@ -233,38 +281,6 @@ int main(int argc, char** argv) { std::cout << "Reboot Zones mode ON" << std::endl; holdzones = true; } - else if (database.GetVariable("disablecommandline", tmp)) { - if (tmp.length() == 1) { - if (tmp[0] == '1') { - std::cerr << "Command line disabled in database... exiting" << std::endl; - return 1; - } - } - } - else if (strcasecmp(argv[1], "adduser") == 0) { - if (argc == 5) { - if (Seperator::IsNumber(argv[4])) { - if (atoi(argv[4]) >= 0 && atoi(argv[4]) <= 255) { - std::string user; - std::string loginserver; - - ParseAccountString(argv[2], user, loginserver); - - if (database.CreateAccount(argv[2], argv[3], atoi(argv[4]), loginserver.c_str(), 0) == 0) { - std::cerr << "database.CreateAccount failed." << std::endl; - return 1; - } - else { - std::cout << "Account created: Username='" << argv[2] << "', Password='" << argv[3] << "', status=" << argv[4] << std::endl; - return 0; - } - } - } - } - std::cout << "Usage: world adduser username password flag" << std::endl; - std::cout << "flag = 0, 1 or 2" << std::endl; - return 0; - } else if (strcasecmp(argv[1], "flag") == 0) { if (argc == 4) { if (Seperator::IsNumber(argv[3])) { @@ -284,30 +300,6 @@ int main(int argc, char** argv) { std::cout << "flag = 0-200" << std::endl; return 0; } - else if (strcasecmp(argv[1], "startzone") == 0) { - if (argc == 3) { - if (strlen(argv[2]) < 3) { - std::cerr << "Error: zone name too short" << std::endl; - return 1; - } - else if (strlen(argv[2]) > 15) { - std::cerr << "Error: zone name too long" << std::endl; - return 1; - } - else { - if (database.SetVariable("startzone", argv[2])) { - std::cout << "Starting zone changed: '" << argv[2] << "'" << std::endl; - return 0; - } - else { - std::cerr << "database.SetVariable failed." << std::endl; - return 1; - } - } - } - std::cout << "Usage: world startzone zoneshortname" << std::endl; - return 0; - } else if (strcasecmp(argv[1], "ignore_db") == 0) { ignore_db = true; } @@ -360,7 +352,7 @@ int main(int argc, char** argv) { if (!RuleManager::Instance()->UpdateOrphanedRules(&database)) { LogInfo("Failed to process 'Orphaned Rules' update operation."); } - + if (!RuleManager::Instance()->UpdateInjectedRules(&database, "default")) { LogInfo("Failed to process 'Injected Rules' for ruleset 'default' update operation."); } From 4b69f56a6581e1ab78b936637d46c78de9a06bae Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 01:31:39 -0600 Subject: [PATCH 024/157] Update command handler to use actual real arguments --- common/cli/eqemu_command_handler.cpp | 16 +++---- common/database.cpp | 31 ++++++++++++++ common/database.h | 1 + world/world_server_command_handler.cpp | 58 ++++++++++++++------------ world/world_server_command_handler.h | 2 +- 5 files changed, 73 insertions(+), 35 deletions(-) diff --git a/common/cli/eqemu_command_handler.cpp b/common/cli/eqemu_command_handler.cpp index b19c92e9c..a0a019ca8 100644 --- a/common/cli/eqemu_command_handler.cpp +++ b/common/cli/eqemu_command_handler.cpp @@ -38,10 +38,6 @@ namespace EQEmuCommand { void DisplayDebug(argh::parser &cmd) { if (cmd[{"-d", "--debug"}]) { - std::cout << "Positional args:\n"; - for (auto &pos_arg : cmd) - std::cout << '\t' << pos_arg << std::endl; - std::cout << "Positional args:\n"; for (auto &pos_arg : cmd.pos_args()) std::cout << '\t' << pos_arg << std::endl; @@ -73,16 +69,18 @@ namespace EQEmuCommand { { bool arguments_filled = true; + int index = 2; for (auto &arg : arguments) { - if (cmd(arg).str().empty()) { + if (cmd(arg).str().empty() && cmd(index).str().empty()) { arguments_filled = false; } + index++; } if (!arguments_filled || argc == 2) { std::string arguments_string; for (auto &arg : arguments) { - arguments_string += " " + arg + "=*\n"; + arguments_string += " " + arg; } std::string options_string; @@ -90,8 +88,12 @@ namespace EQEmuCommand { options_string += " " + opt + "\n"; } + std::stringstream str; + str << termcolor::yellow << "\nCommand" << termcolor::reset << "\n\n"; + std::cout << fmt::format( - "Command\n\n{0} \n\nArgs\n{1}\nOptions\n{2}", + "{0}{1}{2}\n\nOptions\n{3}", + str.str(), argv[1], arguments_string, options_string diff --git a/common/database.cpp b/common/database.cpp index 70741cf86..e0f601782 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -293,6 +293,37 @@ bool Database::SetAccountStatus(const char* name, int16 status) { return true; } +/** + * @param account_name + * @param status + * @return + */ +bool Database::SetAccountStatus(const std::string& account_name, int16 status) +{ + LogInfo("Account [{}] is attempting to be set to status [{}]", account_name, status); + + std::string query = fmt::format( + SQL( + UPDATE account SET status = {} WHERE name = '{}' + ), + status, + account_name + ); + + auto results = QueryDatabase(query); + + if (!results.Success()) { + return false; + } + + if (results.RowsAffected() == 0) { + LogWarning("Account [{}] does not exist!", account_name); + return false; + } + + return true; +} + /* This initially creates the character during character create */ bool Database::ReserveName(uint32 account_id, char* name) { std::string query = StringFormat("SELECT `account_id`, `name` FROM `character_data` WHERE `name` = '%s'", name); diff --git a/common/database.h b/common/database.h index 0dce5318d..1fca7e1a8 100644 --- a/common/database.h +++ b/common/database.h @@ -179,6 +179,7 @@ public: bool DeleteAccount(const char *name, const char* loginserver); bool GetLiveChar(uint32 account_id, char* cname); bool SetAccountStatus(const char* name, int16 status); + bool SetAccountStatus(const std::string& account_name, int16 status); bool SetLocalPassword(uint32 accid, const char* password); bool UpdateLiveChar(char* charname, uint32 account_id); diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index ed6a3e844..95e1d7989 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -22,6 +22,7 @@ #include "../common/eqemu_logsys.h" #include "../common/json/json.h" #include "../common/version.h" +#include "worlddb.h" namespace WorldserverCommandHandler { @@ -45,38 +46,12 @@ namespace WorldserverCommandHandler { /** * Register commands */ - function_map["test:hello-world"] = &WorldserverCommandHandler::HelloWorld; function_map["database:version"] = &WorldserverCommandHandler::DatabaseVersion; + function_map["database:set-account-status"] = &WorldserverCommandHandler::DatabaseSetAccountStatus; EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); } - /** - * @param argc - * @param argv - * @param cmd - * @param description - */ - void HelloWorld(int argc, char **argv, argh::parser &cmd, std::string &description) - { - description = "Test command"; - - if (cmd[{"-h", "--help"}]) { - return; - } - - std::vector arguments = {}; - std::vector options = { - "--hello", - "--write" - }; - - EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - - LogInfo("hello world!"); - - } - /** * @param argc * @param argv @@ -102,4 +77,33 @@ namespace WorldserverCommandHandler { std::cout << payload.str() << std::endl; } + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void DatabaseSetAccountStatus(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Sets account status by account name"; + + std::vector arguments = { + "{name}", + "{status}" + }; + + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; + } + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + database.SetAccountStatus( + cmd(2).str(), + std::stoi(cmd(3).str()) + ); + } + } \ No newline at end of file diff --git a/world/world_server_command_handler.h b/world/world_server_command_handler.h index a9b14cc58..dae5e6c8d 100644 --- a/world/world_server_command_handler.h +++ b/world/world_server_command_handler.h @@ -26,8 +26,8 @@ namespace WorldserverCommandHandler { void CommandHandler(int argc, char **argv); - void HelloWorld(int argc, char **argv, argh::parser &cmd, std::string &description); void DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description); + void DatabaseSetAccountStatus(int argc, char **argv, argh::parser &cmd, std::string &description); }; From 4fa14e44aa638f44c56ee0031b72815190954e82 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 01:57:34 -0600 Subject: [PATCH 025/157] Update loginserver commands to use the new and more proper argument format --- common/cli/eqemu_command_handler.cpp | 22 +++--- loginserver/loginserver_command_handler.cpp | 57 +++++++------- loginserver/main.cpp | 84 +++++++++++++-------- 3 files changed, 96 insertions(+), 67 deletions(-) diff --git a/common/cli/eqemu_command_handler.cpp b/common/cli/eqemu_command_handler.cpp index a0a019ca8..7ce0dab05 100644 --- a/common/cli/eqemu_command_handler.cpp +++ b/common/cli/eqemu_command_handler.cpp @@ -84,20 +84,22 @@ namespace EQEmuCommand { } std::string options_string; - for (auto &opt : options) { + for (auto &opt : options) { options_string += " " + opt + "\n"; } - std::stringstream str; - str << termcolor::yellow << "\nCommand" << termcolor::reset << "\n\n"; + std::stringstream command_string; - std::cout << fmt::format( - "{0}{1}{2}\n\nOptions\n{3}", - str.str(), - argv[1], - arguments_string, - options_string - ) << std::endl; + command_string << + termcolor::colorize << + termcolor::yellow << + "\nCommand" << + termcolor::reset << "\n\n" << + termcolor::green << argv[1] << arguments_string << termcolor::reset << "\n" << + termcolor::yellow << (!options_string.empty() ? "\nOptions\n\n" : "") << + termcolor::reset << termcolor::cyan << options_string << termcolor::reset; + + std::cout << command_string.str() << std::endl; exit(1); } diff --git a/loginserver/loginserver_command_handler.cpp b/loginserver/loginserver_command_handler.cpp index ae487d676..35599e299 100644 --- a/loginserver/loginserver_command_handler.cpp +++ b/loginserver/loginserver_command_handler.cpp @@ -112,6 +112,9 @@ namespace LoginserverCommandHandler { return; } + server.token_manager = new LoginserverWebserver::TokenManager; + server.token_manager->LoadApiTokens(); + for (auto &it : server.token_manager->loaded_api_tokens) { LogInfo( "token [{0}] can_write [{1}] can_read [{2}]", @@ -133,8 +136,8 @@ namespace LoginserverCommandHandler { description = "Creates Local Loginserver Account"; std::vector arguments = { - "--username", - "--password" + "{username}", + "{password}" }; std::vector options = { "--email=*" @@ -147,8 +150,8 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); AccountManagement::CreateLoginServerAccount( - cmd("--username").str(), - cmd("--password").str(), + cmd(2).str(), + cmd(3).str(), cmd("--email").str() ); } @@ -164,9 +167,9 @@ namespace LoginserverCommandHandler { description = "Creates Loginserver World Administrator Account"; std::vector arguments = { - "--username", - "--password", - "--email" + "{username}", + "{password}", + "{email}" }; std::vector options = {}; @@ -177,9 +180,9 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); AccountManagement::CreateLoginserverWorldAdminAccount( - cmd("--username").str(), - cmd("--password").str(), - cmd("--email").str() + cmd(2).str(), + cmd(3).str(), + cmd(4).str() ); } @@ -194,8 +197,8 @@ namespace LoginserverCommandHandler { description = "Check user login credentials"; std::vector arguments = { - "--username", - "--password" + "{username}", + "{password}" }; std::vector options = {}; @@ -206,11 +209,11 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); auto res = AccountManagement::CheckLoginserverUserCredentials( - cmd("--username").str(), - cmd("--password").str() + cmd(2).str(), + cmd(3).str() ); - LogInfo("Credentials were {0}", res == true ? "accepted" : "not accepted"); + LogInfo("Credentials were {0}", res != 0 ? "accepted" : "not accepted"); } /** @@ -224,8 +227,8 @@ namespace LoginserverCommandHandler { description = "Change user login credentials"; std::vector arguments = { - "--username", - "--password" + "{username}", + "{password}" }; std::vector options = {}; @@ -236,8 +239,8 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); AccountManagement::UpdateLoginserverUserCredentials( - cmd("--username").str(), - cmd("--password").str() + cmd(2).str(), + cmd(3).str() ); } @@ -252,8 +255,8 @@ namespace LoginserverCommandHandler { description = "Check user external login credentials"; std::vector arguments = { - "--username", - "--password" + "{username}", + "{password}" }; std::vector options = {}; @@ -264,8 +267,8 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); auto res = AccountManagement::CheckExternalLoginserverUserCredentials( - cmd("--username").str(), - cmd("--password").str() + cmd(2).str(), + cmd(3).str() ); LogInfo("Credentials were {0}", res ? "accepted" : "not accepted"); @@ -282,8 +285,8 @@ namespace LoginserverCommandHandler { description = "Update world admin account password"; std::vector arguments = { - "--username", - "--password" + "{username}", + "{password}" }; std::vector options = {}; @@ -294,8 +297,8 @@ namespace LoginserverCommandHandler { EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName( - cmd("--username").str(), - cmd("--password").str() + cmd(2).str(), + cmd(3).str() ); } } diff --git a/loginserver/main.cpp b/loginserver/main.cpp index 67b06387c..9e6006342 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -43,22 +43,28 @@ void CatchSignal(int sig_num) { } -int main(int argc, char **argv) +void LoadDatabaseConnection() { - RegisterExecutablePlatform(ExePlatformLogin); - set_exception_handler(); + LogInfo("MySQL Database Init"); - LogInfo("Logging System Init"); + server.db = new Database( + server.config.GetVariableString("database", "user", "root"), + server.config.GetVariableString("database", "password", ""), + server.config.GetVariableString("database", "host", "localhost"), + server.config.GetVariableString("database", "port", "3306"), + server.config.GetVariableString("database", "db", "peq") + ); - if (argc == 1) { - LogSys.LoadLogSettingsDefaults(); - } +} +void LoadServerConfig() +{ server.config = EQ::JsonConfigFile::Load("login.json"); LogInfo("Config System Init"); + /** - * options: logging + * Logging */ server.options.Trace(server.config.GetVariableBool("logging", "trace", false)); server.options.WorldTrace(server.config.GetVariableBool("logging", "world_trace", false)); @@ -66,7 +72,7 @@ int main(int argc, char **argv) server.options.DumpOutPackets(server.config.GetVariableBool("logging", "dump_packets_out", false)); /** - * options: worldservers + * Worldservers */ server.options.RejectDuplicateServers( server.config.GetVariableBool( @@ -77,7 +83,7 @@ int main(int argc, char **argv) server.options.AllowUnregistered(server.config.GetVariableBool("worldservers", "unregistered_allowed", true)); /** - * options: account + * Account */ server.options.AutoCreateAccounts(server.config.GetVariableBool("account", "auto_create_accounts", true)); server.options.AutoLinkAccounts(server.config.GetVariableBool("account", "auto_link_accounts", false)); @@ -92,6 +98,9 @@ int main(int argc, char **argv) ); #endif + /** + * Default Loginserver Name (Don't change) + */ server.options.DefaultLoginServerName( server.config.GetVariableString( "general", @@ -100,6 +109,10 @@ int main(int argc, char **argv) ) ); + /** + * Security + */ + #ifdef ENABLE_SECURITY server.options.EncryptionMode(server.config.GetVariableInt("security", "mode", 13)); #else @@ -115,19 +128,41 @@ int main(int argc, char **argv) true ) ); +} + +int main(int argc, char **argv) +{ + RegisterExecutablePlatform(ExePlatformLogin); + set_exception_handler(); + + LogInfo("Logging System Init"); + + if (argc == 1) { + LogSys.LoadLogSettingsDefaults(); + } + + /** + * Command handler + */ + if (argc > 1) { + LogSys.SilenceConsoleLogging(); + + LoadServerConfig(); + LoadDatabaseConnection(); + + LogSys.LoadLogSettingsDefaults(); + LogSys.log_settings[Logs::Debug].log_to_console = static_cast(Logs::General); + LogSys.log_settings[Logs::Debug].is_category_enabled = 1; + + LoginserverCommandHandler::CommandHandler(argc, argv); + } + + LoadServerConfig(); /** * mysql connect */ - LogInfo("MySQL Database Init"); - - server.db = new Database( - server.config.GetVariableString("database", "user", "root"), - server.config.GetVariableString("database", "password", ""), - server.config.GetVariableString("database", "host", "localhost"), - server.config.GetVariableString("database", "port", "3306"), - server.config.GetVariableString("database", "db", "peq") - ); + LoadDatabaseConnection(); if (argc == 1) { server.db->LoadLogSettings(LogSys.log_settings); @@ -195,17 +230,6 @@ int main(int argc, char **argv) LoginserverWebserver::RegisterRoutes(api); } - /** - * Command handler - */ - if (argc > 1) { - LogSys.LoadLogSettingsDefaults(); - LogSys.log_settings[Logs::Debug].log_to_console = static_cast(Logs::General); - LogSys.log_settings[Logs::Debug].is_category_enabled = 1; - - LoginserverCommandHandler::CommandHandler(argc, argv); - } - LogInfo("[Config] [Logging] IsTraceOn [{0}]", server.options.IsTraceOn()); LogInfo("[Config] [Logging] IsWorldTraceOn [{0}]", server.options.IsWorldTraceOn()); LogInfo("[Config] [Logging] IsDumpInPacketsOn [{0}]", server.options.IsDumpInPacketsOn()); From af80b51bd37d4770aefacb6035726768c8e0ac77 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 02:00:32 -0600 Subject: [PATCH 026/157] Adjust newline in handler --- common/cli/eqemu_command_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/cli/eqemu_command_handler.cpp b/common/cli/eqemu_command_handler.cpp index 7ce0dab05..09c805d8f 100644 --- a/common/cli/eqemu_command_handler.cpp +++ b/common/cli/eqemu_command_handler.cpp @@ -96,7 +96,7 @@ namespace EQEmuCommand { "\nCommand" << termcolor::reset << "\n\n" << termcolor::green << argv[1] << arguments_string << termcolor::reset << "\n" << - termcolor::yellow << (!options_string.empty() ? "\nOptions\n\n" : "") << + termcolor::yellow << (!options_string.empty() ? "\nOptions\n" : "") << termcolor::reset << termcolor::cyan << options_string << termcolor::reset; std::cout << command_string.str() << std::endl; From 25bd285be567990eaebe5619ceaae10122e15eaf Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 02:08:51 -0600 Subject: [PATCH 027/157] World entrypoint rename --- world/CMakeLists.txt | 2 +- world/{net.cpp => main.cpp} | 83 ++++++++++++------------------------- world/{net.h => main.h} | 0 3 files changed, 28 insertions(+), 57 deletions(-) rename world/{net.cpp => main.cpp} (87%) rename world/{net.h => main.h} (100%) diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index e60b3d666..9906b0cfc 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -14,7 +14,7 @@ SET(world_sources lfplist.cpp login_server.cpp login_server_list.cpp - net.cpp + main.cpp queryserv.cpp ucs.cpp web_interface.cpp diff --git a/world/net.cpp b/world/main.cpp similarity index 87% rename from world/net.cpp rename to world/main.cpp index 13f824583..07fb46d67 100644 --- a/world/net.cpp +++ b/world/main.cpp @@ -1,20 +1,23 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ -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 "../common/global_define.h" #include @@ -189,6 +192,13 @@ void RegisterLoginservers() } } +/** + * World process entrypoint + * + * @param argc + * @param argv + * @return + */ int main(int argc, char** argv) { RegisterExecutablePlatform(ExePlatformWorld); LogSys.LoadLogSettingsDefaults(); @@ -268,39 +278,7 @@ int main(int argc, char** argv) { */ bool ignore_db = false; if (argc >= 2) { - std::string tmp; - if (strcasecmp(argv[1], "help") == 0 || strcasecmp(argv[1], "?") == 0 || strcasecmp(argv[1], "/?") == 0 || strcasecmp(argv[1], "-?") == 0 || strcasecmp(argv[1], "-h") == 0 || strcasecmp(argv[1], "-help") == 0) { - std::cout << "Worldserver command line commands:" << std::endl; - std::cout << "adduser username password flag - adds a user account" << std::endl; - std::cout << "flag username flag - sets GM flag on the account" << std::endl; - std::cout << "startzone zoneshortname - sets the starting zone" << std::endl; - std::cout << "-holdzones - reboots lost zones" << std::endl; - return 0; - } - else if (strcasecmp(argv[1], "-holdzones") == 0) { - std::cout << "Reboot Zones mode ON" << std::endl; - holdzones = true; - } - else if (strcasecmp(argv[1], "flag") == 0) { - if (argc == 4) { - if (Seperator::IsNumber(argv[3])) { - if (atoi(argv[3]) >= 0 && atoi(argv[3]) <= 255) { - if (database.SetAccountStatus(argv[2], atoi(argv[3]))) { - std::cout << "Account flagged: Username='" << argv[2] << "', status=" << argv[3] << std::endl; - return 0; - } - else { - std::cerr << "database.SetAccountStatus failed." << std::endl; - return 1; - } - } - } - } - std::cout << "Usage: world flag username flag" << std::endl; - std::cout << "flag = 0-200" << std::endl; - return 0; - } - else if (strcasecmp(argv[1], "ignore_db") == 0) { + if (strcasecmp(argv[1], "ignore_db") == 0) { ignore_db = true; } else { @@ -399,13 +377,6 @@ int main(int argc, char** argv) { LogInfo("Loading launcher list"); launcher_list.LoadList(); - std::string tmp; - database.GetVariable("holdzones", tmp); - if (tmp.length() == 1 && tmp[0] == '1') { - holdzones = true; - } - LogInfo("Reboot zone modes [{}]", holdzones ? "ON" : "OFF"); - LogInfo("Deleted [{}] stale player corpses from database", database.DeleteStalePlayerCorpses()); LogInfo("Loading adventures"); diff --git a/world/net.h b/world/main.h similarity index 100% rename from world/net.h rename to world/main.h From d23dccec826d3f71d9ca63c94529e952e94578c1 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 02:16:43 -0600 Subject: [PATCH 028/157] Add "world:version" command --- common/version.h | 50 ++++++++++++++------------ world/world_server_command_handler.cpp | 31 +++++++++++++++- world/world_server_command_handler.h | 1 + 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/common/version.h b/common/version.h index bf889cc21..91522324f 100644 --- a/common/version.h +++ b/common/version.h @@ -1,20 +1,22 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) - - 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 -*/ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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_VERSION_H #define _EQEMU_VERSION_H @@ -22,14 +24,15 @@ #define LOGIN_VERSION "0.8.0" #define EQEMU_PROTOCOL_VERSION "0.3.10" -#define CURRENT_VERSION "1.1.3" +#define CURRENT_VERSION "2.0" -/* - Everytime a Database SQL is added to Github, - increment CURRENT_BINARY_DATABASE_VERSION number and make sure you update the manifest - Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt -*/ +/** + * Every time a Database SQL is added to Github increment CURRENT_BINARY_DATABASE_VERSION + * number and make sure you update the manifest + * + * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt + */ #define CURRENT_BINARY_DATABASE_VERSION 9144 @@ -38,6 +41,7 @@ #else #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #endif + #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 95e1d7989..72f09462f 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -46,7 +46,8 @@ namespace WorldserverCommandHandler { /** * Register commands */ - function_map["database:version"] = &WorldserverCommandHandler::DatabaseVersion; + function_map["world:version"] = &WorldserverCommandHandler::Version; + function_map["database:version"] = &WorldserverCommandHandler::DatabaseVersion; function_map["database:set-account-status"] = &WorldserverCommandHandler::DatabaseSetAccountStatus; EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); @@ -77,6 +78,34 @@ namespace WorldserverCommandHandler { std::cout << payload.str() << std::endl; } + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void Version(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Shows server version"; + + if (cmd[{"-h", "--help"}]) { + return; + } + + Json::Value database_version; + + database_version["bots_database_version"] = CURRENT_BINARY_BOTS_DATABASE_VERSION; + database_version["compile_date"] = COMPILE_DATE; + database_version["compile_time"] = COMPILE_TIME; + database_version["database_version"] = CURRENT_BINARY_DATABASE_VERSION; + database_version["server_version"] = CURRENT_VERSION; + + std::stringstream payload; + payload << database_version; + + std::cout << payload.str() << std::endl; + } + /** * @param argc * @param argv diff --git a/world/world_server_command_handler.h b/world/world_server_command_handler.h index dae5e6c8d..af4569dc5 100644 --- a/world/world_server_command_handler.h +++ b/world/world_server_command_handler.h @@ -26,6 +26,7 @@ namespace WorldserverCommandHandler { void CommandHandler(int argc, char **argv); + void Version(int argc, char **argv, argh::parser &cmd, std::string &description); void DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description); void DatabaseSetAccountStatus(int argc, char **argv, argh::parser &cmd, std::string &description); }; From b222a619d7d14c59d57b9049dec19c5e805ac191 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 03:04:02 -0600 Subject: [PATCH 029/157] Initial schema fetch with world command "database:schema" --- common/CMakeLists.txt | 1 + common/database_schema.h | 204 +++++++++++++++++++++++++ world/world_server_command_handler.cpp | 44 ++++++ world/world_server_command_handler.h | 1 + 4 files changed, 250 insertions(+) create mode 100644 common/database_schema.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index c4dec4074..a959d9093 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -122,6 +122,7 @@ SET(common_headers cli/terminal_color.hpp data_verification.h database.h + database_schema.h dbcore.h deity.h emu_constants.h diff --git a/common/database_schema.h b/common/database_schema.h new file mode 100644 index 000000000..98cc98a5b --- /dev/null +++ b/common/database_schema.h @@ -0,0 +1,204 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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_DATABASE_SCHEMA_H +#define EQEMU_DATABASE_SCHEMA_H + +#include + +namespace DatabaseSchema { + + /** + * Gets player tables + * + * @return + */ + static std::vector GetPlayerTables() + { + std::vector tables = { + "adventure_stats", + "char_recipe_list", + "character_activities", + "character_alt_currency", + "character_alternate_abilities", + "character_auras", + "character_bandolier", + "character_bind", + "character_buffs", + "character_corpse_items", + "character_corpses", + "character_currency", + "character_data", + "character_disciplines", + "character_enabledtasks", + "character_inspect_messages", + "character_item_recast", + "character_languages", + "character_leadership_abilities", + "character_material", + "character_memmed_spells", + "character_pet_buffs", + "character_pet_inventory", + "character_potionbelt", + "character_skills", + "character_spells", + "character_tribute", + "completed_tasks", + "faction_values", + "friends", + "guild_members", + "instance_list_player", + "inventory", + "inventory_snapshots", + "keyring", + "mail", + "player_titlesets", + "quest_globals", + "timers", + "titles", + "zone_flags" + }; + + return tables; + } + + /** + * Gets content tables + * + * @return + */ + static std::vector GetContentTables() + { + std::vector tables = { + "aa_ability", + "aa_actions", + "aa_effects", + "aa_rank_effects", + "aa_rank_prereqs", + "aa_ranks", + "aa_required_level_cost", + "adventure_template", + "adventure_template_entry", + "adventure_template_entry_flavor", + "altadv_vars", + "alternate_currency", + "auras", + "base_data", + "blocked_spells", + "books", + "char_create_combinations", + "char_create_point_allocations", + "class_skill", + "damageshieldtypes", + "data_buckets", + "doors", + "faction_base_data", + "faction_list", + "faction_list_mod", + "fear_hints", + "fishing", + "forage", + "global_loot", + "goallists", + "graveyard", + "grid", + "grid_entries", + "ground_spawns", + "horses", + "instance_list", + "items", + "ldon_trap_entries", + "ldon_trap_templates", + "lootdrop", + "lootdrop_entries", + "loottable", + "loottable_entries", + "merchantlist", + "npc_emotes", + "npc_faction", + "npc_faction_entries", + "npc_scale_global_base", + "npc_spells", + "npc_spells_effects", + "npc_spells_effects_entries", + "npc_spells_entries", + "npc_types", + "npc_types_metadata", + "npc_types_tint", + "object", + "pets", + "pets_equipmentset", + "pets_equipmentset_entries", + "proximities", + "races", + "skill_caps", + "spawn2", + "spawn_condition_values", + "spawn_conditions", + "spawn_events", + "spawnentry", + "spawngroup", + "spells_new", + "start_zones", + "starting_items", + "task_activities", + "tasks", + "tasksets", + "titles", + "tradeskill_recipe", + "tradeskill_recipe_entries", + "traps", + "tribute_levels", + "tributes", + "veteran_reward_templates", + "zone", + "zone_points", + "zone_server", + "zoneserver_auth", + }; + + return tables; + } + + /** + * Gets server tables + * + * @return + */ + static std::vector GetServerTables() + { + std::vector tables = { + "bug_reports", + "db_str", + "eqtime", + "level_exp_mods", + "logsys_categories", + "name_filter", + "perl_event_export_settings", + "profanity_list", + "saylink" + }; + + return tables; + } + +} + +#endif //EQEMU_DATABASE_SCHEMA_H diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 72f09462f..7efbc1f31 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -23,6 +23,7 @@ #include "../common/json/json.h" #include "../common/version.h" #include "worlddb.h" +#include "../common/database_schema.h" namespace WorldserverCommandHandler { @@ -49,6 +50,7 @@ namespace WorldserverCommandHandler { function_map["world:version"] = &WorldserverCommandHandler::Version; function_map["database:version"] = &WorldserverCommandHandler::DatabaseVersion; function_map["database:set-account-status"] = &WorldserverCommandHandler::DatabaseSetAccountStatus; + function_map["database:schema"] = &WorldserverCommandHandler::DatabaseGetSchema; EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); } @@ -135,4 +137,46 @@ namespace WorldserverCommandHandler { ); } + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void DatabaseGetSchema(int argc, char **argv, argh::parser &cmd, std::string &description) + { + if (cmd[{"-h", "--help"}]) { + return; + } + + Json::Value player_tables_json; + std::vector player_tables = DatabaseSchema::GetPlayerTables(); + for (const auto &table : player_tables) { + player_tables_json.append(table); + } + + Json::Value content_tables_json; + std::vector content_tables = DatabaseSchema::GetContentTables(); + for (const auto &table : content_tables) { + content_tables_json.append(table); + } + + Json::Value server_tables_json; + std::vector server_tables = DatabaseSchema::GetServerTables(); + for (const auto &table : server_tables) { + server_tables_json.append(table); + } + + Json::Value schema; + + schema["server_tables"] = server_tables_json; + schema["player_tables"] = player_tables_json; + schema["content_tables"] = content_tables_json; + + std::stringstream payload; + payload << schema; + + std::cout << payload.str() << std::endl; + } + } \ No newline at end of file diff --git a/world/world_server_command_handler.h b/world/world_server_command_handler.h index af4569dc5..f3d4317f2 100644 --- a/world/world_server_command_handler.h +++ b/world/world_server_command_handler.h @@ -29,6 +29,7 @@ namespace WorldserverCommandHandler { void Version(int argc, char **argv, argh::parser &cmd, std::string &description); void DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description); void DatabaseSetAccountStatus(int argc, char **argv, argh::parser &cmd, std::string &description); + void DatabaseGetSchema(int argc, char **argv, argh::parser &cmd, std::string &description); }; From 4695aa30adaae49b1d36f32200bb6227ec800b88 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 04:11:45 -0600 Subject: [PATCH 030/157] Add more DatabaseSchema entries --- common/database_schema.h | 92 +++++++++++++++++++++++++- world/world_server_command_handler.cpp | 21 ++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/common/database_schema.h b/common/database_schema.h index 98cc98a5b..b60f82162 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -33,7 +33,13 @@ namespace DatabaseSchema { static std::vector GetPlayerTables() { std::vector tables = { + "account", + "account_ip", + "account_flags", + "account_rewards", + "adventure_details", "adventure_stats", + "buyer", "char_recipe_list", "character_activities", "character_alt_currency", @@ -55,15 +61,21 @@ namespace DatabaseSchema { "character_material", "character_memmed_spells", "character_pet_buffs", + "character_pet_info", "character_pet_inventory", "character_potionbelt", "character_skills", "character_spells", + "character_tasks", "character_tribute", "completed_tasks", "faction_values", "friends", + "guild_bank", "guild_members", + "guild_ranks", + "guild_relations", + "guilds", "instance_list_player", "inventory", "inventory_snapshots", @@ -71,8 +83,11 @@ namespace DatabaseSchema { "mail", "player_titlesets", "quest_globals", + "sharedbank", "timers", "titles", + "trader", + "trader_audit", "zone_flags" }; @@ -185,15 +200,90 @@ namespace DatabaseSchema { static std::vector GetServerTables() { std::vector tables = { + "bugs", "bug_reports", + "command_settings", "db_str", + "discovered_items", "eqtime", + "eventlog", + "gm_ips", + "hackers", + "ip_exemptions", + "launcher", + "launcher_zones", "level_exp_mods", "logsys_categories", "name_filter", "perl_event_export_settings", + "petitions", "profanity_list", - "saylink" + "reports", + "rule_sets", + "rule_values", + "saylink", + "variables", + }; + + return tables; + } + + /** + * Gets state tables + * Tables that keep track of server state + * + * @return + */ + static std::vector GetStateTables() + { + std::vector tables = { + "adventure_members", + "chatchannels", + "group_id", + "group_leaders", + "item_tick", + "lfguild", + "merchantlist_temp", + "object_contents", + "raid_details", + "raid_leaders", + "raid_members", + "respawn_times", + "spell_buckets", + "spell_globals", + }; + + return tables; + } + + /** + * Gets login tables + * + * @return + */ + static std::vector GetLoginTables() + { + std::vector tables = { + "login_accounts", + "login_api_tokens", + "login_server_admins", + "login_server_list_types", + "login_world_servers", + }; + + return tables; + } + + /** + * Gets login tables + * + * @return + */ + static std::vector GetVersionTables() + { + std::vector tables = { + "db_version", + "inventory_versions", }; return tables; diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 7efbc1f31..286ab50ad 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -167,11 +167,32 @@ namespace WorldserverCommandHandler { server_tables_json.append(table); } + Json::Value login_tables_json; + std::vector login_tables = DatabaseSchema::GetLoginTables(); + for (const auto &table : login_tables) { + login_tables_json.append(table); + } + + Json::Value state_tables_json; + std::vector state_tables = DatabaseSchema::GetStateTables(); + for (const auto &table : state_tables) { + state_tables_json.append(table); + } + + Json::Value version_tables_json; + std::vector version_tables = DatabaseSchema::GetVersionTables(); + for (const auto &table : version_tables) { + version_tables_json.append(table); + } + Json::Value schema; schema["server_tables"] = server_tables_json; schema["player_tables"] = player_tables_json; schema["content_tables"] = content_tables_json; + schema["login_tables"] = login_tables_json; + schema["state_tables"] = state_tables_json; + schema["version_tables"] = version_tables_json; std::stringstream payload; payload << schema; From f5429130417393a5adb234cb14d7e6bd71127a88 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 17:54:36 -0600 Subject: [PATCH 031/157] Update database_schema.h --- common/database_schema.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/database_schema.h b/common/database_schema.h index b60f82162..898532314 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -33,6 +33,7 @@ namespace DatabaseSchema { static std::vector GetPlayerTables() { std::vector tables = { + "aa_timers", "account", "account_ip", "account_flags", From 71a1142f6b757485a4ea4813b4a2fc877b8c5f1b Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Dec 2019 23:31:54 -0600 Subject: [PATCH 032/157] Move data buckets to player tables --- common/database_schema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/database_schema.h b/common/database_schema.h index 898532314..c24014d7f 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -70,6 +70,7 @@ namespace DatabaseSchema { "character_tasks", "character_tribute", "completed_tasks", + "data_buckets", "faction_values", "friends", "guild_bank", @@ -123,7 +124,6 @@ namespace DatabaseSchema { "char_create_point_allocations", "class_skill", "damageshieldtypes", - "data_buckets", "doors", "faction_base_data", "faction_list", From c173936e326b909c3c62b6144a0a49fcc2c8046a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 24 Dec 2019 00:27:04 -0600 Subject: [PATCH 033/157] Add banned IP's :angry: --- common/database_schema.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/database_schema.h b/common/database_schema.h index c24014d7f..ffbca98a8 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -201,6 +201,7 @@ namespace DatabaseSchema { static std::vector GetServerTables() { std::vector tables = { + "Banned_IPs", "bugs", "bug_reports", "command_settings", From 72f4f10dbb9982e3287d8de58364f9eddbd7fe37 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 24 Dec 2019 02:36:00 -0500 Subject: [PATCH 034/157] Rename table `Banned_IPs` to `banned_ips` [skip ci] --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + utils/sql/git/required/2019_12_24_banned_ips_update.sql | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 utils/sql/git/required/2019_12_24_banned_ips_update.sql diff --git a/common/version.h b/common/version.h index bf889cc21..e51279456 100644 --- a/common/version.h +++ b/common/version.h @@ -31,7 +31,7 @@ */ -#define CURRENT_BINARY_DATABASE_VERSION 9144 +#define CURRENT_BINARY_DATABASE_VERSION 9145 #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 c00255a55..3a6fdc601 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -398,6 +398,7 @@ 9142|2019_09_02_required_spawn_filter.sql|SHOW COLUMNS FROM `spawnentry` LIKE 'condition_value_filter'|empty| 9143|2019_09_16_account_table_changes.sql|SHOW COLUMNS FROM `account` LIKE 'ls_id'|empty| 9144|2019_11_09_logsys_description_update.sql|SELECT * FROM `logsys_categories`|not_empty| +9145|2019_12_24_banned_ips_update.sql|SHOW TABLES LIKE 'Banned_IPs'|not_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/2019_12_24_banned_ips_update.sql b/utils/sql/git/required/2019_12_24_banned_ips_update.sql new file mode 100644 index 000000000..b05bd682b --- /dev/null +++ b/utils/sql/git/required/2019_12_24_banned_ips_update.sql @@ -0,0 +1,5 @@ +RENAME TABLE `Banned_IPs` TO `Banned_IPs_`; + +CREATE TABLE `banned_ips` (PRIMARY KEY (`ip_address`)) SELECT `ip_address`, `notes` FROM `Banned_IPs_`; + +DROP TABLE IF EXISTS `Banned_IPs_`; From 20538e91a1f747c11ad7f86b5ab85cb52e434284 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 24 Dec 2019 02:59:58 -0600 Subject: [PATCH 035/157] Adjust usages for banned_ips table to reflect Uleat's changes --- common/database.cpp | 4 ++-- common/database.h | 2 +- common/ruletypes.h | 2 +- utils/sql/user_tables.txt | 2 +- zone/command.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 70741cf86..cbec7fc10 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -124,7 +124,7 @@ uint32 Database::CheckLogin(const char* name, const char* password, const char * //Get Banned IP Address List - Only return false if the incoming connection's IP address is not present in the banned_ips table. bool Database::CheckBannedIPs(const char* loginIP) { - std::string query = StringFormat("SELECT ip_address FROM Banned_IPs WHERE ip_address='%s'", loginIP); + std::string query = StringFormat("SELECT ip_address FROM banned_ips WHERE ip_address='%s'", loginIP); auto results = QueryDatabase(query); @@ -140,7 +140,7 @@ bool Database::CheckBannedIPs(const char* loginIP) } bool Database::AddBannedIP(char* bannedIP, const char* notes) { - std::string query = StringFormat("INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes); + std::string query = StringFormat("INSERT into banned_ips SET ip_address='%s', notes='%s'", bannedIP, notes); auto results = QueryDatabase(query); if (!results.Success()) { return false; diff --git a/common/database.h b/common/database.h index 0dce5318d..9fc196d18 100644 --- a/common/database.h +++ b/common/database.h @@ -120,7 +120,7 @@ public: /* General Information Queries */ - bool AddBannedIP(char* bannedIP, const char* notes); //Add IP address to the Banned_IPs table. + bool AddBannedIP(char* bannedIP, const char* notes); //Add IP address to the banned_ips table. bool AddGMIP(char* ip_address, char* name); bool CheckBannedIPs(const char* loginIP); //Check incoming connection against banned IP table. bool CheckGMIPs(const char* loginIP, uint32 account_id); diff --git a/common/ruletypes.h b/common/ruletypes.h index 5207b6cbf..c7b4a1aee 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -212,7 +212,7 @@ RULE_CATEGORY_END() RULE_CATEGORY(World) RULE_INT(World, ZoneAutobootTimeoutMS, 60000, "") RULE_INT(World, ClientKeepaliveTimeoutMS, 65000, "") -RULE_BOOL(World, UseBannedIPsTable, false, "Toggle whether or not to check incoming client connections against the Banned_IPs table. Set this value to false to disable this feature") +RULE_BOOL(World, UseBannedIPsTable, false, "Toggle whether or not to check incoming client connections against the banned_ips table. Set this value to false to disable this feature") RULE_BOOL(World, EnableTutorialButton, true, "") RULE_BOOL(World, EnableReturnHomeButton, true, "") RULE_INT(World, MaxLevelForTutorial, 10, "") diff --git a/utils/sql/user_tables.txt b/utils/sql/user_tables.txt index 9806395e8..ee19fe472 100644 --- a/utils/sql/user_tables.txt +++ b/utils/sql/user_tables.txt @@ -6,7 +6,7 @@ account_rewards adventure_details adventure_members adventure_stats -Banned_IPs +banned_ips bugs buyer char_recipe_list diff --git a/zone/command.cpp b/zone/command.cpp index 3d71e4ab4..9740e0811 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7547,7 +7547,7 @@ void command_ipban(Client *c, const Seperator *sep) c->Message(Chat::White, "Usage: #ipban [xxx.xxx.xxx.xxx]"); } else { if(database.AddBannedIP(sep->arg[1], c->GetName())) { - c->Message(Chat::White, "%s has been successfully added to the Banned_IPs table by %s", sep->arg[1], c->GetName()); + c->Message(Chat::White, "%s has been successfully added to the banned_ips table by %s", sep->arg[1], c->GetName()); } else { c->Message(Chat::White, "IPBan Failed (IP address is possibly already in the table?)"); } From 7678a905c8ba3ee4b52fca5c080a43471c40cdb4 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 24 Dec 2019 14:57:58 -0500 Subject: [PATCH 036/157] Modified bot command 'pull' to ignore engaged targets --- zone/bot.cpp | 2 +- zone/bot_command.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c695d7169..b7be0c962 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2825,7 +2825,7 @@ void Bot::AI_Process() return; } - else if (HasTargetReflection()) { + else if (GetTarget()->GetHateList().size()) { WipeHateList(); SetTarget(nullptr); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 51fa44ced..8030d4898 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -4264,6 +4264,12 @@ void bot_command_pull(Client *c, const Seperator *sep) return; } + if (target_mob->IsNPC() && target_mob->GetHateList().size()) { + + c->Message(m_fail, "Your current target is already engaged!"); + return; + } + Bot* bot_puller = nullptr; for (auto bot_iter : sbl) { From 6c35611cd02143452e8a031cfa0f7e715210c218 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 24 Dec 2019 16:24:25 -0500 Subject: [PATCH 037/157] Exported Entity::IsBot() (as Mob) to the perl api --- zone/perl_mob.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index ca72ca803..cf6ef4be7 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -95,6 +95,31 @@ XS(XS_Mob_IsNPC) { XSRETURN(1); } +XS(XS_Mob_IsBot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsBot) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsBot(THIS)"); + { + Mob* THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Mob*, tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->IsBot(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + XS(XS_Mob_IsMob); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_IsMob) { dXSARGS; @@ -8564,6 +8589,7 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "IsClient"), XS_Mob_IsClient, file, "$"); newXSproto(strcpy(buf, "IsNPC"), XS_Mob_IsNPC, file, "$"); + newXSproto(strcpy(buf, "IsBot"), XS_Mob_IsBot, file, "$"); newXSproto(strcpy(buf, "IsMob"), XS_Mob_IsMob, file, "$"); newXSproto(strcpy(buf, "IsCorpse"), XS_Mob_IsCorpse, file, "$"); newXSproto(strcpy(buf, "IsPlayerCorpse"), XS_Mob_IsPlayerCorpse, file, "$"); From 995fb6914ed4699e8b2f51a92ec32ba65865a782 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 24 Dec 2019 23:57:36 -0600 Subject: [PATCH 038/157] Adjust order of operations --- common/cli/eqemu_command_handler.cpp | 9 +++++---- common/database_schema.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/common/cli/eqemu_command_handler.cpp b/common/cli/eqemu_command_handler.cpp index 09c805d8f..bbcdb4612 100644 --- a/common/cli/eqemu_command_handler.cpp +++ b/common/cli/eqemu_command_handler.cpp @@ -182,12 +182,13 @@ namespace EQEmuCommand { } std::cout << std::endl; - } - else if (!ran_command) { - std::cerr << "Unknown command [" << argv[1] << "] ! Try --help" << std::endl; + + std::exit(1); } - exit(1); + if (ran_command) { + std::exit(1); + } } } diff --git a/common/database_schema.h b/common/database_schema.h index ffbca98a8..6033e85b5 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -201,7 +201,7 @@ namespace DatabaseSchema { static std::vector GetServerTables() { std::vector tables = { - "Banned_IPs", + "banned_ips", "bugs", "bug_reports", "command_settings", From fe18033b5d7bb6da2bb97ffc8991734f8014eedd Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 24 Dec 2019 23:57:54 -0600 Subject: [PATCH 039/157] Only add a loginserver if it has a non-empty value --- world/main.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/world/main.cpp b/world/main.cpp index 07fb46d67..0ab5f2089 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -178,15 +178,21 @@ void RegisterLoginservers() LinkedListIterator iterator(loginlist); iterator.Reset(); while (iterator.MoreElements()) { - loginserverlist.Add( - iterator.GetData()->LoginHost.c_str(), - iterator.GetData()->LoginPort, - iterator.GetData()->LoginAccount.c_str(), - iterator.GetData()->LoginPassword.c_str(), - iterator.GetData()->LoginLegacy - ); + if (iterator.GetData()->LoginHost.length()) { + loginserverlist.Add( + iterator.GetData()->LoginHost.c_str(), + iterator.GetData()->LoginPort, + iterator.GetData()->LoginAccount.c_str(), + iterator.GetData()->LoginPassword.c_str(), + iterator.GetData()->LoginLegacy + ); - LogInfo("Added loginserver [{}]:[{}]", iterator.GetData()->LoginHost.c_str(), iterator.GetData()->LoginPort); + LogInfo( + "Added loginserver [{}]:[{}]", + iterator.GetData()->LoginHost.c_str(), + iterator.GetData()->LoginPort + ); + } iterator.Advance(); } } From 77feaa9ac197d22c502dd9b712db2f93ae40614f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 25 Dec 2019 00:19:59 -0600 Subject: [PATCH 040/157] Adjust description [skip ci] --- world/world_server_command_handler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 286ab50ad..003e9abe2 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -145,6 +145,8 @@ namespace WorldserverCommandHandler { */ void DatabaseGetSchema(int argc, char **argv, argh::parser &cmd, std::string &description) { + description = "Displays server database schema"; + if (cmd[{"-h", "--help"}]) { return; } From 07fd803d41c02a915453f1653d6a535a94bb8883 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 25 Dec 2019 02:21:10 -0600 Subject: [PATCH 041/157] Fix db updates for binaries in ./bin/ path --- utils/scripts/eqemu_server.pl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) mode change 100644 => 100755 utils/scripts/eqemu_server.pl diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl old mode 100644 new mode 100755 index e06408bd9..2f57b822e --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -516,13 +516,20 @@ sub check_for_input { } sub check_for_world_bootup_database_update { - if ($OS eq "Windows") { - @db_version = split(': ', `world db_version`); - } - if ($OS eq "Linux") { - @db_version = split(': ', `./world db_version`); + + 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`); + } + $binary_database_version = trim($db_version[1]); $local_database_version = trim(get_mysql_result("SELECT version FROM db_version LIMIT 1")); From 8cb51eb253f5f00c073f7f80d1b4ea31d3b7661a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 25 Dec 2019 03:16:14 -0600 Subject: [PATCH 042/157] Scanning optimization work from over a year ago from EZ - cleaned up a bit --- common/eqemu_logsys.h | 8 +- common/eqemu_logsys_log_aliases.h | 30 ++++ common/ruletypes.h | 1 + world/world_server_command_handler.cpp | 4 +- zone/aggro.cpp | 110 +----------- zone/client.h | 1 - zone/client_process.cpp | 18 +- zone/entity.cpp | 54 +++++- zone/entity.h | 7 +- zone/mob.cpp | 14 +- zone/mob.h | 9 +- zone/mob_ai.cpp | 132 +++++---------- zone/npc.cpp | 224 ++++++++++++++++++++++++- zone/npc.h | 3 + 14 files changed, 393 insertions(+), 222 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index d2d860101..460ff2fef 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -107,6 +107,9 @@ namespace Logs { Emergency, Alert, Notice, + AIScanClose, + AIYellForHelp, + AICastBeneficialClose, MaxCategoryID /* Don't Remove this */ }; @@ -172,7 +175,10 @@ namespace Logs { "Critical", "Emergency", "Alert", - "Notice" + "Notice", + "AI Scan Close", + "AI Yell For Help", + "AI Cast Beneficial Close", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index f90d0e223..d569fba77 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -491,6 +491,36 @@ OutF(LogSys, Logs::Detail, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogAIScanClose(message, ...) do {\ + if (LogSys.log_settings[Logs::AIScanClose].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AIScanClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAIScanCloseDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AIScanClose].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AIScanClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAIYellForHelp(message, ...) do {\ + if (LogSys.log_settings[Logs::AIYellForHelp].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AIYellForHelp, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAIYellForHelpDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AIYellForHelp].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AIYellForHelp, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAICastBeneficialClose(message, ...) do {\ + if (LogSys.log_settings[Logs::AICastBeneficialClose].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AICastBeneficialClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAICastBeneficialCloseDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AICastBeneficialClose].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AICastBeneficialClose, __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__);\ diff --git a/common/ruletypes.h b/common/ruletypes.h index c7b4a1aee..6933b00e4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -568,6 +568,7 @@ RULE_INT(Range, ClientPositionUpdates, 300, "") RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "") RULE_INT(Range, CriticalDamage, 80, "") RULE_INT(Range, ClientNPCScan, 300, "") +RULE_INT(Range, MobCloseScanDistance, 300, "") RULE_CATEGORY_END() diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 003e9abe2..3a929d89f 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -189,10 +189,10 @@ namespace WorldserverCommandHandler { Json::Value schema; - schema["server_tables"] = server_tables_json; - schema["player_tables"] = player_tables_json; schema["content_tables"] = content_tables_json; schema["login_tables"] = login_tables_json; + schema["player_tables"] = player_tables_json; + schema["server_tables"] = server_tables_json; schema["state_tables"] = state_tables_json; schema["version_tables"] = version_tables_json; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 1d579567f..ff0077353 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -36,19 +36,6 @@ extern Zone* zone; //#define LOSDEBUG 6 -//look around a client for things which might aggro the client. -void EntityList::CheckClientAggro(Client *around) -{ - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - Mob *mob = it->second; - if (mob->IsClient()) //also ensures that mob != around - continue; - - if (mob->CheckWillAggro(around) && !mob->CheckAggro(around)) - mob->AddToHateList(around, 25); - } -} - void EntityList::DescribeAggro(Client *towho, NPC *from_who, float d, bool verbose) { float d2 = d*d; @@ -402,22 +389,6 @@ bool Mob::CheckWillAggro(Mob *mob) { return(false); } -Mob* EntityList::AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange) { - if (!sender || !sender->IsNPC()) - return(nullptr); - - auto it = npc_list.begin(); - while (it != npc_list.end()) { - Mob *mob = it->second; - - if (sender->CheckWillAggro(mob)) - return mob; - ++it; - } - - return nullptr; -} - int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con) { // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker @@ -462,82 +433,11 @@ int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con) return Count; } -void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { - if(!sender || !attacker) - return; - if (sender->GetPrimaryFaction() == 0 ) - return; // well, if we dont have a faction set, we're gonna be indiff to everybody - - if (sender->HasAssistAggro()) - return; - - for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { - NPC *mob = it->second; - if (!mob) - continue; - - if (mob->CheckAggro(attacker)) - continue; - - if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap)) - break; - - float r = mob->GetAssistRange(); - r = r * r; - - if ( - mob != sender - && mob != attacker -// && !mob->IsCorpse() -// && mob->IsAIControlled() - && mob->GetPrimaryFaction() != 0 - && DistanceSquared(mob->GetPosition(), sender->GetPosition()) <= r - && !mob->IsEngaged() - && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) - // If we're a pet we don't react to any calls for help if our owner is a client - ) - { - //if they are in range, make sure we are not green... - //then jump in if they are our friend - if(mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) - { - bool useprimfaction = false; - if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) - { - const NPCFactionList *cf = database.GetNPCFactionEntry(mob->GetNPCFactionID()); - if(cf){ - if(cf->assistprimaryfaction != 0) - useprimfaction = true; - } - } - - if(useprimfaction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE ) - { - //attacking someone on same faction, or a friend - //Father Nitwit: make sure we can see them. - if(mob->CheckLosFN(sender)) { -#if (EQDEBUG>=5) - LogDebug("AIYellForHelp(\"[{}]\",\"[{}]\") [{}] attacking [{}] Dist [{}] Z [{}]", - sender->GetName(), attacker->GetName(), mob->GetName(), - attacker->GetName(), DistanceSquared(mob->GetPosition(), - sender->GetPosition()), std::abs(sender->GetZ()+mob->GetZ())); -#endif - mob->AddToHateList(attacker, 25, 0, false); - sender->AddAssistCap(); - } - } - } - } - } -} - -/* -returns false if attack should not be allowed -I try to list every type of conflict that's possible here, so it's easy -to see how the decision is made. Yea, it could be condensed and made -faster, but I'm doing it this way to make it readable and easy to modify -*/ - +/** + * @param target + * @param isSpellAttack + * @return + */ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) { diff --git a/zone/client.h b/zone/client.h index 82b8c7475..8876e4645 100644 --- a/zone/client.h +++ b/zone/client.h @@ -226,7 +226,6 @@ public: Client(EQStreamInterface * ieqs); ~Client(); - std::unordered_map close_mobs; bool is_client_moving; void SetDisplayMobInfoWindow(bool display_mob_info_window); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index a857c3c21..e0068d6e9 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -251,17 +251,21 @@ bool Client::Process() { } } - /* Build a close range list of NPC's */ + /** + * Scan close range mobs + * + * Used in aggro checks + */ if (npc_close_scan_timer.Check()) { close_mobs.clear(); - //Force spawn updates when traveled far - bool force_spawn_updates = false; - float client_update_range = (RuleI(Range, ClientForceSpawnUpdateRange) * RuleI(Range, ClientForceSpawnUpdateRange)); + float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan)); - auto &mob_list = entity_list.GetMobList(); - for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) { - Mob* mob = itr->second; + auto &mob_list = entity_list.GetMobList(); + + for (auto itr : mob_list) { + Mob *mob = itr.second; float distance = DistanceSquared(m_Position, mob->GetPosition()); + if (mob->IsNPC()) { if (distance <= scan_range) { close_mobs.insert(std::pair(mob, distance)); diff --git a/zone/entity.cpp b/zone/entity.cpp index 7f016f197..597780878 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2494,7 +2494,7 @@ bool EntityList::RemoveMob(uint16 delete_id) auto it = mob_list.find(delete_id); if (it != mob_list.end()) { - RemoveMobFromClientCloseLists(it->second); + RemoveMobFromCloseLists(it->second); if (npc_list.count(delete_id)) entity_list.RemoveNPC(delete_id); @@ -2512,17 +2512,19 @@ bool EntityList::RemoveMob(uint16 delete_id) // This is for if the ID is deleted for some reason bool EntityList::RemoveMob(Mob *delete_mob) { - if (delete_mob == 0) + if (delete_mob == 0) { return true; + } auto it = mob_list.begin(); while (it != mob_list.end()) { if (it->second == delete_mob) { - RemoveMobFromClientCloseLists(it->second); + RemoveMobFromCloseLists(it->second); safe_delete(it->second); - if (!corpse_list.count(it->first)) + if (!corpse_list.count(it->first)) { free_ids.push(it->first); + } mob_list.erase(it); return true; } @@ -2539,28 +2541,62 @@ bool EntityList::RemoveNPC(uint16 delete_id) // make sure its proximity is removed RemoveProximity(delete_id); // remove from client close lists - RemoveMobFromClientCloseLists(npc->CastToMob()); + RemoveMobFromCloseLists(npc->CastToMob()); // remove from the list npc_list.erase(it); // remove from limit list if needed - if (npc_limit_list.count(delete_id)) + if (npc_limit_list.count(delete_id)) { npc_limit_list.erase(delete_id); + } + return true; } return false; } -bool EntityList::RemoveMobFromClientCloseLists(Mob *mob) +/** + * @param mob + * @return + */ +bool EntityList::RemoveMobFromCloseLists(Mob *mob) { - auto it = client_list.begin(); - while (it != client_list.end()) { + auto it = mob_list.begin(); + while (it != mob_list.end()) { it->second->close_mobs.erase(mob); ++it; } return false; } +/** + * @param close_mobs + * @param scanning_mob + */ +void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob) +{ + float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); + int list_count = 0; + + close_mobs.clear(); + + auto it = mob_list.begin(); + while (it != mob_list.end()) { + float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); + if (distance <= scan_range) { + close_mobs.insert(std::pair(it->second, distance)); + list_count++; + } + else if (it->second->GetAggroRange() >= scan_range) { + close_mobs.insert(std::pair(it->second, distance)); + list_count++; + } + ++it; + } + + LogAIScanClose("Close List Size [{}] for mob [{}]", list_count, scanning_mob->GetCleanName()); +} + bool EntityList::RemoveMerc(uint16 delete_id) { auto it = merc_list.find(delete_id); diff --git a/zone/entity.h b/zone/entity.h index 7631f0532..561e9387a 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -284,7 +284,7 @@ public: bool RemoveTrap(uint16 delete_id); bool RemoveObject(uint16 delete_id); bool RemoveProximity(uint16 delete_npc_id); - bool RemoveMobFromClientCloseLists(Mob *mob); + bool RemoveMobFromCloseLists(Mob *mob); void RemoveAllMobs(); void RemoveAllClients(); void RemoveAllNPCs(); @@ -443,11 +443,7 @@ public: bool LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count); bool LimitCheckName(const char* npc_name); - void CheckClientAggro(Client *around); - Mob* AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange); int GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con); - void AIYellForHelp(Mob* sender, Mob* attacker); - bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes); bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes); Mob* GetTargetForMez(Mob* caster); uint32 CheckNPCsClose(Mob *center); @@ -501,6 +497,7 @@ public: void RefreshAutoXTargets(Client *c); void RefreshClientXTargets(Client *c); void SendAlternateAdvancementStats(); + void ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob); void GetTrapInfo(Client* client); bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); diff --git a/zone/mob.cpp b/zone/mob.cpp index 44ce45d93..f4d3acab6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -116,7 +116,9 @@ Mob::Mob( m_specialattacks(eSpecialAttacks::None), attack_anim_timer(1000), position_update_melee_push_timer(500), - hate_list_cleanup_timer(6000) + hate_list_cleanup_timer(6000), + mob_scan_close(6000), + mob_check_moving_timer(1000) { mMovementManager = &MobMovementManager::Get(); mMovementManager->AddMob(this); @@ -524,6 +526,16 @@ uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) { return(ANIM_STAND); } +void Mob::GetCloseMobList(std::list> &m_list) +{ + m_list.clear(); + auto it = close_mobs.begin(); + while (it != close_mobs.end()) { + m_list.push_back(std::make_pair(it->first, it->second)); + ++it; + } +} + void Mob::SetInvisible(uint8 state) { invisible = state; diff --git a/zone/mob.h b/zone/mob.h index ab3df4277..5f9dcae03 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1,3 +1,4 @@ + /* EQEMu: Everquest Server Emulator Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.org) @@ -169,6 +170,12 @@ public: void DisplayInfo(Mob *mob); + std::unordered_map close_mobs; + Timer mob_scan_close; + Timer mob_check_moving_timer; + + void GetCloseMobList(std::list> &m_list); + //Somewhat sorted: needs documenting! //Attack @@ -968,7 +975,7 @@ public: void SetEntityVariable(const char *id, const char *m_var); bool EntityVariableExists(const char *id); - void AI_Event_Engaged(Mob* attacker, bool iYellForHelp = true); + void AI_Event_Engaged(Mob* attacker, bool yell_for_help = true); void AI_Event_NoLongerEngaged(); FACTION_VALUE GetSpecialFactionCon(Mob* iOther); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 5958fe18a..bb020060d 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -378,59 +378,6 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::spells::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust)); } -bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { - if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) { - //according to live, you can buff and heal through walls... - //now with PCs, this only applies if you can TARGET the target, but - // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. - // - // This check was put in to address an idle-mob CPU issue - LogError("Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); - return(false); - } - - if(!caster) - return false; - - if(caster->AI_HasSpells() == false) - return false; - - if(caster->GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS)) - return false; - - if (iChance < 100) { - uint8 tmp = zone->random.Int(0, 99); - if (tmp >= iChance) - return false; - } - if (caster->GetPrimaryFaction() == 0 ) - return(false); // well, if we dont have a faction set, we're gonna be indiff to everybody - - float iRange2 = iRange*iRange; - - //Only iterate through NPCs - for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { - NPC* mob = it->second; - - if (mob->GetReverseFactionCon(caster) >= FACTION_KINDLY) { - continue; - } - - if (DistanceSquared(caster->GetPosition(), mob->GetPosition()) > iRange2) { - continue; - } - - if ((iSpellTypes & SpellType_Buff) && !RuleB(NPC, BuffFriends)) { - if (mob != caster) - iSpellTypes = SpellType_Heal; - } - - if (caster->AICastSpell(mob, 100, iSpellTypes)) - return true; - } - return false; -} - void Mob::AI_Init() { pAIControlled = false; @@ -1415,12 +1362,20 @@ void Mob::AI_Process() { } else if (zone->CanDoCombat() && CastToNPC()->WillAggroNPCs() && AI_scan_area_timer->Check()) { - /* - * NPC to NPC aggro checking, npc needs npc_aggro flag - */ - Mob *temp_target = entity_list.AICheckNPCtoNPCAggro(this, GetAggroRange(), GetAssistRange()); - if (temp_target) { - AddToHateList(temp_target); + /** + * NPC to NPC aggro (npc_aggro flag set) + */ + for (auto &close_mob : close_mobs) { + Mob *mob = close_mob.first; + float distance = close_mob.second; + + if (mob->IsClient()) { + continue; + } + + if (this->CheckWillAggro(mob)) { + this->AddToHateList(mob); + } } AI_scan_area_timer->Disable(); @@ -1877,47 +1832,46 @@ void NPC::AI_SetupNextWaypoint() { } } -// Note: Mob that caused this may not get added to the hate list until after this function call completes -void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { - if (!IsAIControlled()) +/** + * @param attacker + * @param yell_for_help + */ +void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help) +{ + if (!IsAIControlled()) { return; + } SetAppearance(eaStanding); - /* - Kick off auto cast timer - */ - if (this->IsNPC()) - this->CastToNPC()->AIautocastspell_timer->Start(300, false); + if (IsNPC()) { + CastToNPC()->AIautocastspell_timer->Start(300, false); - if (iYellForHelp) { - if(IsPet()) { - GetOwner()->AI_Event_Engaged(attacker, iYellForHelp); - } else if (!HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) { - entity_list.AIYellForHelp(this, attacker); - if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled()) - assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer)); + if (yell_for_help) { + if (IsPet()) { + GetOwner()->AI_Event_Engaged(attacker, yell_for_help); + } + else if (!HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) { + CastToNPC()->AIYellForHelp(this, attacker); + if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled()) { + assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer)); + } + } } - } - if(IsNPC()) - { - if(CastToNPC()->GetGrid() > 0) - { + if (CastToNPC()->GetGrid() > 0) { DistractedFromGrid = true; } - if(attacker && !attacker->IsCorpse()) - { + if (attacker && !attacker->IsCorpse()) { //Because sometimes the AIYellForHelp triggers another engaged and then immediately a not engaged //if the target dies before it goes off - if(attacker->GetHP() > 0) - { - if(!CastToNPC()->GetCombatEvent() && GetHP() > 0) - { + if (attacker->GetHP() > 0) { + if (!CastToNPC()->GetCombatEvent() && GetHP() > 0) { parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0); uint16 emoteid = GetEmoteID(); - if(emoteid != 0) - CastToNPC()->DoNPCEmote(ENTERCOMBAT,emoteid); + if (emoteid != 0) { + CastToNPC()->DoNPCEmote(ENTERCOMBAT, emoteid); + } CastToNPC()->SetCombatEvent(true); } } @@ -1996,7 +1950,7 @@ bool NPC::AI_EngagedCastCheck() { // try casting a heal or gate if (!AICastSpell(this, AISpellVar.engaged_beneficial_self_chance, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) { // try casting a heal on nearby - if (!entity_list.AICheckCloseBeneficialSpells(this, AISpellVar.engaged_beneficial_other_chance, MobAISpellRange, SpellType_Heal)) { + if (!AICheckCloseBeneficialSpells(this, AISpellVar.engaged_beneficial_other_chance, MobAISpellRange, SpellType_Heal)) { //nobody to heal, try some detrimental spells. if(!AICastSpell(GetTarget(), AISpellVar.engaged_detrimental_chance, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) { //no spell to cast, try again soon. @@ -2033,7 +1987,7 @@ bool NPC::AI_IdleCastCheck() { if (AIautocastspell_timer->Check(false)) { AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. if (!AICastSpell(this, AISpellVar.idle_beneficial_chance, SpellType_Heal | SpellType_Buff | SpellType_Pet)) { - if(!entity_list.AICheckCloseBeneficialSpells(this, 33, MobAISpellRange, SpellType_Heal | SpellType_Buff)) { + if(!AICheckCloseBeneficialSpells(this, 33, MobAISpellRange, SpellType_Heal | SpellType_Buff)) { //if we didnt cast any spells, our autocast timer just resets to the //last duration it was set to... try to put up a more reasonable timer... AIautocastspell_timer->Start(RandomTimer(AISpellVar.idle_no_sp_recast_min, AISpellVar.idle_no_sp_recast_max), false); diff --git a/zone/npc.cpp b/zone/npc.cpp index efbe98be1..38de9529a 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -704,6 +704,30 @@ bool NPC::Process() SpellProcess(); + if (mob_scan_close.Check()) { + LogAIScanClose( + "is_moving [{}] npc [{}] timer [{}]", + moving ? "true" : "false", + GetCleanName(), + mob_scan_close.GetDuration() + ); + + entity_list.ScanCloseMobs(close_mobs, this); + + if (moving) { + mob_scan_close.Disable(); + mob_scan_close.Start(RandomTimer(3000, 6000)); + } + else { + mob_scan_close.Disable(); + mob_scan_close.Start(RandomTimer(6000, 60000)); + } + } + + if (mob_check_moving_timer.Check() && moving) { + mob_scan_close.Trigger(); + } + if (tic_timer.Check()) { parse->EventNPC(EVENT_TICK, this, nullptr, "", 0); BuffProcess(); @@ -851,7 +875,7 @@ bool NPC::Process() if (assist_timer.Check() && IsEngaged() && !Charmed() && !HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) { - entity_list.AIYellForHelp(this, GetTarget()); + AIYellForHelp(this, GetTarget()); if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled()) assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer)); } @@ -2975,6 +2999,11 @@ bool NPC::IsProximitySet() return false; } +/** + * @param box_size + * @param move_distance + * @param move_delay + */ void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay) { AI_SetRoambox( @@ -2986,3 +3015,196 @@ void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay) move_delay ); } + +/** + * @param caster + * @param chance + * @param in_cast_range + * @param spell_types + * @return + */ +bool NPC::AICheckCloseBeneficialSpells( + NPC *caster, + uint8 chance, + float in_cast_range, + uint32 spell_types +) +{ + if((spell_types & SPELL_TYPES_DETRIMENTAL) != 0) { + LogError("Detrimental spells requested from AICheckCloseBeneficialSpells!"); + return false; + } + + if (!caster) { + return false; + } + + if (!caster->AI_HasSpells()) { + return false; + } + + if (caster->GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS)) { + return false; + } + + if (chance < 100) { + uint8 tmp = zone->random.Int(0, 99); + if (tmp >= chance) { + return false; + } + } + + /** + * Indifferent + */ + if (caster->GetPrimaryFaction() == 0) { + return false; + } + + /** + * Cast range + */ + in_cast_range = (in_cast_range * in_cast_range); + + /** + * Check through close range mobs + */ + for (auto & close_mob : close_mobs) { + Mob *mob = close_mob.first; + float cached_close_mob_distance = close_mob.second; + + if (mob->IsClient()) { + continue; + } + + if (cached_close_mob_distance > in_cast_range) { + continue; + } + + LogAICastBeneficialClose( + "NPC [{}] Distance [{}] Cast Range [{}] Caster [{}]", + mob->GetCleanName(), + cached_close_mob_distance, + in_cast_range, + caster->GetCleanName() + ); + + if (mob->GetReverseFactionCon(caster) >= FACTION_KINDLY) { + continue; + } + + if ((spell_types & SpellType_Buff) && !RuleB(NPC, BuffFriends)) { + if (mob != caster) { + spell_types = SpellType_Heal; + } + } + + if (caster->AICastSpell(mob, 100, spell_types)) { + return true; + } + } + + return false; +} + +/** + * @param sender + * @param attacker + */ +void NPC::AIYellForHelp(Mob *sender, Mob *attacker) +{ + if (!sender || !attacker) { + return; + } + + /** + * If we dont have a faction set, we're gonna be indiff to everybody + */ + if (sender->GetPrimaryFaction() == 0) { + return; + } + + if (sender->HasAssistAggro()) + return; + + LogAIYellForHelp( + "NPC [{}] ID [{}] is starting to scan", + GetCleanName(), + GetID() + ); + + for (auto & close_mob : close_mobs) { + Mob *mob = close_mob.first; + + float distance = DistanceSquared(m_Position, mob->GetPosition()); + + if (mob->IsClient()) { + continue; + } + + float assist_range = (mob->GetAssistRange() * mob->GetAssistRange()); + + if (distance > assist_range) { + continue; + } + + LogAIYellForHelpDetail( + "NPC [{}] ID [{}] is scanning - checking against NPC [{}] range [{}] dist [{}]", + GetCleanName(), + GetID(), + mob->GetCleanName(), + assist_range, + distance + ); + + if (mob->CheckAggro(attacker)) { + continue; + } + + if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap)) { + break; + } + + if ( + mob != sender + && mob != attacker + && mob->GetPrimaryFaction() != 0 + && !mob->IsEngaged() + && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) + ) { + + /** + * if they are in range, make sure we are not green... + * then jump in if they are our friend + */ + if (mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) { + bool use_primary_faction = false; + if (mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) { + const NPCFactionList *cf = database.GetNPCFactionEntry(mob->CastToNPC()->GetNPCFactionID()); + if (cf) { + if (cf->assistprimaryfaction != 0) { + use_primary_faction = true; + } + } + } + + if (use_primary_faction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE) { + //attacking someone on same faction, or a friend + //Father Nitwit: make sure we can see them. + if (mob->CheckLosFN(sender)) { + mob->AddToHateList(attacker, 25, 0, false); + sender->AddAssistCap(); + + LogAIYellForHelpDetail( + "NPC [{}] is assisting [{}] against target [{}]", + mob->GetCleanName(), + this->GetCleanName(), + attacker->GetCleanName() + ); + } + } + } + } + } + +} \ No newline at end of file diff --git a/zone/npc.h b/zone/npc.h index 5d7a7bd73..6d53d2106 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -143,6 +143,9 @@ public: virtual bool AI_IdleCastCheck(); virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); + bool AICheckCloseBeneficialSpells(NPC* caster, uint8 chance, float in_cast_range, uint32 spell_types); + void AIYellForHelp(Mob* sender, Mob* attacker); + void LevelScale(); virtual void SetTarget(Mob* mob); From 0643df3dbe9f2211905f8ee6510060436a3a2ede Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 25 Dec 2019 19:02:35 -0500 Subject: [PATCH 043/157] Tweaked client-referenced _GetWalkspeed() and _GetRunSpeed() functions to avoid external calls when HorseID is null --- zone/mob.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 44ce45d93..fedc5298f 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -598,7 +598,7 @@ int Mob::_GetWalkSpeed() const { runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap; aa_mod += aabonuses.BaseMovementSpeed; - if (IsClient()) { + if (IsClient() && CastToClient()->GetHorseId()) { Mob *horse = entity_list.GetMob(CastToClient()->GetHorseId()); if (horse) { speed_mod = horse->GetBaseRunspeed(); @@ -656,7 +656,7 @@ int Mob::_GetRunSpeed() const { { speed_mod = 325; } - else + else if (CastToClient()->GetHorseId()) { Mob* horse = entity_list.GetMob(CastToClient()->GetHorseId()); if(horse) From f9e822072f2585f6516cc08f057afb61d2a6568d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 28 Dec 2019 17:08:34 -0600 Subject: [PATCH 044/157] AE Scanning adjustments, testing --- common/database.cpp | 1 + common/spdat.cpp | 3 +- zone/attack.cpp | 2 + zone/effects.cpp | 307 +++++++++++++++++++++++++++++++------------- zone/entity.cpp | 32 +++-- zone/entity.h | 36 +++++- zone/spells.cpp | 34 +++-- zone/zone.cpp | 27 ++-- 8 files changed, 312 insertions(+), 130 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 4e762f917..fde091322 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -683,6 +683,7 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe pp->RestTimer // " RestTimer) " ); auto results = QueryDatabase(query); + /* Save Bind Points */ query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i), " diff --git a/common/spdat.cpp b/common/spdat.cpp index 3fd6d19e4..3f0ae6e41 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -87,8 +87,9 @@ bool IsTargetableAESpell(uint16 spell_id) { - if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) + if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) { return true; + } return false; } diff --git a/zone/attack.cpp b/zone/attack.cpp index f39fb6b90..78aecd22d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2153,6 +2153,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", ((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); + entity_list.RemoveMobFromCloseLists(CastToMob()); + Mob *oos = nullptr; if (killer_mob) { oos = killer_mob->GetOwnerOrSelf(); diff --git a/zone/effects.cpp b/zone/effects.cpp index ba82cf3f0..4b908cec6 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -697,110 +697,241 @@ void EntityList::AETaunt(Client* taunter, float range, int32 bonus_hate) } } -// causes caster to hit every mob within dist range of center with -// spell_id. -// NPC spells will only affect other NPCs with compatible faction -void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int *max_targets) +/** + * Causes caster to hit every mob within dist range of center with spell_id + * + * @param caster_mob + * @param center_mob + * @param spell_id + * @param affect_caster + * @param resist_adjust + * @param max_targets + */ +void EntityList::AESpell( + Mob *caster_mob, + Mob *center_mob, + uint16 spell_id, + bool affect_caster, + int16 resist_adjust, + int *max_targets +) { - Mob *curmob = nullptr; + Mob *current_mob = nullptr; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + bool is_npc = caster_mob->IsNPC(); - float dist = caster->GetAOERange(spell_id); - float dist2 = dist * dist; - float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; - float dist_targ = 0; + const auto &cast_target_position = + spells[spell_id].targettype == ST_Ring ? + caster_mob->GetTargetRingLocation() : + static_cast(center_mob->GetPosition()); - const auto &position = spells[spell_id].targettype == ST_Ring ? caster->GetTargetRingLocation() : static_cast(center->GetPosition()); - glm::vec2 min = { position.x - dist, position.y - dist }; - glm::vec2 max = { position.x + dist, position.y + dist }; + /** + * If using Old Rain Targets - there is no max target limitation + */ + if (RuleB(Spells, OldRainTargets)) { + max_targets = nullptr; + } - bool bad = IsDetrimentalSpell(spell_id); - bool isnpc = caster->IsNPC(); - - if (RuleB(Spells, OldRainTargets)) - max_targets = nullptr; // ignore it! - - // if we have a passed in value, use it, otherwise default to data - // detrimental Target AEs have a default value of 4 for PCs and unlimited for NPCs + /** + * Max AOE targets + */ int max_targets_allowed = 0; // unlimited - if (max_targets) // rains pass this in since they need to preserve the count through waves + if (max_targets) { // rains pass this in since they need to preserve the count through waves max_targets_allowed = *max_targets; - else if (spells[spell_id].aemaxtargets) + } + else if (spells[spell_id].aemaxtargets) { max_targets_allowed = spells[spell_id].aemaxtargets; - else if (IsTargetableAESpell(spell_id) && bad && !isnpc) + } + else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc) { max_targets_allowed = 4; + } - int iCounter = 0; + int target_hit_counter = 0; + float distance_to_target = 0; - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; - // test to fix possible cause of random zone crashes..external methods accessing client properties before they're initialized - if (curmob->IsClient() && !curmob->CastToClient()->ClientFinishedLoading()) - continue; - if (curmob == caster && !affect_caster) //watch for caster too - continue; - if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient()) - continue; - if (spells[spell_id].targettype == ST_AreaClientOnly && !curmob->IsClient()) - continue; - if (spells[spell_id].targettype == ST_AreaNPCOnly && !curmob->IsNPC()) - continue; - // check PC/NPC only flag 1 = PCs, 2 = NPCs - if (spells[spell_id].pcnpc_only_flag == 1 && !curmob->IsClient() && !curmob->IsMerc()) - continue; - if (spells[spell_id].pcnpc_only_flag == 2 && (curmob->IsClient() || curmob->IsMerc())) - continue; - if (!IsWithinAxisAlignedBox(static_cast(curmob->GetPosition()), min, max)) - continue; + for (auto &it : caster_mob->close_mobs) { + current_mob = it.first; - dist_targ = DistanceSquared(curmob->GetPosition(), position); + if (!current_mob) { + continue; + } - if (dist_targ > dist2) //make sure they are in range + LogDebug("iterating [{}]", current_mob->GetCleanName()); + + if (!AESpellFilterCriteria( + current_mob, + caster_mob, + center_mob, + spell_id, + max_targets, + max_targets_allowed, + target_hit_counter, + distance_to_target, + cast_target_position, + affect_caster, + resist_adjust + )) { continue; - if (dist_targ < min_range2) //make sure they are in range - continue; - if (isnpc && curmob->IsNPC() && spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting - FACTION_VALUE f = curmob->GetReverseFactionCon(caster); - if (bad) { - //affect mobs that are on our hate list, or - //which have bad faction with us - if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) - continue; - } else { - //only affect mobs we would assist. - if (!(f <= FACTION_AMIABLE)) - continue; + } + + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); + } + + LogDebug("Done iterating [{}]", caster_mob->GetCleanName()); + + if (max_targets && max_targets_allowed) { + *max_targets = *max_targets - target_hit_counter; + } +} + +/** + * @param caster_mob + * @param center_mob + * @param spell_id + * @param affect_caster + * @param resist_adjust + * @param max_targets + */ +bool EntityList::AESpellFilterCriteria( + Mob *current_mob, + Mob *caster_mob, + Mob *center_mob, + uint16 spell_id, + int *max_targets, + int &max_targets_allowed, + int &target_hit_counter, + float &distance_to_target, + const glm::vec3 &cast_target_position, + bool affect_caster, + int16 resist_adjust +) { + + if (!current_mob) { + return false; + } + + bool is_npc = caster_mob->IsNPC(); + float distance = caster_mob->GetAOERange(spell_id); + float distance_squared = distance * distance; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + glm::vec2 min = {cast_target_position.x - distance, cast_target_position.y - distance}; + glm::vec2 max = {cast_target_position.x + distance, cast_target_position.y + distance}; + + if (current_mob->IsClient() && !current_mob->CastToClient()->ClientFinishedLoading()) { + return false; + } + + if (current_mob == caster_mob && !affect_caster) { + return false; + } + + if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { + return false; + } + + if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) { + return false; + } + + if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) { + return false; + } + + /** + * Check PC / NPC + * 1 = PC + * 2 = NPC + */ + if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc()) { + return false; + } + + if (spells[spell_id].pcnpc_only_flag == 2 && (current_mob->IsClient() || current_mob->IsMerc())) { + return false; + } + + if (!IsWithinAxisAlignedBox(static_cast(current_mob->GetPosition()), min, max)) { + return false; + } + + distance_to_target = DistanceSquared(current_mob->GetPosition(), cast_target_position); + + if (distance_to_target > distance_squared) { + return false; + } + + if (distance_to_target < min_range2) { + return false; + } + + if (is_npc && current_mob->IsNPC() && + spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting + FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob); + if (is_detrimental_spell) { + //affect mobs that are on our hate list, or + //which have bad faction with us + if ( + !(caster_mob->CheckAggro(current_mob) || + faction_value == FACTION_THREATENLY || + faction_value == FACTION_SCOWLS)) { + return false; } } - //finally, make sure they are within range - if (bad) { - if (!caster->IsAttackAllowed(curmob, true)) - continue; - if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob)) - continue; - if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize())) - continue; - } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... - // This does not check faction for beneficial AE buffs..only agro and attackable. - // I've tested for spells that I can find without problem, but a faction-based - // check may still be needed. Any changes here should also reflect in BardAEPulse() - if (caster->IsAttackAllowed(curmob, true)) - continue; - if (caster->CheckAggro(curmob)) - continue; - } - - curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); - caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); - - if (max_targets_allowed) { // if we have a limit, increment count - iCounter++; - if (iCounter >= max_targets_allowed) // we done - break; + else { + //only affect mobs we would assist. + if (!(faction_value <= FACTION_AMIABLE)) { + return false; + } } } - if (max_targets && max_targets_allowed) - *max_targets = *max_targets - iCounter; + /** + * Finally, make sure they are within range + */ + if (is_detrimental_spell) { + if (!caster_mob->IsAttackAllowed(current_mob, true)) { + return false; + } + if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) { + return false; + } + if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN( + caster_mob->GetTargetRingX(), + caster_mob->GetTargetRingY(), + caster_mob->GetTargetRingZ(), + current_mob->GetSize())) { + return false; + } + } + else { + + /** + * Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... + * This does not check faction for beneficial AE buffs... only agro and attackable. + * I've tested for spells that I can find without problem, but a faction-based + * check may still be needed. Any changes here should also reflect in BardAEPulse() + */ + if (caster_mob->IsAttackAllowed(current_mob, true)) { + return false; + } + if (caster_mob->CheckAggro(current_mob)) { + return false; + } + } + + /** + * Increment hit count if max targets + */ + if (max_targets_allowed) { + target_hit_counter++; + if (target_hit_counter >= max_targets_allowed) { + return false; + } + } + + return true; } void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) @@ -812,8 +943,8 @@ void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool a bool bad = IsDetrimentalSpell(spell_id); - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; + for (auto & it : mob_list) { + curmob = it.second; if (curmob == center) //do not affect center continue; if (curmob == caster && !affect_caster) //watch for caster too diff --git a/zone/entity.cpp b/zone/entity.cpp index 597780878..c7ecb0f80 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2486,30 +2486,41 @@ void EntityList::RemoveAllEncounters() } } +/** + * @param delete_id + * @return + */ bool EntityList::RemoveMob(uint16 delete_id) { - if (delete_id == 0) + if (delete_id == 0) { return true; + } auto it = mob_list.find(delete_id); if (it != mob_list.end()) { RemoveMobFromCloseLists(it->second); - if (npc_list.count(delete_id)) + if (npc_list.count(delete_id)) { entity_list.RemoveNPC(delete_id); - else if (client_list.count(delete_id)) + } + else if (client_list.count(delete_id)) { entity_list.RemoveClient(delete_id); + } safe_delete(it->second); - if (!corpse_list.count(delete_id)) + if (!corpse_list.count(delete_id)) { free_ids.push(it->first); + } mob_list.erase(it); return true; } return false; } -// This is for if the ID is deleted for some reason +/** + * @param delete_mob + * @return + */ bool EntityList::RemoveMob(Mob *delete_mob) { if (delete_mob == 0) { @@ -2533,19 +2544,19 @@ bool EntityList::RemoveMob(Mob *delete_mob) return false; } +/** + * @param delete_id + * @return + */ bool EntityList::RemoveNPC(uint16 delete_id) { auto it = npc_list.find(delete_id); if (it != npc_list.end()) { NPC *npc = it->second; - // make sure its proximity is removed RemoveProximity(delete_id); - // remove from client close lists RemoveMobFromCloseLists(npc->CastToMob()); - // remove from the list npc_list.erase(it); - // remove from limit list if needed if (npc_limit_list.count(delete_id)) { npc_limit_list.erase(delete_id); } @@ -2561,11 +2572,14 @@ bool EntityList::RemoveNPC(uint16 delete_id) */ bool EntityList::RemoveMobFromCloseLists(Mob *mob) { + LogDebug("Removing mob [{}] from close lists", mob->GetCleanName()); + auto it = mob_list.begin(); while (it != mob_list.end()) { it->second->close_mobs.erase(mob); ++it; } + return false; } diff --git a/zone/entity.h b/zone/entity.h index 561e9387a..94884167c 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -388,11 +388,37 @@ public: void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); void QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); - void AEAttack(Mob *attacker, float dist, int Hand = EQEmu::invslot::slotPrimary, int count = 0, bool IsFromSpell = false); - void AETaunt(Client *caster, float range=0, int32 bonus_hate=0); - void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0, int *max_targets = nullptr); - void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); - void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); + void AEAttack( + Mob *attacker, + float dist, + int Hand = EQEmu::invslot::slotPrimary, + int count = 0, + bool IsFromSpell = false + ); + void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0); + void AESpell( + Mob *caster, + Mob *center, + uint16 spell_id, + bool affect_caster = true, + int16 resist_adjust = 0, + int *max_targets = nullptr + ); + static bool AESpellFilterCriteria( + Mob *current_mob, + Mob *caster_mob, + Mob *center_mob, + uint16 spell_id, + int *max_targets, + int &max_targets_allowed, + int &target_hit_counter, + float &distance_to_target, + const glm::vec3 &cast_target_position, + bool affect_caster = true, + int16 resist_adjust = 0 + ); + void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); + void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); //trap stuff Mob* GetTrapTrigger(Trap* trap); diff --git a/zone/spells.cpp b/zone/spells.cpp index cec6e8142..07de6b715 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4793,24 +4793,32 @@ int16 Mob::CalcFearResistChance() return resistchance; } -float Mob::GetAOERange(uint16 spell_id) { - float range; +/** + * @param spell_id + * @return + */ +float Mob::GetAOERange(uint16 spell_id) +{ + float range = spells[spell_id].aoerange; - range = spells[spell_id].aoerange; - if(range == 0) //for TGB spells, they prolly do not have an aoe range + /** + * For TGB + */ + if (range == 0) { range = spells[spell_id].range; - if(range == 0) - range = 10; //something.... - - if(IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { - //Live AA - Extended Notes, SionachiesCrescendo - float song_bonus = static_cast(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange); - range += range*song_bonus /100.0f; } - range = GetActSpellRange(spell_id, range); + if (range == 0) { + range = 10; + } - return(range); + if (IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { + //Live AA - Extended Notes, SionachiesCrescendo + float song_bonus = static_cast(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange); + range += range * song_bonus / 100.0f; + } + + return GetActSpellRange(spell_id, range); } /////////////////////////////////////////////////////////////////////////////// diff --git a/zone/zone.cpp b/zone/zone.cpp index 99c120cab..b88ddd071 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1997,7 +1997,6 @@ const char *Zone::GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &locat if (spell_id != blocked_spells[x].spellid && blocked_spells[x].spellid != 0) { continue; } - switch (blocked_spells[x].type) { case ZoneBlockedSpellTypes::ZoneWide: { return blocked_spells[x].message; @@ -2033,21 +2032,21 @@ void Zone::SetInstanceTimer(uint32 new_duration) void Zone::LoadLDoNTraps() { - const std::string query = "SELECT id, type, spell_id, skill, locked FROM ldon_trap_templates"; - auto results = database.QueryDatabase(query); - if (!results.Success()) { + const std::string query = "SELECT id, type, spell_id, skill, locked FROM ldon_trap_templates"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { return; - } + } - for (auto row = results.begin();row != results.end(); ++row) { - auto lt = new LDoNTrapTemplate; - lt->id = atoi(row[0]); - lt->type = (LDoNChestTypes)atoi(row[1]); - lt->spell_id = atoi(row[2]); - lt->skill = atoi(row[3]); - lt->locked = atoi(row[4]); - ldon_trap_list[lt->id] = lt; - } + for (auto row = results.begin(); row != results.end(); ++row) { + auto lt = new LDoNTrapTemplate; + lt->id = atoi(row[0]); + lt->type = (LDoNChestTypes) atoi(row[1]); + lt->spell_id = atoi(row[2]); + lt->skill = atoi(row[3]); + lt->locked = atoi(row[4]); + ldon_trap_list[lt->id] = lt; + } } From 5fee9b2b3ed345bfb4a9e035f752f0d841b63a96 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 28 Dec 2019 23:17:38 -0500 Subject: [PATCH 045/157] Fix for Client::SendStatsWindow(...) based zone crashes --- zone/client.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 911c090dc..a99b2818a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6743,11 +6743,22 @@ void Client::SendStatsWindow(Client* client, bool use_window) GetRawACNoShield(shield_ac); std::string skill_list[] = { - "1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration","Alteration","Apply Poison","Archery","Backstab","Bind Wound","Bash","Block","Brass Instruments","Channeling","Conjuration", - "Defense","Disarm","Disarm Traps","Divination","Dodge","Double Attack","Dragon Punch","Dual Wield","Eagle Strike","Evocation","Feign Death","Flying Kick","Forage","Hand To Hand","Hide","Kick", - "Meditate","Mend","Offense","Parry","Pick Lock","Piercing","Riposte","Round Kick","Safe Fall","Sense Heading","Singing","Sneak","Specialize Abjuration","Specialize Alteration","Specialize Conjuration", - "Specialize Divination","Specialize Evocation","Pick Pockets","Stringed_Instruments","Swimming","Throwing","Tiger Claw","Tracking","Wind Instruments","Fishing","Make Poison","Tinkering","Research","Alchemy", - "Baking","Tailoring","Sense Traps","Blacksmithing","Fletching","Brewing","Alcohol_Tolerance","Begging","Jewelry Making","Pottery","Percussion Instruments","Intimidation","Berserking","Taunt","Frenzy" + "1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration", + "Alteration","Apply Poison","Archery","Backstab","Bind Wound", + "Bash","Block","Brass Instruments","Channeling","Conjuration", + "Defense","Disarm","Disarm Traps","Divination","Dodge", + "Double Attack","Dragon Punch","Dual Wield","Eagle Strike","Evocation", + "Feign Death","Flying Kick","Forage","Hand To Hand","Hide", + "Kick","Meditate","Mend","Offense","Parry", + "Pick Lock","1H Piercing","Riposte","Round Kick","Safe Fall", + "Sense Heading","Singing","Sneak","Specialize Abjuration","Specialize Alteration", + "Specialize Conjuration","Specialize Divination","Specialize Evocation","Pick Pockets","Stringed Instruments", + "Swimming","Throwing","Tiger Claw","Tracking","Wind Instruments", + "Fishing","Make Poison","Tinkering","Research","Alchemy", + "Baking","Tailoring","Sense Traps","Blacksmithing","Fletching", + "Brewing","Alcohol_Tolerance","Begging","Jewelry Making","Pottery", + "Percussion Instruments","Intimidation","Berserking","Taunt","Frenzy", + "Remove Traps","Triple Attack","2H Piercing" }; std::string skill_mods = ""; From 4b6a1242f5b8eb39bb5d62e92d0a64aeebab8318 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 28 Dec 2019 22:45:35 -0600 Subject: [PATCH 046/157] Fix water map loading case sensitivity issues when using lowercase water maps like the rest of the map code --- .gitignore | 4 +++- zone/map.cpp | 55 +++++++++++++++++++++++++++++++--------------- zone/map.h | 2 +- zone/water_map.cpp | 28 +++++++++++++++++++++-- 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index daba92762..89dd521c5 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,6 @@ perl/ *cbp submodules/* -cmake-build-debug/ \ No newline at end of file +cmake-build-debug/ + +.nfs.* \ No newline at end of file diff --git a/zone/map.cpp b/zone/map.cpp index a0e3e6400..dbfa3928e 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -242,46 +242,65 @@ bool Map::Load(std::string filename, bool force_mmf_overwrite) return true; } #else -bool Map::Load(std::string filename) + +/** + * @param filename + * @return + */ +bool Map::Load(const std::string &filename) { #endif /*USE_MAP_MMFS*/ - FILE *f = fopen(filename.c_str(), "rb"); - if(f) { + FILE *map_file = fopen(filename.c_str(), "rb"); + if (map_file) { uint32 version; - if(fread(&version, sizeof(version), 1, f) != 1) { - fclose(f); + if (fread(&version, sizeof(version), 1, map_file) != 1) { + fclose(map_file); return false; } - - if(version == 0x01000000) { + + if (version == 0x01000000) { LogInfo("Loaded V1 Map File [{}]", filename.c_str()); - bool v = LoadV1(f); - fclose(f); + bool loaded_map_file = LoadV1(map_file); + fclose(map_file); + + if (loaded_map_file) { + LogInfo("Loaded V1 Map File [{}]", filename.c_str()); + } else { + LogError("Failed to load V1 Map File [{}]", filename.c_str()); + } #ifdef USE_MAP_MMFS if (v) return SaveMMF(filename, force_mmf_overwrite); #endif /*USE_MAP_MMFS*/ - return v; - } else if(version == 0x02000000) { - LogInfo("Loaded V2 Map File [{}]", filename.c_str()); - bool v = LoadV2(f); - fclose(f); + return loaded_map_file; + } + else if (version == 0x02000000) { + LogInfo("Loading V2 Map File [{}]", filename.c_str()); + bool loaded_map_file = LoadV2(map_file); + fclose(map_file); + + if (loaded_map_file) { + LogInfo("Loaded V2 Map File [{}]", filename.c_str()); + } else { + LogError("Failed to load V2 Map File [{}]", filename.c_str()); + } #ifdef USE_MAP_MMFS if (v) return SaveMMF(filename, force_mmf_overwrite); #endif /*USE_MAP_MMFS*/ - return v; - } else { - fclose(f); + return loaded_map_file; + } + else { + fclose(map_file); return false; } } - + return false; } diff --git a/zone/map.h b/zone/map.h index 8a2ec7f68..eefb43f2d 100644 --- a/zone/map.h +++ b/zone/map.h @@ -47,7 +47,7 @@ public: #ifdef USE_MAP_MMFS bool Load(std::string filename, bool force_mmf_overwrite = false); #else - bool Load(std::string filename); + bool Load(const std::string& filename); #endif static Map *LoadMapFile(std::string file); diff --git a/zone/water_map.cpp b/zone/water_map.cpp index d2e6daad1..ec5575f24 100644 --- a/zone/water_map.cpp +++ b/zone/water_map.cpp @@ -10,10 +10,34 @@ #include #include +/** + * @param name + * @return + */ +inline bool file_exists(const std::string& name) { + std::ifstream f(name.c_str()); + return f.good(); +} + +/** + * @param zone_name + * @return + */ WaterMap* WaterMap::LoadWaterMapfile(std::string zone_name) { std::transform(zone_name.begin(), zone_name.end(), zone_name.begin(), ::tolower); - - std::string file_path = Config->MapDir + "water/" + zone_name + std::string(".wtr"); + + std::string filename; + if (file_exists("maps")) { + filename = "maps"; + } + else if (file_exists("Maps")) { + filename = "Maps"; + } + else { + filename = Config->MapDir; + } + + std::string file_path = filename + "/water/" + zone_name + std::string(".wtr"); LogDebug("Attempting to load water map with path [{}]", file_path.c_str()); FILE *f = fopen(file_path.c_str(), "rb"); if(f) { From 9481e9eb2d2a179bab63cf08839c90dfe543130f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 28 Dec 2019 23:58:18 -0600 Subject: [PATCH 047/157] More scanning changes around AE cast --- common/eqemu_logsys.h | 2 + common/eqemu_logsys_log_aliases.h | 10 ++++ common/ruletypes.h | 2 +- zone/attack.cpp | 7 ++- zone/effects.cpp | 86 ++++++++++++++++++++++--------- zone/entity.cpp | 6 --- zone/mob.cpp | 19 ++++--- 7 files changed, 91 insertions(+), 41 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 460ff2fef..7514f34a1 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -110,6 +110,7 @@ namespace Logs { AIScanClose, AIYellForHelp, AICastBeneficialClose, + AoeCast, MaxCategoryID /* Don't Remove this */ }; @@ -179,6 +180,7 @@ namespace Logs { "AI Scan Close", "AI Yell For Help", "AI Cast Beneficial Close", + "AOE Cast", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index d569fba77..5b9b249ba 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -521,6 +521,16 @@ OutF(LogSys, Logs::Detail, Logs::AICastBeneficialClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogAoeCast(message, ...) do {\ + if (LogSys.log_settings[Logs::AoeCast].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AoeCast, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAoeCastDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AoeCast].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AoeCast, __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__);\ diff --git a/common/ruletypes.h b/common/ruletypes.h index 6933b00e4..5aeaa3021 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -568,7 +568,7 @@ RULE_INT(Range, ClientPositionUpdates, 300, "") RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "") RULE_INT(Range, CriticalDamage, 80, "") RULE_INT(Range, ClientNPCScan, 300, "") -RULE_INT(Range, MobCloseScanDistance, 300, "") +RULE_INT(Range, MobCloseScanDistance, 600, "") RULE_CATEGORY_END() diff --git a/zone/attack.cpp b/zone/attack.cpp index 78aecd22d..3f1bb6ebd 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -850,11 +850,12 @@ int Mob::ACSum() auto over_cap = ac - softcap; ac = softcap + (over_cap * returns); } - LogCombat("ACSum ac [{}] softcap [{}] returns [{}]", ac, softcap, returns); + LogCombatDetail("ACSum ac [{}] softcap [{}] returns [{}]", ac, softcap, returns); } else { - LogCombat("ACSum ac [{}]", ac); + LogCombatDetail("ACSum ac [{}]", ac); } + return ac; } @@ -2153,8 +2154,6 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", ((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); - entity_list.RemoveMobFromCloseLists(CastToMob()); - Mob *oos = nullptr; if (killer_mob) { oos = killer_mob->GetOwnerOrSelf(); diff --git a/zone/effects.cpp b/zone/effects.cpp index 4b908cec6..d9292d2e4 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -748,37 +748,77 @@ void EntityList::AESpell( int target_hit_counter = 0; float distance_to_target = 0; + float distance = caster_mob->GetAOERange(spell_id); - for (auto &it : caster_mob->close_mobs) { - current_mob = it.first; + LogAoeCast( + "Close scan distance [{}] cast distance [{}]", + RuleI(Range, MobCloseScanDistance), + distance + ); - if (!current_mob) { - continue; + if (distance <= RuleI(Range, MobCloseScanDistance)) { + + LogAoeCast("Using close scan mob list"); + + for (auto &it : caster_mob->close_mobs) { + current_mob = it.first; + + if (!current_mob) { + continue; + } + + LogAoeCast("Checking against close scan mob [{}]", current_mob->GetCleanName()); + + if (!AESpellFilterCriteria( + current_mob, + caster_mob, + center_mob, + spell_id, + max_targets, + max_targets_allowed, + target_hit_counter, + distance_to_target, + cast_target_position, + affect_caster, + resist_adjust + )) { + continue; + } + + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } + } else { - LogDebug("iterating [{}]", current_mob->GetCleanName()); + LogAoeCast("Using full entity mob list"); - if (!AESpellFilterCriteria( - current_mob, - caster_mob, - center_mob, - spell_id, - max_targets, - max_targets_allowed, - target_hit_counter, - distance_to_target, - cast_target_position, - affect_caster, - resist_adjust - )) { - continue; + for (auto &it : mob_list) { + current_mob = it.second; + + LogAoeCast("Checking against full zone scan mob [{}]", current_mob->GetCleanName()); + + if (!AESpellFilterCriteria( + current_mob, + caster_mob, + center_mob, + spell_id, + max_targets, + max_targets_allowed, + target_hit_counter, + distance_to_target, + cast_target_position, + affect_caster, + resist_adjust + )) { + continue; + } + + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } - - current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); - caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } - LogDebug("Done iterating [{}]", caster_mob->GetCleanName()); + LogAoeCast("Done iterating [{}]", caster_mob->GetCleanName()); if (max_targets && max_targets_allowed) { *max_targets = *max_targets - target_hit_counter; diff --git a/zone/entity.cpp b/zone/entity.cpp index c7ecb0f80..815e703cd 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2498,9 +2498,6 @@ bool EntityList::RemoveMob(uint16 delete_id) auto it = mob_list.find(delete_id); if (it != mob_list.end()) { - - RemoveMobFromCloseLists(it->second); - if (npc_list.count(delete_id)) { entity_list.RemoveNPC(delete_id); } @@ -2530,8 +2527,6 @@ bool EntityList::RemoveMob(Mob *delete_mob) auto it = mob_list.begin(); while (it != mob_list.end()) { if (it->second == delete_mob) { - RemoveMobFromCloseLists(it->second); - safe_delete(it->second); if (!corpse_list.count(it->first)) { free_ids.push(it->first); @@ -2554,7 +2549,6 @@ bool EntityList::RemoveNPC(uint16 delete_id) if (it != npc_list.end()) { NPC *npc = it->second; RemoveProximity(delete_id); - RemoveMobFromCloseLists(npc->CastToMob()); npc_list.erase(it); if (npc_limit_list.count(delete_id)) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 6c04d2c13..c0c2cc092 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -467,35 +467,40 @@ Mob::~Mob() AI_Stop(); if (GetPet()) { - if (GetPet()->Charmed()) + if (GetPet()->Charmed()) { GetPet()->BuffFadeByEffect(SE_Charm); - else + } + else { SetPet(0); + } } EQApplicationPacket app; CreateDespawnPacket(&app, !IsCorpse()); - Corpse* corpse = entity_list.GetCorpseByID(GetID()); - if(!corpse || (corpse && !corpse->IsPlayerCorpse())) + Corpse *corpse = entity_list.GetCorpseByID(GetID()); + if (!corpse || (corpse && !corpse->IsPlayerCorpse())) { entity_list.QueueClients(this, &app, true); + } entity_list.RemoveFromTargets(this, true); - if(trade) { + if (trade) { Mob *with = trade->With(); - if(with && with->IsClient()) { + if (with && with->IsClient()) { with->CastToClient()->FinishTrade(with); with->trade->Reset(); } delete trade; } - if(HasTempPetsActive()){ + if (HasTempPetsActive()) { entity_list.DestroyTempPets(this); } entity_list.UnMarkNPC(GetID()); UninitializeBuffSlots(); + entity_list.RemoveMobFromCloseLists(this); + #ifdef BOTS LeaveHealRotationTargetPool(); #endif From 6b465c576da91efdf5cba81b2b80f4847068dac8 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 02:01:48 -0600 Subject: [PATCH 048/157] More scanning work to unify data structures --- common/eqemu_logsys.h | 2 ++ common/eqemu_logsys_log_aliases.h | 10 ++++++++++ zone/attack.cpp | 2 +- zone/client_process.cpp | 6 +++--- zone/effects.cpp | 2 +- zone/entity.cpp | 26 ++++++++++++++++++++------ zone/entity.h | 12 ++++++++++-- zone/mob.cpp | 11 +---------- zone/mob.h | 4 +--- zone/mob_ai.cpp | 3 +-- zone/npc.cpp | 14 +++++++------- 11 files changed, 57 insertions(+), 35 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 7514f34a1..fc1dd36f2 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -111,6 +111,7 @@ namespace Logs { AIYellForHelp, AICastBeneficialClose, AoeCast, + EntityManagement, MaxCategoryID /* Don't Remove this */ }; @@ -181,6 +182,7 @@ namespace Logs { "AI Yell For Help", "AI Cast Beneficial Close", "AOE Cast", + "Entity Management", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 5b9b249ba..24abfdfc9 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -531,6 +531,16 @@ OutF(LogSys, Logs::Detail, Logs::AoeCast, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogEntityManagement(message, ...) do {\ + if (LogSys.log_settings[Logs::EntityManagement].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::EntityManagement, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogEntityManagementDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::EntityManagement].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::EntityManagement, __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__);\ diff --git a/zone/attack.cpp b/zone/attack.cpp index 3f1bb6ebd..cd68f5d57 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5488,4 +5488,4 @@ int32 Mob::GetHPRegen() const int32 Mob::GetManaRegen() const { return mana_regen; -} +} \ No newline at end of file diff --git a/zone/client_process.cpp b/zone/client_process.cpp index e0068d6e9..e75d53d7c 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -268,10 +268,10 @@ bool Client::Process() { if (mob->IsNPC()) { if (distance <= scan_range) { - close_mobs.insert(std::pair(mob, distance)); + close_mobs.insert(std::pair(mob->GetID(), mob)); } else if ((mob->GetAggroRange() * mob->GetAggroRange()) > scan_range) { - close_mobs.insert(std::pair(mob, distance)); + close_mobs.insert(std::pair(mob->GetID(), mob)); } } } @@ -597,7 +597,7 @@ bool Client::Process() { if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) { int npc_scan_count = 0; for (auto & close_mob : close_mobs) { - Mob *mob = close_mob.first; + Mob *mob = close_mob.second; if (!mob) continue; diff --git a/zone/effects.cpp b/zone/effects.cpp index d9292d2e4..5d9730a54 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -761,7 +761,7 @@ void EntityList::AESpell( LogAoeCast("Using close scan mob list"); for (auto &it : caster_mob->close_mobs) { - current_mob = it.first; + current_mob = it.second; if (!current_mob) { continue; diff --git a/zone/entity.cpp b/zone/entity.cpp index 815e703cd..b9756827e 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -62,7 +62,8 @@ extern char errorname[32]; Entity::Entity() { - id = 0; + id = 0; + initial_id = 0; spawn_timestamp = time(nullptr); } @@ -2566,11 +2567,22 @@ bool EntityList::RemoveNPC(uint16 delete_id) */ bool EntityList::RemoveMobFromCloseLists(Mob *mob) { - LogDebug("Removing mob [{}] from close lists", mob->GetCleanName()); + LogEntityManagement( + "Attempting to remove mob [{}] from close lists entity_id ({})", + mob->GetCleanName(), + mob->GetInitialId() + ); auto it = mob_list.begin(); while (it != mob_list.end()) { - it->second->close_mobs.erase(mob); + LogEntityManagement( + "Removing mob [{}] from [{}] close list entity_id ({})", + mob->GetCleanName(), + it->second->GetCleanName(), + mob->GetInitialId() + ); + + it->second->close_mobs.erase(mob->GetInitialId()); ++it; } @@ -2581,7 +2593,7 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob) * @param close_mobs * @param scanning_mob */ -void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob) +void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob) { float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); int list_count = 0; @@ -2590,13 +2602,15 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob auto it = mob_list.begin(); while (it != mob_list.end()) { + Mob *mob = it->second; + float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); if (distance <= scan_range) { - close_mobs.insert(std::pair(it->second, distance)); + close_mobs.insert(std::pair(mob->GetID(), mob)); list_count++; } else if (it->second->GetAggroRange() >= scan_range) { - close_mobs.insert(std::pair(it->second, distance)); + close_mobs.insert(std::pair(mob->GetID(), mob)); list_count++; } ++it; diff --git a/zone/entity.h b/zone/entity.h index 94884167c..d251eeb57 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -109,6 +109,7 @@ public: const Beacon *CastToBeacon() const; const Encounter *CastToEncounter() const; + inline const uint16& GetInitialId() const { return initial_id; } inline const uint16& GetID() const { return id; } inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; } @@ -122,10 +123,17 @@ public: protected: friend class EntityList; - inline virtual void SetID(uint16 set_id) { id = set_id; } + inline virtual void SetID(uint16 set_id) { + id = set_id; + + if (initial_id == 0 && set_id > 0) { + initial_id = set_id; + } + } uint32 pDBAsyncWorkID; private: uint16 id; + uint16 initial_id; time_t spawn_timestamp; }; @@ -523,7 +531,7 @@ public: void RefreshAutoXTargets(Client *c); void RefreshClientXTargets(Client *c); void SendAlternateAdvancementStats(); - void ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob); + void ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob); void GetTrapInfo(Client* client); bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); diff --git a/zone/mob.cpp b/zone/mob.cpp index c0c2cc092..6a4c12969 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -500,6 +500,7 @@ Mob::~Mob() UninitializeBuffSlots(); entity_list.RemoveMobFromCloseLists(this); + close_mobs.clear(); #ifdef BOTS LeaveHealRotationTargetPool(); @@ -531,16 +532,6 @@ uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) { return(ANIM_STAND); } -void Mob::GetCloseMobList(std::list> &m_list) -{ - m_list.clear(); - auto it = close_mobs.begin(); - while (it != close_mobs.end()) { - m_list.push_back(std::make_pair(it->first, it->second)); - ++it; - } -} - void Mob::SetInvisible(uint8 state) { invisible = state; diff --git a/zone/mob.h b/zone/mob.h index 5f9dcae03..cdd6f7041 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -170,12 +170,10 @@ public: void DisplayInfo(Mob *mob); - std::unordered_map close_mobs; + std::unordered_map close_mobs; Timer mob_scan_close; Timer mob_check_moving_timer; - void GetCloseMobList(std::list> &m_list); - //Somewhat sorted: needs documenting! //Attack diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index bb020060d..525948bb2 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1366,8 +1366,7 @@ void Mob::AI_Process() { * NPC to NPC aggro (npc_aggro flag set) */ for (auto &close_mob : close_mobs) { - Mob *mob = close_mob.first; - float distance = close_mob.second; + Mob *mob = close_mob.second; if (mob->IsClient()) { continue; diff --git a/zone/npc.cpp b/zone/npc.cpp index 38de9529a..5325ea836 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3070,21 +3070,22 @@ bool NPC::AICheckCloseBeneficialSpells( * Check through close range mobs */ for (auto & close_mob : close_mobs) { - Mob *mob = close_mob.first; - float cached_close_mob_distance = close_mob.second; + Mob *mob = close_mob.second; if (mob->IsClient()) { continue; } - if (cached_close_mob_distance > in_cast_range) { + float distance = DistanceSquared(mob->GetPosition(), GetPosition()); + + if (distance > in_cast_range) { continue; } LogAICastBeneficialClose( "NPC [{}] Distance [{}] Cast Range [{}] Caster [{}]", mob->GetCleanName(), - cached_close_mob_distance, + distance, in_cast_range, caster->GetCleanName() ); @@ -3133,9 +3134,8 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) GetID() ); - for (auto & close_mob : close_mobs) { - Mob *mob = close_mob.first; - + for (auto &close_mob : close_mobs) { + Mob *mob = close_mob.second; float distance = DistanceSquared(m_Position, mob->GetPosition()); if (mob->IsClient()) { From ec5faea9b1793932998c0d663f3a9c1e9356e4fb Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 02:42:52 -0600 Subject: [PATCH 049/157] Use GetSmartMobList in AESpell --- zone/effects.cpp | 328 ++++++++++++++++++----------------------------- zone/entity.cpp | 15 +++ zone/entity.h | 17 +-- zone/mob.h | 1 + 4 files changed, 143 insertions(+), 218 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 5d9730a54..ef2d1d842 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -716,15 +716,20 @@ void EntityList::AESpell( int *max_targets ) { - Mob *current_mob = nullptr; - bool is_detrimental_spell = IsDetrimentalSpell(spell_id); - bool is_npc = caster_mob->IsNPC(); - const auto &cast_target_position = spells[spell_id].targettype == ST_Ring ? caster_mob->GetTargetRingLocation() : static_cast(center_mob->GetPosition()); + Mob *current_mob = nullptr; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + bool is_npc = caster_mob->IsNPC(); + float distance = caster_mob->GetAOERange(spell_id); + float distance_squared = distance * distance; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; + glm::vec2 min = {cast_target_position.x - distance, cast_target_position.y - distance}; + glm::vec2 max = {cast_target_position.x + distance, cast_target_position.y + distance}; + /** * If using Old Rain Targets - there is no max target limitation */ @@ -748,7 +753,6 @@ void EntityList::AESpell( int target_hit_counter = 0; float distance_to_target = 0; - float distance = caster_mob->GetAOERange(spell_id); LogAoeCast( "Close scan distance [{}] cast distance [{}]", @@ -756,66 +760,129 @@ void EntityList::AESpell( distance ); - if (distance <= RuleI(Range, MobCloseScanDistance)) { + for (auto &it : entity_list.GetCloseMobList(caster_mob, distance)) { + current_mob = it.second; - LogAoeCast("Using close scan mob list"); - - for (auto &it : caster_mob->close_mobs) { - current_mob = it.second; - - if (!current_mob) { - continue; - } - - LogAoeCast("Checking against close scan mob [{}]", current_mob->GetCleanName()); - - if (!AESpellFilterCriteria( - current_mob, - caster_mob, - center_mob, - spell_id, - max_targets, - max_targets_allowed, - target_hit_counter, - distance_to_target, - cast_target_position, - affect_caster, - resist_adjust - )) { - continue; - } - - current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); - caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); + if (!current_mob) { + continue; } - } else { - LogAoeCast("Using full entity mob list"); + LogAoeCast("Checking AOE against mob [{}]", current_mob->GetCleanName()); - for (auto &it : mob_list) { - current_mob = it.second; + if (current_mob->IsClient() && !current_mob->CastToClient()->ClientFinishedLoading()) { + continue; + } - LogAoeCast("Checking against full zone scan mob [{}]", current_mob->GetCleanName()); + if (current_mob == caster_mob && !affect_caster) { + continue; + } - if (!AESpellFilterCriteria( - current_mob, - caster_mob, - center_mob, - spell_id, - max_targets, - max_targets_allowed, - target_hit_counter, - distance_to_target, - cast_target_position, - affect_caster, - resist_adjust - )) { + if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { + continue; + } + + if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) { + continue; + } + + if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) { + continue; + } + + /** + * Check PC / NPC + * 1 = PC + * 2 = NPC + */ + if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc()) { + continue; + } + + if (spells[spell_id].pcnpc_only_flag == 2 && (current_mob->IsClient() || current_mob->IsMerc())) { + continue; + } + + if (!IsWithinAxisAlignedBox(static_cast(current_mob->GetPosition()), min, max)) { + continue; + } + + distance_to_target = DistanceSquared(current_mob->GetPosition(), cast_target_position); + + if (distance_to_target > distance_squared) { + continue; + } + + if (distance_to_target < min_range2) { + continue; + } + + if (is_npc && current_mob->IsNPC() && + spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting + FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob); + if (is_detrimental_spell) { + //affect mobs that are on our hate list, or + //which have bad faction with us + if ( + !(caster_mob->CheckAggro(current_mob) || + faction_value == FACTION_THREATENLY || + faction_value == FACTION_SCOWLS)) { + continue; + } + } + else { + //only affect mobs we would assist. + if (!(faction_value <= FACTION_AMIABLE)) { + continue; + } + } + } + + /** + * Finally, make sure they are within range + */ + if (is_detrimental_spell) { + if (!caster_mob->IsAttackAllowed(current_mob, true)) { + continue; + } + if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) { + continue; + } + if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN( + caster_mob->GetTargetRingX(), + caster_mob->GetTargetRingY(), + caster_mob->GetTargetRingZ(), + current_mob->GetSize())) { continue; } - - current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); - caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } + else { + + /** + * Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... + * This does not check faction for beneficial AE buffs... only agro and attackable. + * I've tested for spells that I can find without problem, but a faction-based + * check may still be needed. Any changes here should also reflect in BardAEPulse() + */ + if (caster_mob->IsAttackAllowed(current_mob, true)) { + continue; + } + if (caster_mob->CheckAggro(current_mob)) { + continue; + } + } + + /** + * Increment hit count if max targets + */ + if (max_targets_allowed) { + target_hit_counter++; + if (target_hit_counter >= max_targets_allowed) { + break; + } + } + + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } LogAoeCast("Done iterating [{}]", caster_mob->GetCleanName()); @@ -825,155 +892,6 @@ void EntityList::AESpell( } } -/** - * @param caster_mob - * @param center_mob - * @param spell_id - * @param affect_caster - * @param resist_adjust - * @param max_targets - */ -bool EntityList::AESpellFilterCriteria( - Mob *current_mob, - Mob *caster_mob, - Mob *center_mob, - uint16 spell_id, - int *max_targets, - int &max_targets_allowed, - int &target_hit_counter, - float &distance_to_target, - const glm::vec3 &cast_target_position, - bool affect_caster, - int16 resist_adjust -) { - - if (!current_mob) { - return false; - } - - bool is_npc = caster_mob->IsNPC(); - float distance = caster_mob->GetAOERange(spell_id); - float distance_squared = distance * distance; - float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; - bool is_detrimental_spell = IsDetrimentalSpell(spell_id); - glm::vec2 min = {cast_target_position.x - distance, cast_target_position.y - distance}; - glm::vec2 max = {cast_target_position.x + distance, cast_target_position.y + distance}; - - if (current_mob->IsClient() && !current_mob->CastToClient()->ClientFinishedLoading()) { - return false; - } - - if (current_mob == caster_mob && !affect_caster) { - return false; - } - - if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { - return false; - } - - if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) { - return false; - } - - if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) { - return false; - } - - /** - * Check PC / NPC - * 1 = PC - * 2 = NPC - */ - if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc()) { - return false; - } - - if (spells[spell_id].pcnpc_only_flag == 2 && (current_mob->IsClient() || current_mob->IsMerc())) { - return false; - } - - if (!IsWithinAxisAlignedBox(static_cast(current_mob->GetPosition()), min, max)) { - return false; - } - - distance_to_target = DistanceSquared(current_mob->GetPosition(), cast_target_position); - - if (distance_to_target > distance_squared) { - return false; - } - - if (distance_to_target < min_range2) { - return false; - } - - if (is_npc && current_mob->IsNPC() && - spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting - FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob); - if (is_detrimental_spell) { - //affect mobs that are on our hate list, or - //which have bad faction with us - if ( - !(caster_mob->CheckAggro(current_mob) || - faction_value == FACTION_THREATENLY || - faction_value == FACTION_SCOWLS)) { - return false; - } - } - else { - //only affect mobs we would assist. - if (!(faction_value <= FACTION_AMIABLE)) { - return false; - } - } - } - - /** - * Finally, make sure they are within range - */ - if (is_detrimental_spell) { - if (!caster_mob->IsAttackAllowed(current_mob, true)) { - return false; - } - if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) { - return false; - } - if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN( - caster_mob->GetTargetRingX(), - caster_mob->GetTargetRingY(), - caster_mob->GetTargetRingZ(), - current_mob->GetSize())) { - return false; - } - } - else { - - /** - * Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... - * This does not check faction for beneficial AE buffs... only agro and attackable. - * I've tested for spells that I can find without problem, but a faction-based - * check may still be needed. Any changes here should also reflect in BardAEPulse() - */ - if (caster_mob->IsAttackAllowed(current_mob, true)) { - return false; - } - if (caster_mob->CheckAggro(current_mob)) { - return false; - } - } - - /** - * Increment hit count if max targets - */ - if (max_targets_allowed) { - target_hit_counter++; - if (target_hit_counter >= max_targets_allowed) { - return false; - } - } - - return true; -} - void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) { Mob *curmob = nullptr; diff --git a/zone/entity.cpp b/zone/entity.cpp index b9756827e..fa9a198f1 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5030,3 +5030,18 @@ void EntityList::ReloadMerchants() { } } } + +/** + * @param mob + * @param distance + * @return + */ +std::unordered_map &EntityList::GetCloseMobList(Mob *mob, float distance) +{ + if (distance <= RuleI(Range, MobCloseScanDistance)) { + return mob->close_mobs; + } + + return mob_list; +} + diff --git a/zone/entity.h b/zone/entity.h index d251eeb57..125bd1c7a 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -412,19 +412,6 @@ public: int16 resist_adjust = 0, int *max_targets = nullptr ); - static bool AESpellFilterCriteria( - Mob *current_mob, - Mob *caster_mob, - Mob *center_mob, - uint16 spell_id, - int *max_targets, - int &max_targets_allowed, - int &target_hit_counter, - float &distance_to_target, - const glm::vec3 &cast_target_position, - bool affect_caster = true, - int16 resist_adjust = 0 - ); void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); @@ -525,6 +512,8 @@ public: inline const std::unordered_map &GetObjectList() { return object_list; } inline const std::unordered_map &GetDoorsList() { return door_list; } + std::unordered_map &GetCloseMobList(Mob *mob, float distance); + void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); uint16 GetFreeID(); @@ -537,6 +526,7 @@ public: bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); void UpdateAllTraps(bool respawn, bool repopnow = false); void ClearTrapPointers(); + protected: friend class Zone; void Depop(bool StartSpawnTimer = false); @@ -592,6 +582,7 @@ private: private: std::list bot_list; #endif + }; class BulkZoneSpawnPacket { diff --git a/zone/mob.h b/zone/mob.h index cdd6f7041..c0449af4b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -171,6 +171,7 @@ public: void DisplayInfo(Mob *mob); std::unordered_map close_mobs; + std::unordered_map& GetSmartMobList(float distance = 0); Timer mob_scan_close; Timer mob_check_moving_timer; From 233f26996b472208e2882edf485eb44ee49738de Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:15:42 -0600 Subject: [PATCH 050/157] Add GetCloseMobList into other calls --- zone/entity.cpp | 3 +++ zone/entity.h | 2 +- zone/mob.h | 1 - zone/npc.cpp | 26 ++++++++++---------------- zone/npc.h | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index fa9a198f1..e20090491 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5032,6 +5032,9 @@ void EntityList::ReloadMerchants() { } /** + * If we have a distance requested that is greater than our scanning distance + * then we return the full list + * * @param mob * @param distance * @return diff --git a/zone/entity.h b/zone/entity.h index 125bd1c7a..8e37d5342 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -512,7 +512,7 @@ public: inline const std::unordered_map &GetObjectList() { return object_list; } inline const std::unordered_map &GetDoorsList() { return door_list; } - std::unordered_map &GetCloseMobList(Mob *mob, float distance); + std::unordered_map &GetCloseMobList(Mob *mob, float distance = 0); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); diff --git a/zone/mob.h b/zone/mob.h index c0449af4b..cdd6f7041 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -171,7 +171,6 @@ public: void DisplayInfo(Mob *mob); std::unordered_map close_mobs; - std::unordered_map& GetSmartMobList(float distance = 0); Timer mob_scan_close; Timer mob_check_moving_timer; diff --git a/zone/npc.cpp b/zone/npc.cpp index 5325ea836..8b4996177 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3019,14 +3019,14 @@ void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay) /** * @param caster * @param chance - * @param in_cast_range + * @param cast_range * @param spell_types * @return */ bool NPC::AICheckCloseBeneficialSpells( NPC *caster, uint8 chance, - float in_cast_range, + float cast_range, uint32 spell_types ) { @@ -3061,24 +3061,18 @@ bool NPC::AICheckCloseBeneficialSpells( return false; } - /** - * Cast range - */ - in_cast_range = (in_cast_range * in_cast_range); - /** * Check through close range mobs */ - for (auto & close_mob : close_mobs) { + for (auto & close_mob : entity_list.GetCloseMobList(caster, cast_range)) { Mob *mob = close_mob.second; if (mob->IsClient()) { continue; } - float distance = DistanceSquared(mob->GetPosition(), GetPosition()); - - if (distance > in_cast_range) { + float distance = Distance(mob->GetPosition(), caster->GetPosition()); + if (distance > cast_range) { continue; } @@ -3086,7 +3080,7 @@ bool NPC::AICheckCloseBeneficialSpells( "NPC [{}] Distance [{}] Cast Range [{}] Caster [{}]", mob->GetCleanName(), distance, - in_cast_range, + cast_range, caster->GetCleanName() ); @@ -3134,7 +3128,7 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) GetID() ); - for (auto &close_mob : close_mobs) { + for (auto &close_mob : entity_list.GetCloseMobList(sender)) { Mob *mob = close_mob.second; float distance = DistanceSquared(m_Position, mob->GetPosition()); @@ -3143,18 +3137,18 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) } float assist_range = (mob->GetAssistRange() * mob->GetAssistRange()); - if (distance > assist_range) { continue; } LogAIYellForHelpDetail( - "NPC [{}] ID [{}] is scanning - checking against NPC [{}] range [{}] dist [{}]", + "NPC [{}] ID [{}] is scanning - checking against NPC [{}] range [{}] dist [{}] in_range [{}]", GetCleanName(), GetID(), mob->GetCleanName(), assist_range, - distance + distance, + (distance < assist_range) ); if (mob->CheckAggro(attacker)) { diff --git a/zone/npc.h b/zone/npc.h index 6d53d2106..84ab8b4ef 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -143,7 +143,7 @@ public: virtual bool AI_IdleCastCheck(); virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); - bool AICheckCloseBeneficialSpells(NPC* caster, uint8 chance, float in_cast_range, uint32 spell_types); + bool AICheckCloseBeneficialSpells(NPC* caster, uint8 chance, float cast_range, uint32 spell_types); void AIYellForHelp(Mob* sender, Mob* attacker); void LevelScale(); From e531e68b6d1b43fa7aad0a8c69178c14fcb46a3e Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:22:14 -0600 Subject: [PATCH 051/157] Port AEBardPulse to use GetCloseMobList logic --- zone/effects.cpp | 91 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index ef2d1d842..95654909f 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -930,57 +930,88 @@ void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool a } } -// causes caster to hit every mob within dist range of center with -// a bard pulse of spell_id. -// NPC spells will only affect other NPCs with compatible faction -void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) +/** + * Causes caster to hit every mob within dist range of center with a bard pulse of spell_id + * NPC spells will only affect other NPCs with compatible faction + * + * @param caster + * @param center + * @param spell_id + * @param affect_caster + */ +void EntityList::AEBardPulse( + Mob *caster, + Mob *center, + uint16 spell_id, + bool affect_caster) { - Mob *curmob = nullptr; + Mob *current_mob = nullptr; + float distance = caster->GetAOERange(spell_id); + float distance_squared = distance * distance; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + bool is_npc = caster->IsNPC(); - float dist = caster->GetAOERange(spell_id); - float dist2 = dist * dist; + for (auto &it : entity_list.GetCloseMobList(caster, distance)) { + current_mob = it.second; - bool bad = IsDetrimentalSpell(spell_id); - bool isnpc = caster->IsNPC(); + /** + * Skip self + */ + if (current_mob == center) { + continue; + } - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; - if (curmob == center) //do not affect center + if (current_mob == caster && !affect_caster) { continue; - if (curmob == caster && !affect_caster) //watch for caster too + } + + if (DistanceSquared(center->GetPosition(), current_mob->GetPosition()) > distance_squared) { //make sure they are in range continue; - if (DistanceSquared(center->GetPosition(), curmob->GetPosition()) > dist2) //make sure they are in range - continue; - if (isnpc && curmob->IsNPC()) { //check npc->npc casting - FACTION_VALUE f = curmob->GetReverseFactionCon(caster); - if (bad) { + } + + /** + * check npc->npc casting + */ + if (is_npc && current_mob->IsNPC()) { + FACTION_VALUE faction = current_mob->GetReverseFactionCon(caster); + if (is_detrimental_spell) { //affect mobs that are on our hate list, or //which have bad faction with us - if (!(caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) + if (!(caster->CheckAggro(current_mob) || faction == FACTION_THREATENLY || faction == FACTION_SCOWLS)) { continue; - } else { + } + } + else { //only affect mobs we would assist. - if (!(f <= FACTION_AMIABLE)) + if (!(faction <= FACTION_AMIABLE)) { continue; + } } } - //finally, make sure they are within range - if (bad) { - if (!center->CheckLosFN(curmob)) + + /** + * LOS + */ + if (is_detrimental_spell) { + if (!center->CheckLosFN(current_mob)) { continue; - } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... + } + } + else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... // See notes in AESpell() above for more info. - if (caster->IsAttackAllowed(curmob, true)) + if (caster->IsAttackAllowed(current_mob, true)) { continue; - if (caster->CheckAggro(curmob)) + } + if (caster->CheckAggro(current_mob)) { continue; + } } - //if we get here... cast the spell. - curmob->BardPulse(spell_id, caster); + current_mob->BardPulse(spell_id, caster); } - if (caster->IsClient()) + if (caster->IsClient()) { caster->CastToClient()->CheckSongSkillIncrease(spell_id); + } } // Rampage and stuff for clients. Normal and Duration rampages From 0f9c34cf3cb6b620b3c278dbce88cc0337f7b75b Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:28:48 -0600 Subject: [PATCH 052/157] Port more AE functions to use flexible GetCloseMobList call --- zone/effects.cpp | 104 +++++++++++++++++++++++++++++++---------------- zone/entity.h | 4 +- 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 95654909f..0af580524 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -673,27 +673,42 @@ void Client::SendDisciplineTimer(uint32 timer_id, uint32 duration) } } -void EntityList::AETaunt(Client* taunter, float range, int32 bonus_hate) +/** + * @param taunter + * @param range + * @param bonus_hate + */ +void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate) { - if (range == 0) - range = 40; //Live AE taunt range - Hardcoded. - range = range * range; + /** + * Live AE taunt range - Hardcoded. + */ + if (range == 0) { + range = 40; + } - auto it = npc_list.begin(); - while (it != npc_list.end()) { - NPC *them = it->second; - float zdiff = taunter->GetZ() - them->GetZ(); - if (zdiff < 0) - zdiff *= -1; - if (zdiff < 10 - && taunter->IsAttackAllowed(them) - && DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range) { + float range_squared = range * range; + + for (auto &it : entity_list.GetCloseMobList(taunter, range)) { + Mob *them = it.second; + + if (!them->IsNPC()) { + continue; + } + + float z_difference = taunter->GetZ() - them->GetZ(); + if (z_difference < 0) { + z_difference *= -1; + } + + if (z_difference < 10 + && taunter->IsAttackAllowed(them) + && DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared) { if (taunter->CheckLosFN(them)) { - taunter->Taunt(them, true,0,true,bonus_hate); + taunter->Taunt(them, true, 0, true, bonus_hate); } } - ++it; } } @@ -1014,31 +1029,48 @@ void EntityList::AEBardPulse( } } -// Rampage and stuff for clients. Normal and Duration rampages -//NPCs handle it differently in Mob::Rampage -void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) { -//Dook- Will need tweaking, currently no pets or players or horses - Mob *curmob = nullptr; +/** + * Rampage - Normal and Duration rampages + * NPCs handle it differently in Mob::Rampage + * + * @param attacker + * @param distance + * @param Hand + * @param count + * @param is_from_spell + */ +void EntityList::AEAttack( + Mob *attacker, + float distance, + int Hand, + int count, + bool is_from_spell) +{ + Mob *current_mob = nullptr; + float distance_squared = distance * distance; + int hit_count = 0; - float dist2 = dist * dist; + for (auto &it : entity_list.GetCloseMobList(attacker, distance)) { + current_mob = it.second; - int hit = 0; + if (current_mob->IsNPC() + && current_mob != attacker //this is not needed unless NPCs can use this + && (attacker->IsAttackAllowed(current_mob)) + && current_mob->GetRace() != 216 && current_mob->GetRace() != 472 /* dont attack horses */ + && (DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared) + ) { - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - curmob = it->second; - if (curmob->IsNPC() - && curmob != attacker //this is not needed unless NPCs can use this - &&(attacker->IsAttackAllowed(curmob)) - && curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */ - && (DistanceSquared(curmob->GetPosition(), attacker->GetPosition()) <= dist2) - ) { - if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) - attacker->Attack(curmob, Hand, false, false, IsFromSpell); - else - attacker->CastToClient()->DoAttackRounds(curmob, Hand, IsFromSpell); - hit++; - if (count != 0 && hit >= count) + if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) { + attacker->Attack(current_mob, Hand, false, false, is_from_spell); + } + else { + attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell); + } + + hit_count++; + if (count != 0 && hit_count >= count) { return; + } } } } diff --git a/zone/entity.h b/zone/entity.h index 8e37d5342..5ce1cedda 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -398,10 +398,10 @@ public: void AEAttack( Mob *attacker, - float dist, + float distance, int Hand = EQEmu::invslot::slotPrimary, int count = 0, - bool IsFromSpell = false + bool is_from_spell = false ); void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0); void AESpell( From 54f73d7420b08fe0d6dbe10b9a49ae766e2f1097 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:33:07 -0600 Subject: [PATCH 053/157] Port MassGroupBuff --- zone/effects.cpp | 61 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 0af580524..c6261dae6 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -907,41 +907,64 @@ void EntityList::AESpell( } } -void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) +/** + * @param caster + * @param center + * @param spell_id + * @param affect_caster + */ +void EntityList::MassGroupBuff( + Mob *caster, + Mob *center, + uint16 spell_id, + bool affect_caster) { - Mob *curmob = nullptr; + Mob *current_mob = nullptr; + float distance = caster->GetAOERange(spell_id); + float distance_squared = distance * distance; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); - float dist = caster->GetAOERange(spell_id); - float dist2 = dist * dist; + for (auto &it : entity_list.GetCloseMobList(caster, distance)) { + current_mob = it.second; - bool bad = IsDetrimentalSpell(spell_id); - - for (auto & it : mob_list) { - curmob = it.second; - if (curmob == center) //do not affect center - continue; - if (curmob == caster && !affect_caster) //watch for caster too - continue; - if (DistanceSquared(center->GetPosition(), curmob->GetPosition()) > dist2) //make sure they are in range + /** + * Skip center + */ + if (current_mob == center) { continue; + } - //Only npcs mgb should hit are client pets... - if (curmob->IsNPC()) { - Mob *owner = curmob->GetOwner(); + /** + * Skip self + */ + if (current_mob == caster && !affect_caster) { + continue; + } + + if (DistanceSquared(center->GetPosition(), current_mob->GetPosition()) > distance_squared) { //make sure they are in range + continue; + } + + /** + * Pets + */ + if (current_mob->IsNPC()) { + Mob *owner = current_mob->GetOwner(); if (owner) { if (!owner->IsClient()) { continue; } - } else { + } + else { continue; } } - if (bad) { + if (is_detrimental_spell) { continue; } - caster->SpellOnTarget(spell_id, curmob); + caster->SpellOnTarget(spell_id, current_mob); } } From 59b2d18a950635639397fcb1ff50fb98375f73bb Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 03:37:01 -0600 Subject: [PATCH 054/157] Adjust pointer --- zone/effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index c6261dae6..5528f784a 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -706,7 +706,7 @@ void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate) && taunter->IsAttackAllowed(them) && DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared) { if (taunter->CheckLosFN(them)) { - taunter->Taunt(them, true, 0, true, bonus_hate); + taunter->Taunt(them->CastToNPC(), true, 0, true, bonus_hate); } } } From ccce630cb2b053856f2e11b991d6f68cb564c654 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 13:30:05 -0600 Subject: [PATCH 055/157] Adjustments [skip ci] --- zone/attack.cpp | 3 +++ zone/entity.cpp | 14 +++++++------- zone/mob.cpp | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index cd68f5d57..a48f4008e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2466,6 +2466,9 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil entity_list.UnMarkNPC(GetID()); entity_list.RemoveNPC(GetID()); + // entity_list.RemoveMobFromCloseLists(this); + close_mobs.clear(); + this->SetID(0); if (killer != 0 && emoteid != 0) diff --git a/zone/entity.cpp b/zone/entity.cpp index e20090491..019c921a8 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2567,22 +2567,25 @@ bool EntityList::RemoveNPC(uint16 delete_id) */ bool EntityList::RemoveMobFromCloseLists(Mob *mob) { + uint16 entity_id = mob->GetID() > 0 ? mob->GetID() : mob->GetInitialId(); + LogEntityManagement( "Attempting to remove mob [{}] from close lists entity_id ({})", mob->GetCleanName(), - mob->GetInitialId() + entity_id ); auto it = mob_list.begin(); while (it != mob_list.end()) { + LogEntityManagement( "Removing mob [{}] from [{}] close list entity_id ({})", mob->GetCleanName(), it->second->GetCleanName(), - mob->GetInitialId() + entity_id ); - it->second->close_mobs.erase(mob->GetInitialId()); + it->second->close_mobs.erase(entity_id); ++it; } @@ -2596,7 +2599,6 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob) void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mob *scanning_mob) { float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); - int list_count = 0; close_mobs.clear(); @@ -2607,16 +2609,14 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mo float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); - list_count++; } else if (it->second->GetAggroRange() >= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); - list_count++; } ++it; } - LogAIScanClose("Close List Size [{}] for mob [{}]", list_count, scanning_mob->GetCleanName()); + LogAIScanClose("Close List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); } bool EntityList::RemoveMerc(uint16 delete_id) diff --git a/zone/mob.cpp b/zone/mob.cpp index 6a4c12969..8b8758771 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -496,6 +496,7 @@ Mob::~Mob() if (HasTempPetsActive()) { entity_list.DestroyTempPets(this); } + entity_list.UnMarkNPC(GetID()); UninitializeBuffSlots(); From 78d63165cb7061f80ff8ac9afd85f2c4179fdc4d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 17:10:53 -0600 Subject: [PATCH 056/157] Tweaks [skip ci] --- zone/entity.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/entity.cpp b/zone/entity.cpp index 019c921a8..9c561ce7f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2606,6 +2606,10 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mo while (it != mob_list.end()) { Mob *mob = it->second; + if (!mob->IsNPC() && !mob->IsClient()) { + continue; + } + float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); From 0232a8a188bcbe92952c86f3d954f58ae7c30258 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 17:20:39 -0600 Subject: [PATCH 057/157] More tweaks [skip ci] --- zone/entity.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 9c561ce7f..63059bdfa 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2602,22 +2602,24 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mo close_mobs.clear(); - auto it = mob_list.begin(); - while (it != mob_list.end()) { - Mob *mob = it->second; + for (auto &e : mob_list) { + auto mob = e.second; if (!mob->IsNPC() && !mob->IsClient()) { continue; } - float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition()); + if (mob->GetID() <= 0) { + continue; + } + + float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); } - else if (it->second->GetAggroRange() >= scan_range) { + else if (mob->GetAggroRange() >= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); } - ++it; } LogAIScanClose("Close List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); From a5d41b02b79e40df29327931618ee755a06499e4 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 17:34:50 -0600 Subject: [PATCH 058/157] More tweaks [skip ci] --- zone/client_process.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index e75d53d7c..9678d31cd 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -266,7 +266,11 @@ bool Client::Process() { Mob *mob = itr.second; float distance = DistanceSquared(m_Position, mob->GetPosition()); - if (mob->IsNPC()) { + if (mob->GetID() <= 0) { + continue; + } + + if (mob->IsNPC() || mob->IsClient()) { if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); } From 53a289a6bcfc6cf14ef11b80c505a75ccf134f61 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 29 Dec 2019 21:40:36 -0500 Subject: [PATCH 059/157] Refactor Mob::CastedSpellFinished We now only call GetInv().GetItem() once --- zone/spells.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index cec6e8142..8f787bec4 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -967,6 +967,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo uint16 mana_used, uint32 inventory_slot, int16 resist_adjust) { bool IsFromItem = false; + EQEmu::ItemInstance *item = nullptr; if(IsClient() && slot != CastingSlot::Item && slot != CastingSlot::PotionBelt && spells[spell_id].recast_time > 1000) { // 10 is item if(!CastToClient()->GetPTimers().Expired(&database, pTimerSpellStart + spell_id, false)) { @@ -981,10 +982,10 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)) { IsFromItem = true; - EQEmu::ItemInstance *itm = CastToClient()->GetInv().GetItem(inventory_slot); - if(itm && itm->GetItem()->RecastDelay > 0) + item = CastToClient()->GetInv().GetItem(inventory_slot); + if(item && item->GetItem()->RecastDelay > 0) { - if(!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + itm->GetItem()->RecastType), false)) { + if(!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + item->GetItem()->RecastType), false)) { MessageString(Chat::Red, SPELL_RECAST); LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); StopCasting(); @@ -1298,17 +1299,16 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo && inventory_slot != 0xFFFFFFFF) // 10 is an item { bool fromaug = false; - const EQEmu::ItemInstance* inst = CastToClient()->GetInv()[inventory_slot]; EQEmu::ItemData* augitem = nullptr; uint32 recastdelay = 0; uint32 recasttype = 0; while (true) { - if (inst == nullptr) + if (item == nullptr) break; for (int r = EQEmu::invaug::SOCKET_BEGIN; r <= EQEmu::invaug::SOCKET_END; r++) { - const EQEmu::ItemInstance* aug_i = inst->GetAugment(r); + const EQEmu::ItemInstance* aug_i = item->GetAugment(r); if (!aug_i) continue; @@ -1346,18 +1346,18 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } } - if (inst && inst->IsClassCommon() && (inst->GetItem()->Click.Effect == spell_id) && inst->GetCharges() || fromaug) + if (item && item->IsClassCommon() && (item->GetItem()->Click.Effect == spell_id) && item->GetCharges() || fromaug) { - //const ItemData* item = inst->GetItem(); - int16 charges = inst->GetItem()->MaxCharges; + //const ItemData* item = item->GetItem(); + int16 charges = item->GetItem()->MaxCharges; if(fromaug) { charges = -1; } //Don't destroy the parent item if(charges > -1) { // charged item, expend a charge - LogSpells("Spell [{}]: Consuming a charge from item [{}] ([{}]) which had [{}]/[{}] charges", spell_id, inst->GetItem()->Name, inst->GetItem()->ID, inst->GetCharges(), inst->GetItem()->MaxCharges); + LogSpells("Spell [{}]: Consuming a charge from item [{}] ([{}]) which had [{}]/[{}] charges", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetCharges(), item->GetItem()->MaxCharges); DeleteChargeFromSlot = inventory_slot; } else { - LogSpells("Spell [{}]: Cast from unlimited charge item [{}] ([{}]) ([{}] charges)", spell_id, inst->GetItem()->Name, inst->GetItem()->ID, inst->GetItem()->MaxCharges); + LogSpells("Spell [{}]: Cast from unlimited charge item [{}] ([{}]) ([{}] charges)", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetItem()->MaxCharges); } } else From 67562e3e42bfc139182cf77236c97bca5003ba5a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 29 Dec 2019 20:46:40 -0600 Subject: [PATCH 060/157] Unify scanning rules [skip ci] --- common/ruletypes.h | 1 - zone/client.cpp | 84 ++++++++++++++++++++--------------------- zone/client.h | 2 +- zone/client_process.cpp | 5 +-- 4 files changed, 45 insertions(+), 47 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 5aeaa3021..c6e99e5f4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -567,7 +567,6 @@ RULE_INT(Range, MobPositionUpdates, 600, "") RULE_INT(Range, ClientPositionUpdates, 300, "") RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "") RULE_INT(Range, CriticalDamage, 80, "") -RULE_INT(Range, ClientNPCScan, 300, "") RULE_INT(Range, MobCloseScanDistance, 600, "") RULE_CATEGORY_END() diff --git a/zone/client.cpp b/zone/client.cpp index a99b2818a..f77dc1fd4 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -123,49 +123,49 @@ Client::Client(EQStreamInterface* ieqs) 0, 0 ), - hpupdate_timer(2000), - camp_timer(29000), - process_timer(100), - consume_food_timer(CONSUMPTION_TIMER), - zoneinpacket_timer(1000), - linkdead_timer(RuleI(Zone,ClientLinkdeadMS)), - dead_timer(2000), - global_channel_timer(1000), - shield_timer(500), - fishing_timer(8000), - endupkeep_timer(1000), - forget_timer(0), - autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), - client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000), - client_zone_wide_full_position_update_timer(5 * 60 * 1000), - tribute_timer(Tribute_duration), - proximity_timer(ClientProximity_interval), - TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), - charm_update_timer(6000), - rest_timer(1), - charm_class_attacks_timer(3000), - charm_cast_timer(3500), - qglobal_purge_timer(30000), - TrackingTimer(2000), - RespawnFromHoverTimer(0), - merc_timer(RuleI(Mercs, UpkeepIntervalMS)), - ItemTickTimer(10000), - ItemQuestTimer(500), - anon_toggle_timer(250), - afk_toggle_timer(250), - helm_toggle_timer(250), - aggro_meter_timer(AGGRO_METER_UPDATE_MS), - m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number + hpupdate_timer(2000), + camp_timer(29000), + process_timer(100), + consume_food_timer(CONSUMPTION_TIMER), + zoneinpacket_timer(1000), + linkdead_timer(RuleI(Zone,ClientLinkdeadMS)), + dead_timer(2000), + global_channel_timer(1000), + shield_timer(500), + fishing_timer(8000), + endupkeep_timer(1000), + forget_timer(0), + autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), + client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000), + client_zone_wide_full_position_update_timer(5 * 60 * 1000), + tribute_timer(Tribute_duration), + proximity_timer(ClientProximity_interval), + TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), + charm_update_timer(6000), + rest_timer(1), + charm_class_attacks_timer(3000), + charm_cast_timer(3500), + qglobal_purge_timer(30000), + TrackingTimer(2000), + RespawnFromHoverTimer(0), + merc_timer(RuleI(Mercs, UpkeepIntervalMS)), + ItemTickTimer(10000), + ItemQuestTimer(500), + anon_toggle_timer(250), + afk_toggle_timer(250), + helm_toggle_timer(250), + aggro_meter_timer(AGGRO_METER_UPDATE_MS), + m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f), - m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), - m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), - last_region_type(RegionTypeUnsupported), - m_dirtyautohaters(false), - npc_close_scan_timer(6000), - hp_self_update_throttle_timer(300), - hp_other_update_throttle_timer(500), - position_update_timer(10000), - tmSitting(0) + m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), + m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), + last_region_type(RegionTypeUnsupported), + m_dirtyautohaters(false), + mob_close_scan_timer(6000), + hp_self_update_throttle_timer(300), + hp_other_update_throttle_timer(500), + position_update_timer(10000), + tmSitting(0) { for (int client_filter = 0; client_filter < _FilterCount; client_filter++) diff --git a/zone/client.h b/zone/client.h index 8876e4645..4ff431520 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1523,7 +1523,7 @@ private: Timer afk_toggle_timer; Timer helm_toggle_timer; Timer aggro_meter_timer; - Timer npc_close_scan_timer; + Timer mob_close_scan_timer; 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 */ diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 9678d31cd..14b2036cf 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -253,13 +253,12 @@ bool Client::Process() { /** * Scan close range mobs - * * Used in aggro checks */ - if (npc_close_scan_timer.Check()) { + if (mob_close_scan_timer.Check()) { close_mobs.clear(); - float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan)); + float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); auto &mob_list = entity_list.GetMobList(); for (auto itr : mob_list) { From a7479a628c50e15c6fe7ddf388ffaf478c2e298a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 29 Dec 2019 23:00:42 -0500 Subject: [PATCH 061/157] Use CastTime == 0 on clickies to prevent reagent consumption This /may/ not be correct, but we think it is --- zone/spells.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 8f787bec4..091f1a32c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1151,7 +1151,13 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // first check for component reduction if(IsClient()) { int reg_focus = CastToClient()->GetFocusEffect(focusReagentCost,spell_id);//Client only - if(zone->random.Roll(reg_focus)) { + /* it seems something causes some items not to consume reagents, it's not click type or temp flag + * it maybe cast time being instant, which I had a hard time disproving, so lets do that + * Items that might prove this wrong: Mystic Cloak (1057), Moss Mask (1400), and a bunch others + */ + if (item && item->GetItem() && item->GetItem()->CastTime == 0) { + LogSpells("Spell [{}]: Casted from instant clicky, prevent reagent consumption", spell_id); + } else if(zone->random.Roll(reg_focus)) { LogSpells("Spell [{}]: Reagent focus item prevented reagent consumption ([{}] chance)", spell_id, reg_focus); } else { if(reg_focus > 0) From a9e4d1212ec2d13ebe92d4ee6105a1a94a41b533 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 30 Dec 2019 05:48:42 -0600 Subject: [PATCH 062/157] Update download URL for weekly dump [skip ci] --- utils/scripts/eqemu_server.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 2f57b822e..658f3709d 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -1693,7 +1693,7 @@ sub fetch_server_dlls { sub fetch_peq_db_full { print "[Install] Downloading latest PEQ Database... Please wait...\n"; - get_remote_file("http://edit.peqtgc.com/weekly/peq_beta.zip", "updates_staged/peq_beta.zip", 1); + get_remote_file("http://edit.projecteq.net/weekly/peq_beta.zip", "updates_staged/peq_beta.zip", 1); print "[Install] Downloaded latest PEQ Database... Extracting...\n"; unzip('updates_staged/peq_beta.zip', 'updates_staged/peq_db/'); my $start_dir = "updates_staged/peq_db"; From d71afda954e3bcbb012568b32a3323ce4749ecd0 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 30 Dec 2019 20:15:33 -0600 Subject: [PATCH 063/157] Optimize cleanup QueueCloseClients [skip ci] --- zone/entity.cpp | 74 +++++++++++++++++++++++++++++++++++-------------- zone/entity.h | 2 +- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 63059bdfa..27288998b 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1583,41 +1583,73 @@ void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *a } } -void EntityList::QueueCloseClients(Mob *sender, const EQApplicationPacket *app, - bool ignore_sender, float dist, Mob *SkipThisMob, bool ackreq, eqFilterType filter) +/** + * @param sender + * @param app + * @param ignore_sender + * @param distance + * @param skipped_mob + * @param is_ack_required + * @param filter + */ +void EntityList::QueueCloseClients( + Mob *sender, + const EQApplicationPacket *app, + bool ignore_sender, + float distance, + Mob *skipped_mob, + bool is_ack_required, + eqFilterType filter +) { if (sender == nullptr) { QueueClients(sender, app, ignore_sender); return; } - if (dist <= 0) - dist = 600; - float dist2 = dist * dist; //pow(dist, 2); + if (distance <= 0) { + distance = 600; + } - auto it = client_list.begin(); - while (it != client_list.end()) { - Client *ent = it->second; + float distance_squared = distance * distance; - if ((!ignore_sender || ent != sender) && (ent != SkipThisMob)) { - eqFilterMode filter2 = ent->GetFilter(filter); - if(ent->Connected() && - (filter == FilterNone - || filter2 == FilterShow - || (filter2 == FilterShowGroupOnly && (sender == ent || - (ent->GetGroup() && ent->GetGroup()->IsGroupMember(sender)))) - || (filter2 == FilterShowSelfOnly && ent == sender)) - && (DistanceSquared(ent->GetPosition(), sender->GetPosition()) <= dist2)) { - ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED); + for (auto &e : GetCloseMobList(sender, distance)) { + Mob *mob = e.second; + + if (!mob->IsClient()) { + continue; + } + + Client *client = mob->CastToClient(); + + if ((!ignore_sender || client != sender) && (client != skipped_mob)) { + + if (DistanceSquared(client->GetPosition(), sender->GetPosition()) <= distance_squared) { + continue; + } + + if (!client->Connected()) { + continue; + } + + eqFilterMode client_filter = client->GetFilter(filter); + if ( + filter == FilterNone || client_filter == FilterShow || + (client_filter == FilterShowGroupOnly && + (sender == client || (client->GetGroup() && client->GetGroup()->IsGroupMember(sender)))) || + (client_filter == FilterShowSelfOnly && client == sender) + ) { + client->QueuePacket(app, is_ack_required, Client::CLIENT_CONNECTED); } } - ++it; } } //sender can be null -void EntityList::QueueClients(Mob *sender, const EQApplicationPacket *app, - bool ignore_sender, bool ackreq) +void EntityList::QueueClients( + Mob *sender, const EQApplicationPacket *app, + bool ignore_sender, bool ackreq +) { auto it = client_list.begin(); while (it != client_list.end()) { diff --git a/zone/entity.h b/zone/entity.h index 5ce1cedda..ea97446a6 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -384,7 +384,7 @@ public: void RemoveFromXTargets(Mob* mob); void RemoveFromAutoXTargets(Mob* mob); void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); - void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float dist=200, Mob* SkipThisMob = 0, bool ackreq = true,eqFilterType filter=FilterNone); + void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float distance=200, Mob* skipped_mob = 0, bool is_ack_required = true, eqFilterType filter=FilterNone); void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = 0, uint8 maxstatus = 0); void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0); From 9a08b1be9303263e7cb72da7148c7029ed64b1a1 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 30 Dec 2019 20:18:44 -0600 Subject: [PATCH 064/157] Flip [skip ci] --- zone/entity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 27288998b..adb29011f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1624,7 +1624,7 @@ void EntityList::QueueCloseClients( if ((!ignore_sender || client != sender) && (client != skipped_mob)) { - if (DistanceSquared(client->GetPosition(), sender->GetPosition()) <= distance_squared) { + if (DistanceSquared(client->GetPosition(), sender->GetPosition()) >= distance_squared) { continue; } From 6e4d9a915d67777092fc9b4b0f08072b61ad5735 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 1 Jan 2020 14:56:15 -0600 Subject: [PATCH 065/157] Fix database update condition loop [skip ci] --- utils/sql/db_update_manifest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 3a6fdc601..e68450e7d 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -397,7 +397,7 @@ 9141|2019_07_10_npc_flymode.sql|SHOW COLUMNS FROM `npc_types` LIKE 'flymode'|empty| 9142|2019_09_02_required_spawn_filter.sql|SHOW COLUMNS FROM `spawnentry` LIKE 'condition_value_filter'|empty| 9143|2019_09_16_account_table_changes.sql|SHOW COLUMNS FROM `account` LIKE 'ls_id'|empty| -9144|2019_11_09_logsys_description_update.sql|SELECT * FROM `logsys_categories`|not_empty| +9144|2019_11_09_logsys_description_update.sql|SELECT * FROM db_version WHERE version >= 9143|empty| 9145|2019_12_24_banned_ips_update.sql|SHOW TABLES LIKE 'Banned_IPs'|not_empty| # Upgrade conditions: From 406b1932063dc4375e9d1528d85e26c3a61903fa Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 1 Jan 2020 19:04:11 -0600 Subject: [PATCH 066/157] Cleanup Perl logging and streamline formatting, tie errors to main Quests logging category live output, add string utils --- common/string_util.h | 29 ++ zone/embparser.cpp | 991 ++++++++++++++++++++++++----------------- zone/embperl.cpp | 16 +- zone/embxs.cpp | 10 +- zone/quest_interface.h | 4 +- 5 files changed, 618 insertions(+), 432 deletions(-) diff --git a/common/string_util.h b/common/string_util.h index 0727b98e2..70aa1d16c 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -38,6 +38,35 @@ const std::string StringFormat(const char* format, ...); const std::string vStringFormat(const char* format, va_list args); std::string implode(std::string glue, std::vector src); +/** + * @param s + * @return + */ +static inline std::string <rim(std::string &s) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); + return s; +} + +/** + * @param s + * @return + */ +static inline std::string &rtrim(std::string &s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + return s; +} + +/** + * @param s + * @return + */ +static inline std::string &trim(std::string &s) +{ + return ltrim(rtrim(s)); +} + template std::string implode(const std::string &glue, const std::pair &encapsulation, const std::vector &src) { diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 8d089b182..920d3a249 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -31,7 +31,7 @@ #include #include -extern Zone* zone; +extern Zone *zone; const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_SAY", @@ -121,108 +121,140 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_COMBINE_VALIDATE", }; -PerlembParser::PerlembParser() : perl(nullptr) { - global_npc_quest_status_ = questUnloaded; - player_quest_status_ = questUnloaded; +PerlembParser::PerlembParser() : perl(nullptr) +{ + global_npc_quest_status_ = questUnloaded; + player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded; } -PerlembParser::~PerlembParser() { +PerlembParser::~PerlembParser() +{ safe_delete(perl); } -void PerlembParser::ReloadQuests() { +void PerlembParser::ReloadQuests() +{ try { - if(perl == nullptr) { + if (perl == nullptr) { perl = new Embperl; - } else { + } + else { perl->Reinit(); } MapFunctions(); } - catch(std::exception &e) { - if(perl != nullptr) { + catch (std::exception &e) { + if (perl != nullptr) { delete perl; perl = nullptr; } - LogInfo("Error re-initializing perlembed: [{}]", e.what()); + LogInfo("Error Re-Initializing PerlEmbed: [{}]", e.what()); throw e.what(); } errors_.clear(); npc_quest_status_.clear(); - global_npc_quest_status_ = questUnloaded; - player_quest_status_ = questUnloaded; + global_npc_quest_status_ = questUnloaded; + player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded; item_quest_status_.clear(); spell_quest_status_.clear(); } -int PerlembParser::EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, EQEmu::ItemInstance* item_inst, Mob* mob, - uint32 extradata, bool global, std::vector *extra_pointers) +int PerlembParser::EventCommon( + QuestEventID event, uint32 objid, const char *data, NPC *npcmob, EQEmu::ItemInstance *item_inst, Mob *mob, + uint32 extradata, bool global, std::vector *extra_pointers +) { - if(!perl) + if (!perl) { return 0; + } - if(event >= _LargestEventID) + if (event >= _LargestEventID) { return 0; + } - bool isPlayerQuest = false; - bool isGlobalPlayerQuest = false; - bool isGlobalNPC = false; - bool isItemQuest = false; - bool isSpellQuest = false; + bool isPlayerQuest = false; + bool isGlobalPlayerQuest = false; + bool isGlobalNPC = false; + bool isItemQuest = false; + bool isSpellQuest = false; std::string package_name; - GetQuestTypes(isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, - event, npcmob, item_inst, mob, global); - GetQuestPackageName(isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, - package_name, event, objid, data, npcmob, item_inst, global); + GetQuestTypes( + isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, + event, npcmob, item_inst, mob, global + ); + GetQuestPackageName( + isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, + package_name, event, objid, data, npcmob, item_inst, global + ); const char *sub_name = QuestEventSubroutines[event]; - if(!perl->SubExists(package_name.c_str(), sub_name)) { + if (!perl->SubExists(package_name.c_str(), sub_name)) { return 0; } int char_id = 0; ExportCharID(package_name, char_id, npcmob, mob); - + /* Check for QGlobal export event enable */ - if (parse->perl_event_export_settings[event].qglobals){ - ExportQGlobals(isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, package_name, npcmob, mob, char_id); + if (parse->perl_event_export_settings[event].qglobals) { + ExportQGlobals( + isPlayerQuest, + isGlobalPlayerQuest, + isGlobalNPC, + isItemQuest, + isSpellQuest, + package_name, + npcmob, + mob, + char_id + ); } /* Check for Mob export event enable */ - if (parse->perl_event_export_settings[event].mob){ - ExportMobVariables(isPlayerQuest, isGlobalPlayerQuest, isGlobalNPC, isItemQuest, isSpellQuest, package_name, mob, npcmob); + if (parse->perl_event_export_settings[event].mob) { + ExportMobVariables( + isPlayerQuest, + isGlobalPlayerQuest, + isGlobalNPC, + isItemQuest, + isSpellQuest, + package_name, + mob, + npcmob + ); } /* Check for Zone export event enable */ - if (parse->perl_event_export_settings[event].zone){ + if (parse->perl_event_export_settings[event].zone) { ExportZoneVariables(package_name); } /* Check for Item export event enable */ - if (parse->perl_event_export_settings[event].item){ + if (parse->perl_event_export_settings[event].item) { ExportItemVariables(package_name, mob); } /* Check for Event export event enable */ - if (parse->perl_event_export_settings[event].event_variables){ + if (parse->perl_event_export_settings[event].event_variables) { ExportEventVariables(package_name, event, objid, data, npcmob, item_inst, mob, extradata, extra_pointers); } - if(isPlayerQuest || isGlobalPlayerQuest){ + if (isPlayerQuest || isGlobalPlayerQuest) { return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr); } - else if(isItemQuest) { + else if (isItemQuest) { return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, item_inst); } - else if(isSpellQuest){ - if(mob) { + else if (isSpellQuest) { + if (mob) { return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr); - } else { + } + else { return SendCommands(package_name.c_str(), sub_name, 0, npcmob, mob, nullptr); } } @@ -231,169 +263,212 @@ int PerlembParser::EventCommon(QuestEventID event, uint32 objid, const char * da } } -int PerlembParser::EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { +int PerlembParser::EventNPC( + QuestEventID evt, NPC *npc, Mob *init, std::string data, uint32 extra_data, + std::vector *extra_pointers +) +{ return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data, false, extra_pointers); } -int PerlembParser::EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { +int PerlembParser::EventGlobalNPC( + QuestEventID evt, NPC *npc, Mob *init, std::string data, uint32 extra_data, + std::vector *extra_pointers +) +{ return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data, true, extra_pointers); } -int PerlembParser::EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { +int PerlembParser::EventPlayer( + QuestEventID evt, Client *client, std::string data, uint32 extra_data, + std::vector *extra_pointers +) +{ return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data, false, extra_pointers); } -int PerlembParser::EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { +int PerlembParser::EventGlobalPlayer( + QuestEventID evt, Client *client, std::string data, uint32 extra_data, + std::vector *extra_pointers +) +{ return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data, true, extra_pointers); } -int PerlembParser::EventItem(QuestEventID evt, Client *client, EQEmu::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { +int PerlembParser::EventItem( + QuestEventID evt, Client *client, EQEmu::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, + std::vector *extra_pointers +) +{ // needs pointer validation on 'item' argument return EventCommon(evt, item->GetID(), nullptr, nullptr, item, client, extra_data, false, extra_pointers); } -int PerlembParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { +int PerlembParser::EventSpell( + QuestEventID evt, NPC *npc, Client *client, uint32 spell_id, uint32 extra_data, + std::vector *extra_pointers +) +{ return EventCommon(evt, 0, itoa(spell_id), npc, nullptr, client, extra_data, false, extra_pointers); } -bool PerlembParser::HasQuestSub(uint32 npcid, QuestEventID evt) { +bool PerlembParser::HasQuestSub(uint32 npcid, QuestEventID evt) +{ std::stringstream package_name; package_name << "qst_npc_" << npcid; - if(!perl) + if (!perl) { return false; + } - if(evt >= _LargestEventID) + if (evt >= _LargestEventID) { return false; + } const char *subname = QuestEventSubroutines[evt]; auto iter = npc_quest_status_.find(npcid); - if(iter == npc_quest_status_.end() || iter->second == QuestFailedToLoad) { + if (iter == npc_quest_status_.end() || iter->second == QuestFailedToLoad) { return false; } - return(perl->SubExists(package_name.str().c_str(), subname)); + return (perl->SubExists(package_name.str().c_str(), subname)); } -bool PerlembParser::HasGlobalQuestSub(QuestEventID evt) { - if(!perl) - return false; - - if(global_npc_quest_status_ != questLoaded) { +bool PerlembParser::HasGlobalQuestSub(QuestEventID evt) +{ + if (!perl) { return false; } - if(evt >= _LargestEventID) + if (global_npc_quest_status_ != questLoaded) { return false; + } + + if (evt >= _LargestEventID) { + return false; + } const char *subname = QuestEventSubroutines[evt]; - return(perl->SubExists("qst_global_npc", subname)); + return (perl->SubExists("qst_global_npc", subname)); } -bool PerlembParser::PlayerHasQuestSub(QuestEventID evt) { - if(!perl) - return false; - - if(player_quest_status_ != questLoaded) { +bool PerlembParser::PlayerHasQuestSub(QuestEventID evt) +{ + if (!perl) { return false; } - if(evt >= _LargestEventID) - return false; - - const char *subname = QuestEventSubroutines[evt]; - - return(perl->SubExists("qst_player", subname)); -} - -bool PerlembParser::GlobalPlayerHasQuestSub(QuestEventID evt) { - if(!perl) - return false; - - if(global_player_quest_status_ != questLoaded) { + if (player_quest_status_ != questLoaded) { return false; } - if(evt >= _LargestEventID) + if (evt >= _LargestEventID) { return false; + } const char *subname = QuestEventSubroutines[evt]; - return(perl->SubExists("qst_global_player", subname)); + return (perl->SubExists("qst_player", subname)); } -bool PerlembParser::SpellHasQuestSub(uint32 spell_id, QuestEventID evt) { +bool PerlembParser::GlobalPlayerHasQuestSub(QuestEventID evt) +{ + if (!perl) { + return false; + } + + if (global_player_quest_status_ != questLoaded) { + return false; + } + + if (evt >= _LargestEventID) { + return false; + } + + const char *subname = QuestEventSubroutines[evt]; + + return (perl->SubExists("qst_global_player", subname)); +} + +bool PerlembParser::SpellHasQuestSub(uint32 spell_id, QuestEventID evt) +{ std::stringstream package_name; package_name << "qst_spell_" << spell_id; - if(!perl) - return false; - - auto iter = spell_quest_status_.find(spell_id); - if(iter == spell_quest_status_.end() || iter->second == QuestFailedToLoad) { + if (!perl) { return false; } - if(evt >= _LargestEventID) + auto iter = spell_quest_status_.find(spell_id); + if (iter == spell_quest_status_.end() || iter->second == QuestFailedToLoad) { return false; + } + + if (evt >= _LargestEventID) { + return false; + } const char *subname = QuestEventSubroutines[evt]; - return(perl->SubExists(package_name.str().c_str(), subname)); + return (perl->SubExists(package_name.str().c_str(), subname)); } -bool PerlembParser::ItemHasQuestSub(EQEmu::ItemInstance *itm, QuestEventID evt) { +bool PerlembParser::ItemHasQuestSub(EQEmu::ItemInstance *itm, QuestEventID evt) +{ std::stringstream package_name; package_name << "qst_item_" << itm->GetID(); - if(!perl) + if (!perl) { return false; + } - if (itm == nullptr) + if (itm == nullptr) { return false; + } - if(evt >= _LargestEventID) + if (evt >= _LargestEventID) { return false; + } const char *subname = QuestEventSubroutines[evt]; auto iter = item_quest_status_.find(itm->GetID()); - if(iter == item_quest_status_.end() || iter->second == QuestFailedToLoad) { + if (iter == item_quest_status_.end() || iter->second == QuestFailedToLoad) { return false; } - return(perl->SubExists(package_name.str().c_str(), subname)); + return (perl->SubExists(package_name.str().c_str(), subname)); } -void PerlembParser::LoadNPCScript(std::string filename, int npc_id) { +void PerlembParser::LoadNPCScript(std::string filename, int npc_id) +{ std::stringstream package_name; package_name << "qst_npc_" << npc_id; - if(!perl) + if (!perl) { return; + } auto iter = npc_quest_status_.find(npc_id); - if(iter != npc_quest_status_.end()) { + if (iter != npc_quest_status_.end()) { return; } try { perl->eval_file(package_name.str().c_str(), filename.c_str()); } - catch(const char *err) - { - std::string error = "Error compiling quest file " + filename; - error += ": "; - error += err; - AddError(error); + catch (std::string e) { + AddError( + fmt::format( + "Error Compiling NPC Quest File [{}] NPC ID [{}] Error [{}]", + filename, + npc_id, + e + ) + ); + npc_quest_status_[npc_id] = questFailedToLoad; return; } @@ -401,23 +476,28 @@ void PerlembParser::LoadNPCScript(std::string filename, int npc_id) { npc_quest_status_[npc_id] = questLoaded; } -void PerlembParser::LoadGlobalNPCScript(std::string filename) { - if(!perl) +void PerlembParser::LoadGlobalNPCScript(std::string filename) +{ + if (!perl) { return; + } - if(global_npc_quest_status_ != questUnloaded) { + if (global_npc_quest_status_ != questUnloaded) { return; } try { perl->eval_file("qst_global_npc", filename.c_str()); } - catch(const char *err) - { - std::string error = "Error compiling quest file " + filename; - error += ": "; - error += err; - AddError(error); + catch (std::string e) { + AddError( + fmt::format( + "Error Compiling Global NPC Quest File [{}] Error [{}]", + filename, + e + ) + ); + global_npc_quest_status_ = questFailedToLoad; return; } @@ -425,23 +505,28 @@ void PerlembParser::LoadGlobalNPCScript(std::string filename) { global_npc_quest_status_ = questLoaded; } -void PerlembParser::LoadPlayerScript(std::string filename) { - if(!perl) +void PerlembParser::LoadPlayerScript(std::string filename) +{ + if (!perl) { return; + } - if(player_quest_status_ != questUnloaded) { + if (player_quest_status_ != questUnloaded) { return; } try { perl->eval_file("qst_player", filename.c_str()); } - catch(const char *err) - { - std::string error = "Error compiling quest file " + filename; - error += ": "; - error += err; - AddError(error); + catch (std::string e) { + AddError( + fmt::format( + "Error Compiling Player Quest File [{}] Error [{}]", + filename, + e + ) + ); + player_quest_status_ = questFailedToLoad; return; } @@ -449,23 +534,28 @@ void PerlembParser::LoadPlayerScript(std::string filename) { player_quest_status_ = questLoaded; } -void PerlembParser::LoadGlobalPlayerScript(std::string filename) { - if(!perl) +void PerlembParser::LoadGlobalPlayerScript(std::string filename) +{ + if (!perl) { return; + } - if(global_player_quest_status_ != questUnloaded) { + if (global_player_quest_status_ != questUnloaded) { return; } try { perl->eval_file("qst_global_player", filename.c_str()); } - catch(const char *err) - { - std::string error = "Error compiling quest file " + filename; - error += ": "; - error += err; - AddError(error); + catch (std::string e) { + AddError( + fmt::format( + "Error Compiling Global Player Quest File [{}] Error [{}]", + filename, + e + ) + ); + global_player_quest_status_ = questFailedToLoad; return; } @@ -473,30 +563,37 @@ void PerlembParser::LoadGlobalPlayerScript(std::string filename) { global_player_quest_status_ = questLoaded; } -void PerlembParser::LoadItemScript(std::string filename, EQEmu::ItemInstance *item) { - if (item == nullptr) +void PerlembParser::LoadItemScript(std::string filename, EQEmu::ItemInstance *item) +{ + if (item == nullptr) { return; + } std::stringstream package_name; package_name << "qst_item_" << item->GetID(); - if(!perl) + if (!perl) { return; + } auto iter = item_quest_status_.find(item->GetID()); - if(iter != item_quest_status_.end()) { + if (iter != item_quest_status_.end()) { return; } try { perl->eval_file(package_name.str().c_str(), filename.c_str()); } - catch(const char *err) - { - std::string error = "Error compiling quest file " + filename; - error += ": "; - error += err; - AddError(error); + catch (std::string e) { + AddError( + fmt::format( + "Error Compiling Item Quest File [{}] Item ID [{}] Error [{}]", + filename, + item->GetID(), + e + ) + ); + item_quest_status_[item->GetID()] = questFailedToLoad; return; } @@ -504,27 +601,33 @@ void PerlembParser::LoadItemScript(std::string filename, EQEmu::ItemInstance *it item_quest_status_[item->GetID()] = questLoaded; } -void PerlembParser::LoadSpellScript(std::string filename, uint32 spell_id) { +void PerlembParser::LoadSpellScript(std::string filename, uint32 spell_id) +{ std::stringstream package_name; package_name << "qst_spell_" << spell_id; - if(!perl) + if (!perl) { return; + } auto iter = spell_quest_status_.find(spell_id); - if(iter != spell_quest_status_.end()) { + if (iter != spell_quest_status_.end()) { return; } try { perl->eval_file(package_name.str().c_str(), filename.c_str()); } - catch(const char *err) - { - std::string error = "Error compiling quest file " + filename; - error += ": "; - error += err; - AddError(error); + catch (std::string e) { + AddError( + fmt::format( + "Error Compiling Spell Quest File [{}] Spell ID [{}] Error [{}]", + filename, + spell_id, + e + ) + ); + spell_quest_status_[spell_id] = questFailedToLoad; return; } @@ -532,13 +635,15 @@ void PerlembParser::LoadSpellScript(std::string filename, uint32 spell_id) { spell_quest_status_[spell_id] = questLoaded; } -void PerlembParser::AddVar(std::string name, std::string val) { +void PerlembParser::AddVar(std::string name, std::string val) +{ vars_[name] = val; } -std::string PerlembParser::GetVar(std::string name) { +std::string PerlembParser::GetVar(std::string name) +{ auto iter = vars_.find(name); - if(iter != vars_.end()) { + if (iter != vars_.end()) { return iter->second; } @@ -547,126 +652,157 @@ std::string PerlembParser::GetVar(std::string name) { void PerlembParser::ExportHash(const char *pkgprefix, const char *hashname, std::map &vals) { - if(!perl) + if (!perl) { return; + } - try - { + try { perl->sethash( std::string(pkgprefix).append("::").append(hashname).c_str(), vals ); - } catch(const char *err) { - std::string error = "Error exporting hash: "; - error += err; - AddError(error); + } catch (std::string e) { + AddError( + fmt::format( + "Error exporting Perl hash [{}]", + e + ) + ); } } -void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, int value) +void PerlembParser::ExportVar(const char *pkgprefix, const char *varname, int value) { - if(!perl) + if (!perl) { return; + } try { perl->seti(std::string(pkgprefix).append("::").append(varname).c_str(), value); - } catch(const char * err) { - std::string error = "Error exporting var: "; - error += err; - AddError(error); + } + catch (std::string e) { + AddError( + fmt::format( + "Error exporting Perl variable [{}]", + e + ) + ); } } -void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, unsigned int value) +void PerlembParser::ExportVar(const char *pkgprefix, const char *varname, unsigned int value) { - if(!perl) + if (!perl) { return; + } try { perl->seti(std::string(pkgprefix).append("::").append(varname).c_str(), value); - } catch(const char * err) { - std::string error = "Error exporting var: "; - error += err; - AddError(error); + } catch (std::string e) { + AddError( + fmt::format( + "Error exporting Perl variable [{}]", + e + ) + ); } } -void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, float value) +void PerlembParser::ExportVar(const char *pkgprefix, const char *varname, float value) { - if(!perl) + if (!perl) { return; + } try { perl->setd(std::string(pkgprefix).append("::").append(varname).c_str(), value); - } catch(const char * err) { - std::string error = "Error exporting var: "; - error += err; - AddError(error); + } catch (std::string e) { + AddError( + fmt::format( + "Error exporting Perl variable [{}]", + e + ) + ); } } -void PerlembParser::ExportVarComplex(const char * pkgprefix, const char *varname, const char *value) +void PerlembParser::ExportVarComplex(const char *pkgprefix, const char *varname, const char *value) { - if(!perl) + if (!perl) { return; - try - { + } + try { perl->eval(std::string("$").append(pkgprefix).append("::").append(varname).append("=").append(value).append(";").c_str()); } - catch(const char * err) - { - std::string error = "Error exporting var: "; - error += err; - AddError(error); + catch (std::string e) { + AddError( + fmt::format( + "Error exporting Perl variable [{}]", + e + ) + ); } } void PerlembParser::ExportVar(const char *pkgprefix, const char *varname, const char *value) { - if(!perl) + if (!perl) { return; + } - try - { + try { perl->setstr(std::string(pkgprefix).append("::").append(varname).c_str(), value); } - catch(const char * err) - { - std::string error = "Error exporting var: "; - error += err; - AddError(error); + catch (std::string e) { + AddError( + fmt::format( + "Error exporting Perl variable [{}]", + e + ) + ); } } -int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, EQEmu::ItemInstance* item_inst) { - if(!perl) +int PerlembParser::SendCommands( + const char *pkgprefix, + const char *event, + uint32 npcid, + Mob *other, + Mob *mob, + EQEmu::ItemInstance *item_inst +) +{ + if (!perl) { return 0; + } int ret_value = 0; - if(mob && mob->IsClient()) + if (mob && mob->IsClient()) { quest_manager.StartQuest(other, mob->CastToClient(), item_inst); - else + } + else { quest_manager.StartQuest(other, nullptr, nullptr); + } try { - std::string cmd = "package " + (std::string)(pkgprefix) + (std::string)(";"); + std::string cmd = "package " + (std::string) (pkgprefix) + (std::string) (";"); perl->eval(cmd.c_str()); #ifdef EMBPERL_XS_CLASSES { - std::string cl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client"; - std::string np = (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc"; - std::string qi = (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem"; - std::string enl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list"; - if(clear_vars_.find(cl) != clear_vars_.end()) { + std::string cl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::client"; + std::string np = (std::string) "$" + (std::string) pkgprefix + (std::string) "::npc"; + std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem"; + std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list"; + if (clear_vars_.find(cl) != clear_vars_.end()) { std::string eval_str = cl; eval_str += " = undef;"; perl->eval(eval_str.c_str()); @@ -697,15 +833,16 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 Client *curc = quest_manager.GetInitiator(); snprintf(namebuf, 64, "%s::client", pkgprefix); SV *client = get_sv(namebuf, true); - if(curc != nullptr) { + if (curc != nullptr) { sv_setref_pv(client, "Client", curc); - } else { + } + else { //clear out the value, mainly to get rid of blessedness sv_setsv(client, _empty_sv); } //only export NPC if it's a npc quest - if(!other->IsClient()){ + if (!other->IsClient()) { NPC *curn = quest_manager.GetNPC(); snprintf(namebuf, 64, "%s::npc", pkgprefix); SV *npc = get_sv(namebuf, true); @@ -713,8 +850,8 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 } //only export QuestItem if it's an item quest - if(item_inst) { - EQEmu::ItemInstance* curi = quest_manager.GetQuestItem(); + if (item_inst) { + EQEmu::ItemInstance *curi = quest_manager.GetQuestItem(); snprintf(namebuf, 64, "%s::questitem", pkgprefix); SV *questitem = get_sv(namebuf, true); sv_setref_pv(questitem, "QuestItem", curi); @@ -730,40 +867,35 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 #ifdef EMBPERL_XS_CLASSES { - std::string cl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client"; - std::string np = (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc"; - std::string qi = (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem"; - std::string enl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list"; - clear_vars_[cl] = 1; - clear_vars_[np] = 1; - clear_vars_[qi] = 1; + std::string cl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::client"; + std::string np = (std::string) "$" + (std::string) pkgprefix + (std::string) "::npc"; + std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem"; + std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list"; + clear_vars_[cl] = 1; + clear_vars_[np] = 1; + clear_vars_[qi] = 1; clear_vars_[enl] = 1; } #endif - } catch(const char * err) { - - //try to reduce some of the console spam... - //todo: tweak this to be more accurate at deciding what to filter (we don't want to gag legit errors) - if(!strstr(err,"Undefined subroutine")) { - std::string error = "Script error: "; - error += pkgprefix; - error += "::"; - error += event; - error += " - "; - if(strlen(err) > 0) - error += err; - AddError(error); - } + } catch (std::string e) { + AddError( + fmt::format( + "Script Error | Package [{}] Event [{}] Error [{}]", + pkgprefix, + event, + trim(e) + ) + ); } quest_manager.EndQuest(); #ifdef EMBPERL_XS_CLASSES - if(!quest_manager.QuestsRunning()) { - auto iter = clear_vars_.begin(); + if (!quest_manager.QuestsRunning()) { + auto iter = clear_vars_.begin(); std::string eval_str; - while(iter != clear_vars_.end()) { + while (iter != clear_vars_.end()) { eval_str += iter->first; eval_str += " = undef;"; ++iter; @@ -773,11 +905,13 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 try { perl->eval(eval_str.c_str()); } - catch (const char * err) { - std::string error = "Script clear error: "; - if (strlen(err) > 0) - error += err; - AddError(error); + catch (std::string e) { + AddError( + fmt::format( + "Script Clear Error | Error [{}]", + e + ) + ); } } #endif @@ -785,198 +919,221 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32 return ret_value; } -void PerlembParser::MapFunctions() { +void PerlembParser::MapFunctions() +{ _empty_sv = newSV(0); perl->eval( - "{" - "package quest;" - "&boot_quest;" //load our quest XS -#ifdef EMBPERL_XS_CLASSES - "package Mob;" - "&boot_Mob;" //load our Mob XS + "{" + "package quest;" + "&boot_quest;" //load our quest XS + #ifdef EMBPERL_XS_CLASSES + "package Mob;" + "&boot_Mob;" //load our Mob XS - "package Client;" - "our @ISA = qw(Mob);" //client inherits mob. - "&boot_Mob;" //load our Mob XS - "&boot_Client;" //load our Client XS + "package Client;" + "our @ISA = qw(Mob);" //client inherits mob. + "&boot_Mob;" //load our Mob XS + "&boot_Client;" //load our Client XS - "package NPC;" - "our @ISA = qw(Mob);" //NPC inherits mob. - "&boot_Mob;" //load our Mob XS - "&boot_NPC;" //load our NPC XS + "package NPC;" + "our @ISA = qw(Mob);" //NPC inherits mob. + "&boot_Mob;" //load our Mob XS + "&boot_NPC;" //load our NPC XS - "package Corpse;" - "our @ISA = qw(Mob);" //Corpse inherits mob. - "&boot_Mob;" //load our Mob XS - "&boot_Corpse;" //load our Mob XS + "package Corpse;" + "our @ISA = qw(Mob);" //Corpse inherits mob. + "&boot_Mob;" //load our Mob XS + "&boot_Corpse;" //load our Mob XS - "package EntityList;" - "&boot_EntityList;" //load our EntityList XS + "package EntityList;" + "&boot_EntityList;" //load our EntityList XS - "package PerlPacket;" - "&boot_PerlPacket;" //load our PerlPacket XS + "package PerlPacket;" + "&boot_PerlPacket;" //load our PerlPacket XS - "package Group;" - "&boot_Group;" //load our Group XS + "package Group;" + "&boot_Group;" //load our Group XS - "package Raid;" - "&boot_Raid;" //load our Raid XS + "package Raid;" + "&boot_Raid;" //load our Raid XS - "package QuestItem;" - "&boot_QuestItem;" // load quest Item XS + "package QuestItem;" + "&boot_QuestItem;" // load quest Item XS - "package HateEntry;" - "&boot_HateEntry;" // load quest Hate XS + "package HateEntry;" + "&boot_HateEntry;" // load quest Hate XS - "package Object;" - "&boot_Object;" // load quest Object XS + "package Object;" + "&boot_Object;" // load quest Object XS - "package Doors;" - "&boot_Doors;" // load quest Doors XS + "package Doors;" + "&boot_Doors;" // load quest Doors XS -#endif - "package main;" - "}" + #endif + "package main;" + "}" ); } -void PerlembParser::GetQuestTypes(bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, - bool &isSpellQuest, QuestEventID event, NPC* npcmob, EQEmu::ItemInstance* item_inst, Mob* mob, bool global) +void PerlembParser::GetQuestTypes( + bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, + bool &isSpellQuest, QuestEventID event, NPC *npcmob, EQEmu::ItemInstance *item_inst, Mob *mob, bool global +) { - if(event == EVENT_SPELL_EFFECT_CLIENT || + if (event == EVENT_SPELL_EFFECT_CLIENT || event == EVENT_SPELL_EFFECT_NPC || event == EVENT_SPELL_BUFF_TIC_CLIENT || event == EVENT_SPELL_BUFF_TIC_NPC || event == EVENT_SPELL_FADE || - event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE) - { + event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE) { isSpellQuest = true; } - else - { - if(!npcmob && mob) { - if(!item_inst) { - if(global) { + else { + if (!npcmob && mob) { + if (!item_inst) { + if (global) { isGlobalPlayerQuest = true; - } else { + } + else { isPlayerQuest = true; } } - else + else { isItemQuest = true; + } } } } -void PerlembParser::GetQuestPackageName(bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, - bool &isSpellQuest, std::string &package_name, QuestEventID event, uint32 objid, const char * data, - NPC* npcmob, EQEmu::ItemInstance* item_inst, bool global) +void PerlembParser::GetQuestPackageName( + bool &isPlayerQuest, + bool &isGlobalPlayerQuest, + bool &isGlobalNPC, + bool &isItemQuest, + bool &isSpellQuest, + std::string &package_name, + QuestEventID event, + uint32 objid, + const char *data, + NPC *npcmob, + EQEmu::ItemInstance *item_inst, + bool global +) { - if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { - if(global) { - isGlobalNPC = true; + if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { + if (global) { + isGlobalNPC = true; package_name = "qst_global_npc"; - } else { + } + else { package_name = "qst_npc_"; package_name += itoa(npcmob->GetNPCTypeID()); } } - else if(isItemQuest) { + else if (isItemQuest) { // need a valid EQEmu::ItemInstance pointer check here..unsure how to cancel this process - const EQEmu::ItemData* item = item_inst->GetItem(); + const EQEmu::ItemData *item = item_inst->GetItem(); package_name = "qst_item_"; package_name += itoa(item->ID); } - else if(isPlayerQuest) { + else if (isPlayerQuest) { package_name = "qst_player"; } - else if(isGlobalPlayerQuest) { + else if (isGlobalPlayerQuest) { package_name = "qst_global_player"; } - else - { + else { package_name = "qst_spell_"; package_name += data; } } -void PerlembParser::ExportCharID(const std::string &package_name, int &char_id, NPC *npcmob, Mob *mob) { +void PerlembParser::ExportCharID(const std::string &package_name, int &char_id, NPC *npcmob, Mob *mob) +{ if (mob && mob->IsClient()) { // some events like waypoint and spawn don't have a player involved char_id = mob->CastToClient()->CharacterID(); - } else { - if(npcmob) - { + } + else { + if (npcmob) { char_id = -static_cast(npcmob->GetNPCTypeID()); // make char id negative npc id as a fudge } - else if(mob && mob->IsNPC()) - { + else if (mob && mob->IsNPC()) { char_id = -static_cast(mob->CastToNPC()->GetNPCTypeID()); // make char id negative npc id as a fudge } } ExportVar(package_name.c_str(), "charid", char_id); } -void PerlembParser::ExportQGlobals(bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, - bool isSpellQuest, std::string &package_name, NPC *npcmob, Mob *mob, int char_id) { +void PerlembParser::ExportQGlobals( + bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, + bool isSpellQuest, std::string &package_name, NPC *npcmob, Mob *mob, int char_id +) +{ //NPC quest - if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) - { + if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { //only export for npcs that are global enabled. - if(npcmob && npcmob->GetQglobal()) - { + if (npcmob && npcmob->GetQglobal()) { std::map globhash; - QGlobalCache *npc_c = nullptr; - QGlobalCache *char_c = nullptr; - QGlobalCache *zone_c = nullptr; + QGlobalCache *npc_c = nullptr; + QGlobalCache *char_c = nullptr; + QGlobalCache *zone_c = nullptr; //retrieve our globals - npc_c = npcmob->GetQGlobals(); - if(mob && mob->IsClient()) + npc_c = npcmob->GetQGlobals(); + if (mob && mob->IsClient()) { char_c = mob->CastToClient()->GetQGlobals(); + } zone_c = zone->GetQGlobals(); - if(!npc_c) - { + if (!npc_c) { npc_c = npcmob->CreateQGlobals(); npc_c->LoadByNPCID(npcmob->GetNPCTypeID()); } - if(!char_c) - { - if(mob && mob->IsClient()) - { + if (!char_c) { + if (mob && mob->IsClient()) { char_c = mob->CastToClient()->CreateQGlobals(); char_c->LoadByCharID(mob->CastToClient()->CharacterID()); } } - if(!zone_c) - { + if (!zone_c) { zone_c = zone->CreateQGlobals(); zone_c->LoadByZoneID(zone->GetZoneID()); zone_c->LoadByGlobalContext(); } std::list globalMap; - if(npc_c) - { - QGlobalCache::Combine(globalMap, npc_c->GetBucket(), npcmob->GetNPCTypeID(), char_id, zone->GetZoneID()); + if (npc_c) { + QGlobalCache::Combine( + globalMap, + npc_c->GetBucket(), + npcmob->GetNPCTypeID(), + char_id, + zone->GetZoneID()); } - if(char_c) - { - QGlobalCache::Combine(globalMap, char_c->GetBucket(), npcmob->GetNPCTypeID(), char_id, zone->GetZoneID()); + if (char_c) { + QGlobalCache::Combine( + globalMap, + char_c->GetBucket(), + npcmob->GetNPCTypeID(), + char_id, + zone->GetZoneID()); } - if(zone_c) - { - QGlobalCache::Combine(globalMap, zone_c->GetBucket(), npcmob->GetNPCTypeID(), char_id, zone->GetZoneID()); + if (zone_c) { + QGlobalCache::Combine( + globalMap, + zone_c->GetBucket(), + npcmob->GetNPCTypeID(), + char_id, + zone->GetZoneID()); } auto iter = globalMap.begin(); - while(iter != globalMap.end()) - { + while (iter != globalMap.end()) { globhash[(*iter).name] = (*iter).value; ExportVar(package_name.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); ++iter; @@ -984,47 +1141,41 @@ void PerlembParser::ExportQGlobals(bool isPlayerQuest, bool isGlobalPlayerQuest, ExportHash(package_name.c_str(), "qglobals", globhash); } } - else - { + else { std::map globhash; - QGlobalCache *char_c = nullptr; - QGlobalCache *zone_c = nullptr; + QGlobalCache *char_c = nullptr; + QGlobalCache *zone_c = nullptr; //retrieve our globals - if(mob && mob->IsClient()) + if (mob && mob->IsClient()) { char_c = mob->CastToClient()->GetQGlobals(); - zone_c = zone->GetQGlobals(); + } + zone_c = zone->GetQGlobals(); - if(!char_c) - { - if(mob && mob->IsClient()) - { + if (!char_c) { + if (mob && mob->IsClient()) { char_c = mob->CastToClient()->CreateQGlobals(); char_c->LoadByCharID(mob->CastToClient()->CharacterID()); } } - if(!zone_c) - { + if (!zone_c) { zone_c = zone->CreateQGlobals(); zone_c->LoadByZoneID(zone->GetZoneID()); zone_c->LoadByGlobalContext(); } std::list globalMap; - if(char_c) - { + if (char_c) { QGlobalCache::Combine(globalMap, char_c->GetBucket(), 0, char_id, zone->GetZoneID()); } - if(zone_c) - { + if (zone_c) { QGlobalCache::Combine(globalMap, zone_c->GetBucket(), 0, char_id, zone->GetZoneID()); } auto iter = globalMap.begin(); - while(iter != globalMap.end()) - { + while (iter != globalMap.end()) { globhash[(*iter).name] = (*iter).value; ExportVar(package_name.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); ++iter; @@ -1033,8 +1184,10 @@ void PerlembParser::ExportQGlobals(bool isPlayerQuest, bool isGlobalPlayerQuest, } } -void PerlembParser::ExportMobVariables(bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, - bool isSpellQuest, std::string &package_name, Mob *mob, NPC *npcmob) +void PerlembParser::ExportMobVariables( + bool isPlayerQuest, bool isGlobalPlayerQuest, bool isGlobalNPC, bool isItemQuest, + bool isSpellQuest, std::string &package_name, Mob *mob, NPC *npcmob +) { uint8 fac = 0; if (mob && mob->IsClient()) { @@ -1043,16 +1196,18 @@ void PerlembParser::ExportMobVariables(bool isPlayerQuest, bool isGlobalPlayerQu ExportVar(package_name.c_str(), "status", mob->CastToClient()->Admin()); } - if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest) { + if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest) { if (mob && npcmob && mob->IsClient()) { - Client* client = mob->CastToClient(); + Client *client = mob->CastToClient(); - fac = client->GetFactionLevel(client->CharacterID(), npcmob->GetID(), client->GetFactionRace(), - client->GetClass(), client->GetDeity(), npcmob->GetPrimaryFaction(), npcmob); + fac = client->GetFactionLevel( + client->CharacterID(), npcmob->GetID(), client->GetFactionRace(), + client->GetClass(), client->GetDeity(), npcmob->GetPrimaryFaction(), npcmob + ); } } - if(mob) { + if (mob) { ExportVar(package_name.c_str(), "name", mob->GetName()); ExportVar(package_name.c_str(), "race", GetRaceIDName(mob->GetRace())); ExportVar(package_name.c_str(), "class", GetClassIDName(mob->GetClass())); @@ -1060,19 +1215,17 @@ void PerlembParser::ExportMobVariables(bool isPlayerQuest, bool isGlobalPlayerQu ExportVar(package_name.c_str(), "userid", mob->GetID()); } - if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) - { - if (npcmob) - { + if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { + if (npcmob) { ExportVar(package_name.c_str(), "mname", npcmob->GetName()); ExportVar(package_name.c_str(), "mobid", npcmob->GetID()); ExportVar(package_name.c_str(), "mlevel", npcmob->GetLevel()); - ExportVar(package_name.c_str(), "hpratio",npcmob->GetHPRatio()); - ExportVar(package_name.c_str(), "x", npcmob->GetX() ); - ExportVar(package_name.c_str(), "y", npcmob->GetY() ); - ExportVar(package_name.c_str(), "z", npcmob->GetZ() ); - ExportVar(package_name.c_str(), "h", npcmob->GetHeading() ); - if(npcmob->GetTarget()) { + ExportVar(package_name.c_str(), "hpratio", npcmob->GetHPRatio()); + ExportVar(package_name.c_str(), "x", npcmob->GetX()); + ExportVar(package_name.c_str(), "y", npcmob->GetY()); + ExportVar(package_name.c_str(), "z", npcmob->GetZ()); + ExportVar(package_name.c_str(), "h", npcmob->GetHeading()); + if (npcmob->GetTarget()) { ExportVar(package_name.c_str(), "targetid", npcmob->GetTarget()->GetID()); ExportVar(package_name.c_str(), "targetname", npcmob->GetTarget()->GetName()); } @@ -1084,7 +1237,8 @@ void PerlembParser::ExportMobVariables(bool isPlayerQuest, bool isGlobalPlayerQu } } -void PerlembParser::ExportZoneVariables(std::string &package_name) { +void PerlembParser::ExportZoneVariables(std::string &package_name) +{ if (zone) { ExportVar(package_name.c_str(), "zoneid", zone->GetZoneID()); ExportVar(package_name.c_str(), "zoneln", zone->GetLongName()); @@ -1092,7 +1246,7 @@ void PerlembParser::ExportZoneVariables(std::string &package_name) { ExportVar(package_name.c_str(), "instanceid", zone->GetInstanceID()); ExportVar(package_name.c_str(), "instanceversion", zone->GetInstanceVersion()); TimeOfDay_Struct eqTime; - zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); + zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eqTime); ExportVar(package_name.c_str(), "zonehour", eqTime.hour - 1); ExportVar(package_name.c_str(), "zonemin", eqTime.minute); ExportVar(package_name.c_str(), "zonetime", (eqTime.hour - 1) * 100 + eqTime.minute); @@ -1100,20 +1254,18 @@ void PerlembParser::ExportZoneVariables(std::string &package_name) { } } -void PerlembParser::ExportItemVariables(std::string &package_name, Mob *mob) { - if(mob && mob->IsClient()) - { +void PerlembParser::ExportItemVariables(std::string &package_name, Mob *mob) +{ + if (mob && mob->IsClient()) { std::string hashname = package_name + std::string("::hasitem"); //start with an empty hash perl->eval(std::string("%").append(hashname).append(" = ();").c_str()); - for(int slot = EQEmu::invslot::EQUIPMENT_BEGIN; slot <= EQEmu::invslot::GENERAL_END; slot++) - { - char *hi_decl=nullptr; - int itemid = mob->CastToClient()->GetItemIDAt(slot); - if(itemid != -1 && itemid != 0) - { + for (int slot = EQEmu::invslot::EQUIPMENT_BEGIN; slot <= EQEmu::invslot::GENERAL_END; slot++) { + char *hi_decl = nullptr; + int itemid = mob->CastToClient()->GetItemIDAt(slot); + if (itemid != -1 && itemid != 0) { MakeAnyLenString(&hi_decl, "push (@{$%s{%d}},%d);", hashname.c_str(), itemid, slot); perl->eval(hi_decl); safe_delete_array(hi_decl); @@ -1121,25 +1273,27 @@ void PerlembParser::ExportItemVariables(std::string &package_name, Mob *mob) { } } - if(mob && mob->IsClient()) { + if (mob && mob->IsClient()) { std::string hashname = package_name + std::string("::oncursor"); perl->eval(std::string("%").append(hashname).append(" = ();").c_str()); char *hi_decl = nullptr; - int itemid = mob->CastToClient()->GetItemIDAt(EQEmu::invslot::slotCursor); - if(itemid != -1 && itemid != 0) { - MakeAnyLenString(&hi_decl, "push (@{$%s{%d}},%d);",hashname.c_str(), itemid, EQEmu::invslot::slotCursor); + int itemid = mob->CastToClient()->GetItemIDAt(EQEmu::invslot::slotCursor); + if (itemid != -1 && itemid != 0) { + MakeAnyLenString(&hi_decl, "push (@{$%s{%d}},%d);", hashname.c_str(), itemid, EQEmu::invslot::slotCursor); perl->eval(hi_decl); safe_delete_array(hi_decl); } } } -void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID event, uint32 objid, const char * data, - NPC* npcmob, EQEmu::ItemInstance* item_inst, Mob* mob, uint32 extradata, std::vector *extra_pointers) +void PerlembParser::ExportEventVariables( + std::string &package_name, QuestEventID event, uint32 objid, const char *data, + NPC *npcmob, EQEmu::ItemInstance *item_inst, Mob *mob, uint32 extradata, std::vector *extra_pointers +) { switch (event) { case EVENT_SAY: { - if(npcmob && mob) { + if (npcmob && mob) { npcmob->DoQuestPause(mob); } @@ -1150,15 +1304,15 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID } case EVENT_TRADE: { - if(extra_pointers) { - size_t sz = extra_pointers->size(); - for(size_t i = 0; i < sz; ++i) { - EQEmu::ItemInstance *inst = EQEmu::any_cast(extra_pointers->at(i)); + if (extra_pointers) { + size_t sz = extra_pointers->size(); + for (size_t i = 0; i < sz; ++i) { + EQEmu::ItemInstance *inst = EQEmu::any_cast(extra_pointers->at(i)); std::string var_name = "item"; var_name += std::to_string(i + 1); - if(inst) { + if (inst) { ExportVar(package_name.c_str(), var_name.c_str(), inst->GetItem()->ID); std::string temp_var_name = var_name; @@ -1168,7 +1322,8 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID temp_var_name = var_name; temp_var_name += "_attuned"; ExportVar(package_name.c_str(), temp_var_name.c_str(), inst->IsAttuned()); - } else { + } + else { ExportVar(package_name.c_str(), var_name.c_str(), 0); std::string temp_var_name = var_name; @@ -1206,8 +1361,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID ExportVar(package_name.c_str(), "hpevent", "-1"); ExportVar(package_name.c_str(), "inchpevent", data); } - else - { + else { ExportVar(package_name.c_str(), "hpevent", data); ExportVar(package_name.c_str(), "inchpevent", "-1"); } @@ -1248,7 +1402,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID break; } - case EVENT_ZONE:{ + case EVENT_ZONE: { ExportVar(package_name.c_str(), "target_zone_id", data); break; } @@ -1260,26 +1414,26 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID break; } - case EVENT_TASK_ACCEPTED:{ + case EVENT_TASK_ACCEPTED: { ExportVar(package_name.c_str(), "task_id", data); break; } - case EVENT_TASK_STAGE_COMPLETE:{ + case EVENT_TASK_STAGE_COMPLETE: { Seperator sep(data); ExportVar(package_name.c_str(), "task_id", sep.arg[0]); ExportVar(package_name.c_str(), "activity_id", sep.arg[1]); break; } - case EVENT_TASK_FAIL:{ + case EVENT_TASK_FAIL: { Seperator sep(data); ExportVar(package_name.c_str(), "task_id", sep.arg[0]); break; } case EVENT_TASK_COMPLETE: - case EVENT_TASK_UPDATE:{ + case EVENT_TASK_UPDATE: { Seperator sep(data); ExportVar(package_name.c_str(), "donecount", sep.arg[0]); ExportVar(package_name.c_str(), "activity_id", sep.arg[1]); @@ -1287,7 +1441,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID break; } - case EVENT_PLAYER_PICKUP:{ + case EVENT_PLAYER_PICKUP: { ExportVar(package_name.c_str(), "picked_up_id", data); ExportVar(package_name.c_str(), "picked_up_entity_id", extradata); break; @@ -1300,11 +1454,11 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID break; } - case EVENT_POPUP_RESPONSE:{ + case EVENT_POPUP_RESPONSE: { ExportVar(package_name.c_str(), "popupid", data); break; } - case EVENT_ENVIRONMENTAL_DAMAGE:{ + case EVENT_ENVIRONMENTAL_DAMAGE: { Seperator sep(data); ExportVar(package_name.c_str(), "env_damage", sep.arg[0]); ExportVar(package_name.c_str(), "env_damage_type", sep.arg[1]); @@ -1338,8 +1492,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID } case EVENT_GROUP_CHANGE: { - if(mob && mob->IsClient()) - { + if (mob && mob->IsClient()) { ExportVar(package_name.c_str(), "grouped", mob->IsGrouped()); ExportVar(package_name.c_str(), "raided", mob->IsRaidGrouped()); } @@ -1354,16 +1507,14 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID case EVENT_SPELL_EFFECT_CLIENT: case EVENT_SPELL_EFFECT_NPC: case EVENT_SPELL_BUFF_TIC_CLIENT: - case EVENT_SPELL_BUFF_TIC_NPC: - { + case EVENT_SPELL_BUFF_TIC_NPC: { ExportVar(package_name.c_str(), "caster_id", extradata); break; } - //tradeskill events + //tradeskill events case EVENT_COMBINE_SUCCESS: - case EVENT_COMBINE_FAILURE: - { + case EVENT_COMBINE_FAILURE: { ExportVar(package_name.c_str(), "recipe_id", extradata); ExportVar(package_name.c_str(), "recipe_name", data); break; @@ -1435,7 +1586,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID ExportVar(package_name.c_str(), "killed_npc_id", sep.arg[4]); break; } - case EVENT_USE_SKILL:{ + case EVENT_USE_SKILL: { Seperator sep(data); ExportVar(package_name.c_str(), "skill_id", sep.arg[0]); ExportVar(package_name.c_str(), "skill_level", sep.arg[1]); @@ -1446,7 +1597,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID ExportVar(package_name.c_str(), "recipe_id", extradata); ExportVar(package_name.c_str(), "validate_type", sep.arg[0]); - std::string zone_id = "-1"; + std::string zone_id = "-1"; std::string tradeskill_id = "-1"; if (strcmp(sep.arg[0], "check_zone") == 0) { zone_id = sep.arg[1]; @@ -1454,7 +1605,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID else if (strcmp(sep.arg[0], "check_tradeskill") == 0) { tradeskill_id = sep.arg[1]; } - + ExportVar(package_name.c_str(), "zone_id", zone_id.c_str()); ExportVar(package_name.c_str(), "tradeskill_id", tradeskill_id.c_str()); break; diff --git a/zone/embperl.cpp b/zone/embperl.cpp index 22c248fd7..c7ebb8060 100644 --- a/zone/embperl.cpp +++ b/zone/embperl.cpp @@ -137,10 +137,10 @@ void Embperl::DoInit() { try { init_eval_file(); } - catch(const char *err) + catch(std::string e) { //remember... lasterr() is no good if we crap out here, in construction - LogQuests("perl error: [{}]", err); + LogQuests("Perl Error [{}]", e); throw "failed to install eval_file hook"; } @@ -177,9 +177,9 @@ void Embperl::DoInit() { perl_command = "main::eval_file('plugin', '" + Config->PluginPlFile + "');"; eval_pv(perl_command.c_str(), FALSE); } - catch(const char *err) + catch(std::string e) { - LogQuests("Warning - [{}]: [{}]", Config->PluginPlFile.c_str(), err); + LogQuests("Warning [{}]: [{}]", Config->PluginPlFile, e); } try { @@ -195,9 +195,9 @@ void Embperl::DoInit() { "}"; eval_pv(perl_command.c_str(),FALSE); } - catch(const char *err) + catch(std::string e) { - LogQuests("Perl warning: [{}]", err); + LogQuests("Warning [{}]", e); } #endif //EMBPERL_PLUGIN in_use = false; @@ -237,7 +237,7 @@ void Embperl::init_eval_file(void) { eval_pv( "our %Cache;" - "no warnings;" + "no warnings 'all';" "use Symbol qw(delete_package);" "sub eval_file {" "my($package, $filename) = @_;" @@ -315,7 +315,7 @@ int Embperl::dosub(const char * subname, const std::vector * args, { std::string errmsg = "Perl runtime error: "; errmsg += SvPVX(ERRSV); - throw errmsg.c_str(); + throw errmsg; } return ret_value; diff --git a/zone/embxs.cpp b/zone/embxs.cpp index b9013335b..59bfb333a 100644 --- a/zone/embxs.cpp +++ b/zone/embxs.cpp @@ -100,15 +100,19 @@ XS(XS_EQEmuIO_PRINT) *std::remove(str, str + strlen(str), '\n') = '\0'; std::string log_string = str; + if (log_string.find("did not return a true") != std::string::npos) - return;; + return; + + if (log_string.find("is experimental") != std::string::npos) + return; int i; int pos = 0; int len = 0; for(i = 0; *cur != '\0'; i++, cur++) { if(*cur == '\n') { - Log(Logs::General, Logs::Quests, str); + LogQuests(str); len = 0; pos = i+1; } else { @@ -116,7 +120,7 @@ XS(XS_EQEmuIO_PRINT) } } if(len > 0) { - Log(Logs::General, Logs::Quests, str); + LogQuests(str); } } diff --git a/zone/quest_interface.h b/zone/quest_interface.h index 36ca9458f..4b5183cfb 100644 --- a/zone/quest_interface.h +++ b/zone/quest_interface.h @@ -84,7 +84,9 @@ public: err.insert(err.end(), errors_.begin(), errors_.end()); } - virtual void AddError(std::string error) { + virtual void AddError(std::string error) { + LogQuests(error); + errors_.push_back(error); if(errors_.size() > 30) { errors_.pop_front(); From 689493610f81ed8fcf8da794d720fd6a1ab7e8b2 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 1 Jan 2020 19:16:33 -0600 Subject: [PATCH 067/157] Make windows happy? --- common/string_util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/string_util.h b/common/string_util.h index 70aa1d16c..50ac60d64 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -25,6 +25,7 @@ #ifndef _WIN32 // this doesn't appear to affect linux-based systems..need feedback for _WIN64 #include +#include #endif #include "types.h" From d50e5855c4382da4ad51a89e557174e66796f695 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 1 Jan 2020 20:57:42 -0500 Subject: [PATCH 068/157] There, there windows..it'll be ok... --- common/string_util.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/string_util.h b/common/string_util.h index 50ac60d64..6582ef975 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -25,7 +25,11 @@ #ifndef _WIN32 // this doesn't appear to affect linux-based systems..need feedback for _WIN64 #include -#include +#endif + +#ifdef _WINDOWS +#include +#include #endif #include "types.h" From e7ad57a37ef0ce7a689fc0d529eb0f8025505ceb Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 1 Jan 2020 21:12:09 -0500 Subject: [PATCH 069/157] Resistance is futile! --- common/string_util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/string_util.h b/common/string_util.h index 6582ef975..78fbe66f2 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -28,6 +28,7 @@ #endif #ifdef _WINDOWS +#include #include #include #endif From 69d887b421bcb4bfaba0d39b62f87cdce4fbcabe Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 1 Jan 2020 20:47:58 -0600 Subject: [PATCH 070/157] Use something a little more compatibility friendly --- common/string_util.h | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/common/string_util.h b/common/string_util.h index 78fbe66f2..037d6a2d8 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -45,32 +45,35 @@ const std::string vStringFormat(const char* format, va_list args); std::string implode(std::string glue, std::vector src); /** - * @param s + * @param str + * @param chars * @return */ -static inline std::string <rim(std::string &s) +inline std::string <rim(std::string &str, const std::string &chars = "\t\n\v\f\r ") { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); - return s; + str.erase(0, str.find_first_not_of(chars)); + return str; } /** - * @param s + * @param str + * @param chars * @return */ -static inline std::string &rtrim(std::string &s) +inline std::string &rtrim(std::string &str, const std::string &chars = "\t\n\v\f\r ") { - s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); - return s; + str.erase(str.find_last_not_of(chars) + 1); + return str; } /** - * @param s + * @param str + * @param chars * @return */ -static inline std::string &trim(std::string &s) +inline std::string &trim(std::string &str, const std::string &chars = "\t\n\v\f\r ") { - return ltrim(rtrim(s)); + return ltrim(rtrim(str, chars), chars); } template From 61d1c2d75c24a3d5247b896393163c3a18b00d7f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Thu, 2 Jan 2020 01:08:36 -0600 Subject: [PATCH 071/157] Update legacy_combat.lua to be more accurate, better logging --- utils/mods/legacy_combat.lua | 778 +++++++++++++++++++++++------------ 1 file changed, 512 insertions(+), 266 deletions(-) diff --git a/utils/mods/legacy_combat.lua b/utils/mods/legacy_combat.lua index 9ab70cf4d..9baf918a8 100644 --- a/utils/mods/legacy_combat.lua +++ b/utils/mods/legacy_combat.lua @@ -1,110 +1,147 @@ -MonkACBonusWeight = RuleI.Get(Rule.MonkACBonusWeight); -NPCACFactor = RuleR.Get(Rule.NPCACFactor); -OldACSoftcapRules = RuleB.Get(Rule.OldACSoftcapRules); -ClothACSoftcap = RuleI.Get(Rule.ClothACSoftcap); -LeatherACSoftcap = RuleI.Get(Rule.LeatherACSoftcap); -MonkACSoftcap = RuleI.Get(Rule.MonkACSoftcap); -ChainACSoftcap = RuleI.Get(Rule.ChainACSoftcap); -PlateACSoftcap = RuleI.Get(Rule.PlateACSoftcap); +--[[ + * + * The purpose of this lua file is to backport combat formulas pre-overhaul + * https://github.com/EQEmu/Server/commit/9e824876ba5dac262b121c0e60d022bb2ecc45bd + * + * If your server has years and years of content built on old formulas, it may be appropriate to use this + * instead of taking on the massive task of rescaling tons of carefully tested and layered content + * +]] -AAMitigationACFactor = RuleR.Get(Rule.AAMitigationACFactor); -WarriorACSoftcapReturn = RuleR.Get(Rule.WarriorACSoftcapReturn); -KnightACSoftcapReturn = RuleR.Get(Rule.KnightACSoftcapReturn); -LowPlateChainACSoftcapReturn = RuleR.Get(Rule.LowPlateChainACSoftcapReturn); +MonkACBonusWeight = RuleI.Get(Rule.MonkACBonusWeight); +NPCACFactor = RuleR.Get(Rule.NPCACFactor); +OldACSoftcapRules = RuleB.Get(Rule.OldACSoftcapRules); +ClothACSoftcap = RuleI.Get(Rule.ClothACSoftcap); +LeatherACSoftcap = RuleI.Get(Rule.LeatherACSoftcap); +MonkACSoftcap = RuleI.Get(Rule.MonkACSoftcap); +ChainACSoftcap = RuleI.Get(Rule.ChainACSoftcap); +PlateACSoftcap = RuleI.Get(Rule.PlateACSoftcap); +AAMitigationACFactor = RuleR.Get(Rule.AAMitigationACFactor); +WarriorACSoftcapReturn = RuleR.Get(Rule.WarriorACSoftcapReturn); +KnightACSoftcapReturn = RuleR.Get(Rule.KnightACSoftcapReturn); +LowPlateChainACSoftcapReturn = RuleR.Get(Rule.LowPlateChainACSoftcapReturn); LowChainLeatherACSoftcapReturn = RuleR.Get(Rule.LowChainLeatherACSoftcapReturn); -CasterACSoftcapReturn = RuleR.Get(Rule.CasterACSoftcapReturn); -MiscACSoftcapReturn = RuleR.Get(Rule.MiscACSoftcapReturn); -WarACSoftcapReturn = RuleR.Get(Rule.WarACSoftcapReturn); -ClrRngMnkBrdACSoftcapReturn = RuleR.Get(Rule.ClrRngMnkBrdACSoftcapReturn); -PalShdACSoftcapReturn = RuleR.Get(Rule.PalShdACSoftcapReturn); +CasterACSoftcapReturn = RuleR.Get(Rule.CasterACSoftcapReturn); +MiscACSoftcapReturn = RuleR.Get(Rule.MiscACSoftcapReturn); +WarACSoftcapReturn = RuleR.Get(Rule.WarACSoftcapReturn); +ClrRngMnkBrdACSoftcapReturn = RuleR.Get(Rule.ClrRngMnkBrdACSoftcapReturn); +PalShdACSoftcapReturn = RuleR.Get(Rule.PalShdACSoftcapReturn); DruNecWizEncMagACSoftcapReturn = RuleR.Get(Rule.DruNecWizEncMagACSoftcapReturn); -RogShmBstBerACSoftcapReturn = RuleR.Get(Rule.RogShmBstBerACSoftcapReturn); -SoftcapFactor = RuleR.Get(Rule.SoftcapFactor); -ACthac0Factor = RuleR.Get(Rule.ACthac0Factor); -ACthac20Factor = RuleR.Get(Rule.ACthac20Factor); +RogShmBstBerACSoftcapReturn = RuleR.Get(Rule.RogShmBstBerACSoftcapReturn); +SoftcapFactor = RuleR.Get(Rule.SoftcapFactor); +ACthac0Factor = RuleR.Get(Rule.ACthac0Factor); +ACthac20Factor = RuleR.Get(Rule.ACthac20Factor); +BaseHitChance = RuleR.Get(Rule.BaseHitChance); +NPCBonusHitChance = RuleR.Get(Rule.NPCBonusHitChance); +HitFalloffMinor = RuleR.Get(Rule.HitFalloffMinor); +HitFalloffModerate = RuleR.Get(Rule.HitFalloffModerate); +HitFalloffMajor = RuleR.Get(Rule.HitFalloffMajor); +HitBonusPerLevel = RuleR.Get(Rule.HitBonusPerLevel); +AgiHitFactor = RuleR.Get(Rule.AgiHitFactor); +WeaponSkillFalloff = RuleR.Get(Rule.WeaponSkillFalloff); +ArcheryHitPenalty = RuleR.Get(Rule.ArcheryHitPenalty); +UseOldDamageIntervalRules = RuleB.Get(Rule.UseOldDamageIntervalRules); +CriticalMessageRange = RuleI.Get(Rule.CriticalDamage); -MeleeBaseCritChance = 0.0; -ClientBaseCritChance = 0.0; -BerserkBaseCritChance = 6.0; -WarBerBaseCritChance = 3.0; -RogueCritThrowingChance = 25; -RogueDeadlyStrikeChance = 80; -RogueDeadlyStrikeMod = 2; +--[[ + * + * These are rule values that were removed in the latest combat overhaul, if you are coming from + * older source code, you will need to reference what your rule values were for these variables + * to make sure that things line up correctly + * +]] -BaseHitChance = RuleR.Get(Rule.BaseHitChance); -NPCBonusHitChance = RuleR.Get(Rule.NPCBonusHitChance); -HitFalloffMinor = RuleR.Get(Rule.HitFalloffMinor); -HitFalloffModerate = RuleR.Get(Rule.HitFalloffModerate); -HitFalloffMajor = RuleR.Get(Rule.HitFalloffMajor); -HitBonusPerLevel = RuleR.Get(Rule.HitBonusPerLevel); -AgiHitFactor = RuleR.Get(Rule.AgiHitFactor); -WeaponSkillFalloff = RuleR.Get(Rule.WeaponSkillFalloff); -ArcheryHitPenalty = RuleR.Get(Rule.ArcheryHitPenalty); -UseOldDamageIntervalRules = RuleB.Get(Rule.UseOldDamageIntervalRules); - -CriticalMessageRange = RuleI.Get(Rule.CriticalDamage); +MeleeBaseCritChance = 0.0; +ClientBaseCritChance = 0.0; +BerserkBaseCritChance = 6.0; +WarBerBaseCritChance = 3.0; +RogueCritThrowingChance = 25; +RogueDeadlyStrikeChance = 80; +RogueDeadlyStrikeMod = 2; +-- Source Function: Mob::MeleeMitigation() +-- Partial: Rest happens in DoMeleeMitigation function MeleeMitigation(e) e.IgnoreDefault = true; - + if e.hit.damage_done < 0 or e.hit.base_damage == 0 then return e; end - + + eq.log_combat( + string.format("[%s] [ClientAttack] Damage Table [%i] WeaponDMG [%i]", + e.self:GetCleanName(), + GetDamageTable(e.other, e.hit.skill), + e.hit.base_damage + ) + ); + e.hit.damage_done = 2 * e.hit.base_damage * GetDamageTable(e.other, e.hit.skill) / 100; - e.hit = DoMeleeMitigation(e.self, e.other, e.hit, e.opts); + + eq.log_combat( + string.format("[%s] [ClientAttack] DamageDone [%i] BaseDamage [%i] HitSkill [%i]", + e.self:GetCleanName(), + e.hit.damage_done, + e.hit.base_damage, + e.hit.skill + ) + ); + + e.hit = DoMeleeMitigation(e.self, e.other, e.hit, e.opts); + return e; end +-- Source Function: Mob::CheckHitChance() function CheckHitChance(e) - e.IgnoreDefault = true; - - local other = e.other; - local attacker = other; - local self = e.self; - local defender = self; + e.IgnoreDefault = true; + + local other = e.other; + local attacker = other; + local self = e.self; + local defender = self; local chancetohit = BaseHitChance; - local chance_mod = 0; - - if(e.opts ~= nil) then + local chance_mod = 0; + + if (e.opts ~= nil) then chance_mod = e.opts.hit_chance; end - - if(attacker:IsNPC() and not attacker:IsPet()) then + + if (attacker:IsNPC() and not attacker:IsPet()) then chancetohit = chancetohit + NPCBonusHitChance; end local pvpmode = false; - if(self:IsClient() and other:IsClient()) then + if (self:IsClient() and other:IsClient()) then pvpmode = true; end - + if (chance_mod >= 10000) then e.ReturnValue = true; return e; end - + local avoidanceBonus = 0; - local hitBonus = 0; - + local hitBonus = 0; + local attacker_level = attacker:GetLevel(); - if(attacker_level < 1) then + if (attacker_level < 1) then attacker_level = 1; end - + local defender_level = defender:GetLevel(); - if(defender_level < 1) then + if (defender_level < 1) then defender_level = 1; end local level_difference = attacker_level - defender_level; - local range = defender_level; - range = ((range / 4) + 3); - - if(level_difference < 0) then - if(level_difference >= -range) then + local range = defender_level; + range = ((range / 4) + 3); + + if (level_difference < 0) then + if (level_difference >= -range) then chancetohit = chancetohit + ((level_difference / range) * HitFalloffMinor); - elseif (level_difference >= -(range+3.0)) then + elseif (level_difference >= -(range + 3.0)) then chancetohit = chancetohit - HitFalloffMinor; chancetohit = chancetohit + (((level_difference + range) / 3.0) * HitFalloffModerate); else @@ -116,85 +153,95 @@ function CheckHitChance(e) end chancetohit = chancetohit - (defender:GetAGI() * AgiHitFactor); - - if(attacker:IsClient()) then + + if (attacker:IsClient()) then chancetohit = chancetohit - (WeaponSkillFalloff * (attacker:CastToClient():MaxSkill(e.hit.skill) - attacker:GetSkill(e.hit.skill))); end - - if(defender:IsClient()) then + + if (defender:IsClient()) then chancetohit = chancetohit + (WeaponSkillFalloff * (defender:CastToClient():MaxSkill(Skill.Defense) - defender:GetSkill(Skill.Defense))); end - + local attacker_spellbonuses = attacker:GetSpellBonuses(); - local attacker_itembonuses = attacker:GetItemBonuses(); - local attacker_aabonuses = attacker:GetAABonuses(); + local attacker_itembonuses = attacker:GetItemBonuses(); + local attacker_aabonuses = attacker:GetAABonuses(); local defender_spellbonuses = defender:GetSpellBonuses(); - local defender_itembonuses = defender:GetItemBonuses(); - local defender_aabonuses = defender:GetAABonuses(); - - if(attacker_spellbonuses:MeleeSkillCheckSkill() == e.hit.skill or attacker_spellbonuses:MeleeSkillCheckSkill() == 255) then + local defender_itembonuses = defender:GetItemBonuses(); + local defender_aabonuses = defender:GetAABonuses(); + + if (attacker_spellbonuses:MeleeSkillCheckSkill() == e.hit.skill or attacker_spellbonuses:MeleeSkillCheckSkill() == 255) then chancetohit = chancetohit + attacker_spellbonuses:MeleeSkillCheck(); end - - if(attacker_itembonuses:MeleeSkillCheckSkill() == e.hit.skill or attacker_itembonuses:MeleeSkillCheckSkill() == 255) then - chancetohit = chancetohit + attacker_itembonuses:MeleeSkillCheck(); + + if (attacker_itembonuses:MeleeSkillCheckSkill() == e.hit.skill or attacker_itembonuses:MeleeSkillCheckSkill() == 255) then + chancetohit = chancetohit + attacker_itembonuses:MeleeSkillCheck(); end avoidanceBonus = defender_spellbonuses:AvoidMeleeChanceEffect() + - defender_itembonuses:AvoidMeleeChanceEffect() + - defender_aabonuses:AvoidMeleeChanceEffect() + - (defender_itembonuses:AvoidMeleeChance() / 10.0); - - local owner = Mob(); + defender_itembonuses:AvoidMeleeChanceEffect() + + defender_aabonuses:AvoidMeleeChanceEffect() + + (defender_itembonuses:AvoidMeleeChance() / 10.0); + + local owner = Mob(); if (defender:IsPet()) then owner = defender:GetOwner(); elseif (defender:IsNPC() and defender:CastToNPC():GetSwarmOwner()) then local entity_list = eq.get_entity_list(); - owner = entity_list:GetMobID(defender:CastToNPC():GetSwarmOwner()); + owner = entity_list:GetMobID(defender:CastToNPC():GetSwarmOwner()); end - + if (owner.valid) then avoidanceBonus = avoidanceBonus + owner:GetAABonuses():PetAvoidance() + owner:GetSpellBonuses():PetAvoidance() + owner:GetItemBonuses():PetAvoidance(); end - if(defender:IsNPC()) then + if (defender:IsNPC()) then avoidanceBonus = avoidanceBonus + (defender:CastToNPC():GetAvoidanceRating() / 10.0); end hitBonus = hitBonus + attacker_itembonuses:HitChanceEffect(e.hit.skill) + - attacker_spellbonuses:HitChanceEffect(e.hit.skill) + - attacker_aabonuses:HitChanceEffect(e.hit.skill) + - attacker_itembonuses:HitChanceEffect(Skill.HIGHEST_SKILL + 1) + - attacker_spellbonuses:HitChanceEffect(Skill.HIGHEST_SKILL + 1) + - attacker_aabonuses:HitChanceEffect(Skill.HIGHEST_SKILL + 1); - + attacker_spellbonuses:HitChanceEffect(e.hit.skill) + + attacker_aabonuses:HitChanceEffect(e.hit.skill) + + attacker_itembonuses:HitChanceEffect(Skill.HIGHEST_SKILL + 1) + + attacker_spellbonuses:HitChanceEffect(Skill.HIGHEST_SKILL + 1) + + attacker_aabonuses:HitChanceEffect(Skill.HIGHEST_SKILL + 1); + hitBonus = hitBonus + (attacker_itembonuses:Accuracy(Skill.HIGHEST_SKILL + 1) + - attacker_spellbonuses:Accuracy(Skill.HIGHEST_SKILL + 1) + - attacker_aabonuses:Accuracy(Skill.HIGHEST_SKILL + 1) + - attacker_aabonuses:Accuracy(e.hit.skill) + - attacker_itembonuses:HitChance()) / 15.0; - + attacker_spellbonuses:Accuracy(Skill.HIGHEST_SKILL + 1) + + attacker_aabonuses:Accuracy(Skill.HIGHEST_SKILL + 1) + + attacker_aabonuses:Accuracy(e.hit.skill) + + attacker_itembonuses:HitChance()) / 15.0; + hitBonus = hitBonus + chance_mod; - - if(attacker:IsNPC()) then + + if (attacker:IsNPC()) then hitBonus = hitBonus + (attacker:CastToNPC():GetAccuracyRating() / 10.0); end - + if (e.hit.skill == Skill.Archery) then hitBonus = hitBonus - (hitBonus * ArcheryHitPenalty); end chancetohit = chancetohit + ((chancetohit * (hitBonus - avoidanceBonus)) / 100.0); - if(chancetohit > 1000 or chancetohit < -1000) then - elseif(chancetohit > 95) then + if (chancetohit > 1000 or chancetohit < -1000) then + elseif (chancetohit > 95) then chancetohit = 95; - elseif(chancetohit < 5) then + elseif (chancetohit < 5) then chancetohit = 5; end local tohit_roll = Random.Real(0, 100); - if(tohit_roll <= chancetohit) then + + eq.log_combat( + string.format("[%s] [Mob::CheckHitChance] Chance [%i] ToHitRoll [%i] Hit? [%s]", + e.self:GetCleanName(), + chancetohit, + tohit_roll, + (tohit_roll <= chancetohit) and "true" or "false" + ) + ); + + if (tohit_roll <= chancetohit) then e.ReturnValue = true; else e.ReturnValue = false; @@ -203,32 +250,33 @@ function CheckHitChance(e) return e; end +-- Source Function: Mob::TryCriticalHit() function TryCriticalHit(e) e.IgnoreDefault = true; - - local self = e.self; - local defender = e.other; - - if(e.hit.damage_done < 1 or defender.null) then + + local self = e.self; + local defender = e.other; + + if (e.hit.damage_done < 1 or defender.null) then return e; end - + if ((self:IsPet() and self:GetOwner():IsClient()) or (self:IsNPC() and self:CastToNPC():GetSwarmOwner() ~= 0)) then e.hit = TryPetCriticalHit(self, defender, e.hit); return e; end - + if (self:IsPet() and self:GetOwner().valid and self:GetOwner():IsBot()) then e.hit = TryPetCriticalHit(self, defender, e.hit); return e; end - local critChance = 0.0; + local critChance = 0.0; local IsBerskerSPA = false; - local aabonuses = self:GetAABonuses(); - local itembonuses = self:GetItemBonuses(); + local aabonuses = self:GetAABonuses(); + local itembonuses = self:GetItemBonuses(); local spellbonuses = self:GetSpellBonuses(); - local entity_list = eq.get_entity_list(); + local entity_list = eq.get_entity_list(); if (defender:GetBodyType() == BT.Undead or defender:GetBodyType() == BT.SummonedUndead or defender:GetBodyType() == BT.Vampire) then local SlayRateBonus = aabonuses:SlayUndead(0) + itembonuses:SlayUndead(0) + spellbonuses:SlayUndead(0); @@ -236,28 +284,28 @@ function TryCriticalHit(e) local slayChance = SlayRateBonus / 10000.0; if (Random.RollReal(slayChance)) then local SlayDmgBonus = aabonuses:SlayUndead(1) + itembonuses:SlayUndead(1) + spellbonuses:SlayUndead(1); - e.hit.damage_done = (e.hit.damage_done * SlayDmgBonus * 2.25) / 100; - + e.hit.damage_done = (e.hit.damage_done * SlayDmgBonus * 2.25) / 100; + if (self:GetGender() == 1) then entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses her target! (%d)', self:GetCleanName(), e.hit.damage_done)); else entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses his target! (%d)', self:GetCleanName(), e.hit.damage_done)); end - + return e; end end end - + critChance = critChance + MeleeBaseCritChance; - + if (self:IsClient()) then - critChance = critChance + ClientBaseCritChance; - + critChance = critChance + ClientBaseCritChance; + if (spellbonuses:BerserkSPA() or itembonuses:BerserkSPA() or aabonuses:BerserkSPA()) then IsBerskerSPA = true; end - + if (((self:GetClass() == Class.WARRIOR or self:GetClass() == Class.BERSERKER) and self:GetLevel() >= 12) or IsBerskerSPA) then if (self:IsBerserk() or IsBerskerSPA) then critChance = critChance + BerserkBaseCritChance; @@ -266,21 +314,21 @@ function TryCriticalHit(e) end end end - + local deadlyChance = 0; - local deadlyMod = 0; + local deadlyMod = 0; if (e.hit.skill == Skill.Archery and self:GetClass() == Class.RANGER and self:GetSkill(Skill.Archery) >= 65) then critChance = critChance + 6; end if (e.hit.skill == Skill.Throwing and self:GetClass() == Class.ROGUE and self:GetSkill(Skill.Throwing) >= 65) then - critChance = critChance + RogueCritThrowingChance; + critChance = critChance + RogueCritThrowingChance; deadlyChance = RogueDeadlyStrikeChance; - deadlyMod = RogueDeadlyStrikeMod; + deadlyMod = RogueDeadlyStrikeMod; end - + local CritChanceBonus = GetCriticalChanceBonus(self, e.hit.skill); - + if (CritChanceBonus > 0 or critChance > 0) then if (self:GetDEX() <= 255) then critChance = critChance + (self:GetDEX() / 125.0); @@ -289,43 +337,63 @@ function TryCriticalHit(e) end critChance = critChance + (critChance * CritChanceBonus / 100.0); end - - if(opts ~= nil) then + + if (opts ~= nil) then critChance = critChance * opts.crit_percent; critChance = critChance + opts.crit_flat; end - - if(critChance > 0) then - + + eq.log_combat( + string.format("[%s] [Mob::TryCriticalHit] CritChance [%i] CritChanceBonus [%i] Dex [%i] Post-Dex-Block", + e.self:GetCleanName(), + critChance, + CritChanceBonus, + e.self:GetDEX() + ) + ); + + if (critChance > 0) then + critChance = critChance / 100; - - if(Random.RollReal(critChance)) then - local critMod = 200; - local crip_success = false; + + if (Random.RollReal(critChance)) then + local critMod = 200; + local crip_success = false; local CripplingBlowChance = GetCrippBlowChance(self); - + if (CripplingBlowChance > 0 or (self:IsBerserk() or IsBerskerSPA)) then if (not self:IsBerserk() and not IsBerskerSPA) then critChance = critChance * (CripplingBlowChance / 100.0); end - + if ((self:IsBerserk() or IsBerskerSPA) or Random.RollReal(critChance)) then - critMod = 400; + critMod = 400; crip_success = true; end end - + critMod = critMod + GetCritDmgMod(self, e.hit.skill) * 2; - e.hit.damage_done = e.hit.damage_done * critMod / 100; - + + eq.log_combat( + string.format("[%s] [Mob::TryCriticalHit] CritChance [%i] CritMod [%i] GetCritDmgMod [%i] CripSuccess [%s]", + e.self:GetCleanName(), + critChance, + critMod, + GetCritDmgMod(self, e.hit.skill), + (crip_success) and "true" or "false" + ) + ); + + e.hit.damage_done = e.hit.damage_done * critMod / 100; + local deadlySuccess = false; if (deadlyChance > 0 and Random.RollReal(deadlyChance / 100.0)) then if (self:BehindMob(defender, self:GetX(), self:GetY())) then e.hit.damage_done = e.hit.damage_done * deadlyMod; - deadlySuccess = true; + deadlySuccess = true; end end - + if (crip_success) then entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s lands a Crippling Blow! (%d)', self:GetCleanName(), e.hit.damage_done)); if (defender:GetLevel() <= 55 and not defender:GetSpecialAbility(SpecialAbility.unstunable)) then @@ -339,24 +407,25 @@ function TryCriticalHit(e) end end end - + return e; end +-- Source Function: Mob::TryPetCriticalHit() function TryPetCriticalHit(self, defender, hit) - if(hit.damage_done < 1) then + if (hit.damage_done < 1) then return hit; end - local owner = Mob(); + local owner = Mob(); local critChance = MeleeBaseCritChance; - local critMod = 163; + local critMod = 163; if (self:IsPet()) then owner = self:GetOwner(); elseif (self:IsNPC() and self:CastToNPC():GetSwarmOwner()) then local entity_list = eq.get_entity_list(); - owner = entity_list:GetMobID(self:CastToNPC():GetSwarmOwner()); + owner = entity_list:GetMobID(self:CastToNPC():GetSwarmOwner()); else return hit; end @@ -364,128 +433,198 @@ function TryPetCriticalHit(self, defender, hit) if (owner.null) then return hit; end - - local CritPetChance = owner:GetAABonuses():PetCriticalHit() + owner:GetItemBonuses():PetCriticalHit() + owner:GetSpellBonuses():PetCriticalHit(); + + local CritPetChance = owner:GetAABonuses():PetCriticalHit() + owner:GetItemBonuses():PetCriticalHit() + owner:GetSpellBonuses():PetCriticalHit(); local CritChanceBonus = GetCriticalChanceBonus(self, hit.skill); + eq.log_combat( + string.format("[%s] [Mob::TryPetCriticalHit] CritPetChance [%i] CritChanceBonus [%i] | Bonuses AA [%i] Item [%i] Spell [%i]", + e.self:GetCleanName(), + CritPetChance, + CritChanceBonus, + owner:GetAABonuses():PetCriticalHit(), + owner:GetItemBonuses():PetCriticalHit(), + owner:GetSpellBonuses():PetCriticalHit() + ) + ); + if (CritPetChance or critChance) then critChance = critChance + CritPetChance; critChance = critChance + (critChance * CritChanceBonus / 100.0); + + eq.log_combat( + string.format("[%s] [Mob::TryPetCriticalHit] critChance [%i] PostCalcs", + e.self:GetCleanName(), + critChance + ) + ); + end - if(critChance > 0) then + if (critChance > 0) then critChance = critChance / 100; - if(Random.RollReal(critChance)) then + if (Random.RollReal(critChance)) then local entity_list = eq.get_entity_list(); - critMod = critMod + GetCritDmgMod(self, hit.skill) * 2; - hit.damage_done = (hit.damage_done * critMod) / 100; - entity_list:FilteredMessageClose(this, false, CriticalMessageRange, - MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)', - self:GetCleanName(), e.hit.damage_done)); + critMod = critMod + GetCritDmgMod(self, hit.skill) * 2; + hit.damage_done = (hit.damage_done * critMod) / 100; + + eq.log_combat( + string.format("[%s] [Mob::TryPetCriticalHit] critMod [%i] DmgMod [%i] DamageDone [%i]", + e.self:GetCleanName(), + critMod, + GetCritDmgMod(self, hit.skill), + hit.damage_done + ) + ); + + entity_list:FilteredMessageClose( + this, + false, + CriticalMessageRange, + MT.CritMelee, + Filter.MeleeCrits, + string.format('%s scores a critical hit! (%d)', self:GetCleanName(), e.hit.damage_done) + ); end end - + return hit; end +-- Source Function: Mob::GetCriticalChanceBonus() function GetCriticalChanceBonus(self, skill) - + local critical_chance = 0; - - local aabonuses = self:GetAABonuses(); - local itembonuses = self:GetItemBonuses(); - local spellbonuses = self:GetSpellBonuses(); - - critical_chance = critical_chance + itembonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1); - critical_chance = critical_chance + spellbonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1); - critical_chance = critical_chance + aabonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1); - critical_chance = critical_chance + itembonuses:CriticalHitChance(skill); - critical_chance = critical_chance + spellbonuses:CriticalHitChance(skill); - critical_chance = critical_chance + aabonuses:CriticalHitChance(skill); - + + local aabonuses = self:GetAABonuses(); + local itembonuses = self:GetItemBonuses(); + local spellbonuses = self:GetSpellBonuses(); + + critical_chance = critical_chance + itembonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1); + critical_chance = critical_chance + spellbonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1); + critical_chance = critical_chance + aabonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1); + critical_chance = critical_chance + itembonuses:CriticalHitChance(skill); + critical_chance = critical_chance + spellbonuses:CriticalHitChance(skill); + critical_chance = critical_chance + aabonuses:CriticalHitChance(skill); + + eq.log_combat( + string.format("[%s] [Mob::GetCriticalChanceBonus] Bonuses | Item [%i] Spell [%i] AA [%i] | 2nd Item [%i] Spell [%i] AA [%i] Final Chance [%i]", + self:GetCleanName(), + itembonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1), + spellbonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1), + aabonuses:CriticalHitChance(Skill.HIGHEST_SKILL + 1), + itembonuses:CriticalHitChance(skill), + spellbonuses:CriticalHitChance(skill), + aabonuses:CriticalHitChance(skill), + critical_chance + ) + ); + return critical_chance; end +-- Source Function: Mob::GetCritDmgMob() function GetCritDmgMod(self, skill) - local critDmg_mod = 0; - - local aabonuses = self:GetAABonuses(); - local itembonuses = self:GetItemBonuses(); + local critDmg_mod = 0; + + local aabonuses = self:GetAABonuses(); + local itembonuses = self:GetItemBonuses(); local spellbonuses = self:GetSpellBonuses(); - - critDmg_mod = critDmg_mod + itembonuses:CritDmgMod(Skill.HIGHEST_SKILL + 1); - critDmg_mod = critDmg_mod + spellbonuses:CritDmgMod(Skill.HIGHEST_SKILL + 1); - critDmg_mod = critDmg_mod + aabonuses:CritDmgMod(Skill.HIGHEST_SKILL + 1); - critDmg_mod = critDmg_mod + itembonuses:CritDmgMod(skill); - critDmg_mod = critDmg_mod + spellbonuses:CritDmgMod(skill); - critDmg_mod = critDmg_mod + aabonuses:CritDmgMod(skill); - + critDmg_mod = critDmg_mod + itembonuses:CritDmgMod(Skill.HIGHEST_SKILL + 1); + critDmg_mod = critDmg_mod + spellbonuses:CritDmgMod(Skill.HIGHEST_SKILL + 1); + critDmg_mod = critDmg_mod + aabonuses:CritDmgMod(Skill.HIGHEST_SKILL + 1); + critDmg_mod = critDmg_mod + itembonuses:CritDmgMod(skill); + critDmg_mod = critDmg_mod + spellbonuses:CritDmgMod(skill); + critDmg_mod = critDmg_mod + aabonuses:CritDmgMod(skill); + + if (critDmg_mod < -100) then + critDmg_mod = -100; + end + return critDmg_mod; end +-- Source Function: Mob::GetCrippBlowChance() function GetCrippBlowChance(self) - local aabonuses = self:GetAABonuses(); - local itembonuses = self:GetItemBonuses(); + local aabonuses = self:GetAABonuses(); + local itembonuses = self:GetItemBonuses(); local spellbonuses = self:GetSpellBonuses(); - local crip_chance = itembonuses:CrippBlowChance() + spellbonuses:CrippBlowChance() + aabonuses:CrippBlowChance(); + local crip_chance = itembonuses:CrippBlowChance() + spellbonuses:CrippBlowChance() + aabonuses:CrippBlowChance(); - if(crip_chance < 0) then + if (crip_chance < 0) then crip_chance = 0; end return crip_chance; end +-- Source Function: Mob::MeleeMitigation() function DoMeleeMitigation(defender, attacker, hit, opts) if hit.damage_done <= 0 then return hit; end - - local aabonuses = defender:GetAABonuses(); - local itembonuses = defender:GetItemBonuses(); - local spellbonuses = defender:GetSpellBonuses(); - - local aa_mit = (aabonuses:CombatStability() + itembonuses:CombatStability() + spellbonuses:CombatStability()) / 100.0; - local softcap = (defender:GetSkill(15) + defender:GetLevel()) * SoftcapFactor * (1.0 + aa_mit); + + local aabonuses = defender:GetAABonuses(); + local itembonuses = defender:GetItemBonuses(); + local spellbonuses = defender:GetSpellBonuses(); + + local aa_mit = (aabonuses:CombatStability() + itembonuses:CombatStability() + spellbonuses:CombatStability()) / 100.0; + local softcap = (defender:GetSkill(15) + defender:GetLevel()) * SoftcapFactor * (1.0 + aa_mit); local mitigation_rating = 0.0; - local attack_rating = 0.0; - local shield_ac = 0; - local armor = 0; - local weight = 0.0; - local monkweight = MonkACBonusWeight; - + local attack_rating = 0.0; + local shield_ac = 0; + local armor = 0; + local weight = 0.0; + local monkweight = MonkACBonusWeight; + + eq.log_combat( + string.format("[%s] [Mob::MeleeMitigation] Stability Bonuses | AA [%i] Item [%i] Spell [%i]", + defender:GetCleanName(), + aabonuses:CombatStability(), + itembonuses:CombatStability(), + spellbonuses:CombatStability() + ) + ); + + eq.log_combat( + string.format("[%s] [Mob::MeleeMitigation] Soft Cap [%i]", + defender:GetCleanName(), + softcap + ) + ); + if defender:IsClient() then armor, shield_ac = GetRawACNoShield(defender); - weight = defender:CastToClient():CalcCurrentWeight() / 10; + weight = defender:CastToClient():CalcCurrentWeight() / 10; elseif defender:IsNPC() then - armor = defender:CastToNPC():GetRawAC(); + armor = defender:CastToNPC():GetRawAC(); local PetACBonus = 0; - + if not defender:IsPet() then armor = armor / NPCACFactor; end - + local owner = Mob(); if defender:IsPet() then owner = defender:GetOwner(); elseif defender:CastToNPC():GetSwarmOwner() ~= 0 then local entity_list = eq.get_entity_list(); - owner = entity_list:GetMobID(defender:CastToNPC():GetSwarmOwner()); + owner = entity_list:GetMobID(defender:CastToNPC():GetSwarmOwner()); end - + if owner.valid then PetACBonus = owner:GetAABonuses():PetMeleeMitigation() + owner:GetItemBonuses():PetMeleeMitigation() + owner:GetSpellBonuses():PetMeleeMitigation(); end - + armor = armor + defender:GetSpellBonuses():AC() + defender:GetItemBonuses():AC() + PetACBonus + 1; end - + if (opts ~= nil) then armor = armor * (1.0 - opts.armor_pen_percent); armor = armor - opts.armor_pen_flat; end - + local defender_class = defender:GetClass(); if OldACSoftcapRules then if defender_class == Class.WIZARD or defender_class == Class.MAGICIAN or defender_class == Class.NECROMANCER or defender_class == Class.ENCHANTER then @@ -502,12 +641,12 @@ function DoMeleeMitigation(defender, attacker, hit, opts) end softcap = softcap + shield_ac; - armor = armor + shield_ac; - + armor = armor + shield_ac; + if OldACSoftcapRules then softcap = softcap + (softcap * (aa_mit * AAMitigationACFactor)); end - + if armor > softcap then local softcap_armor = armor - softcap; if OldACSoftcapRules then @@ -529,7 +668,7 @@ function DoMeleeMitigation(defender, attacker, hit, opts) softcap_armor = softcap_armor * WarACSoftcapReturn; elseif defender_class == Class.PALADIN or defender_class == Class.SHADOWKNIGHT then softcap_armor = softcap_armor * PalShdACSoftcapReturn; - elseif defender_class == Class.CLERIC or defender_class == Class.RANGER or defender_class == Class.MONK or defender_class == Class.BARD then + elseif defender_class == Class.CLERIC or defender_class == Class.RANGER or defender_class == Class.MONK or defender_class == Class.BARD then softcap_armor = softcap_armor * ClrRngMnkBrdACSoftcapReturn; elseif defender_class == Class.DRUID or defender_class == Class.NECROMANCER or defender_class == Class.WIZARD or defender_class == Class.ENCHANTER or defender_class == Class.MAGICIAN then softcap_armor = softcap_armor * DruNecWizEncMagACSoftcapReturn; @@ -539,35 +678,52 @@ function DoMeleeMitigation(defender, attacker, hit, opts) softcap_armor = softcap_armor * MiscACSoftcapReturn; end end - + armor = softcap + softcap_armor; end - + local mitigation_rating; if defender_class == Class.WIZARD or defender_class == Class.MAGICIAN or defender_class == Class.NECROMANCER or defender_class == Class.ENCHANTER then mitigation_rating = ((defender:GetSkill(Skill.Defense) + defender:GetItemBonuses():HeroicAGI() / 10) / 4.0) + armor + 1; else mitigation_rating = ((defender:GetSkill(Skill.Defense) + defender:GetItemBonuses():HeroicAGI() / 10) / 3.0) + (armor * 1.333333) + 1; end - + mitigation_rating = mitigation_rating * 0.847; - + local attack_rating; if attacker:IsClient() then - attack_rating = (attacker:CastToClient():CalcATK() + ((attacker:GetSTR() - 66) * 0.9) + (attacker:GetSkill(Skill.Offense)*1.345)); + attack_rating = (attacker:CastToClient():CalcATK() + ((attacker:GetSTR() - 66) * 0.9) + (attacker:GetSkill(Skill.Offense) * 1.345)); else - attack_rating = (attacker:GetATK() + (attacker:GetSkill(Skill.Offense)*1.345) + ((attacker:GetSTR() - 66) * 0.9)); + attack_rating = (attacker:GetATK() + (attacker:GetSkill(Skill.Offense) * 1.345) + ((attacker:GetSTR() - 66) * 0.9)); end - + + eq.log_combat( + string.format("[%s] [Mob::MeleeMitigation] Attack Rating [%02f] Mitigation Rating [%02f] Damage [%i]", + defender:GetCleanName(), + attack_rating, + mitigation_rating, + hit.damage_done + ) + ); + hit.damage_done = GetMeleeMitDmg(defender, attacker, hit.damage_done, hit.min_damage, mitigation_rating, attack_rating); - if hit.damage_done < 0 then + if hit.damage_done < 0 then hit.damage_done = 0; end - + + eq.log_combat( + string.format("[%s] [Mob::MeleeMitigation] Final Damage [%i]", + defender:GetCleanName(), + hit.damage_done + ) + ); + return hit; end +-- Source Function: N/A function GetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating) if defender:IsClient() then return ClientGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating); @@ -576,28 +732,29 @@ function GetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_ratin end end +-- Source Function: Client::GetMeleeMitDmg() function ClientGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating) if (not attacker:IsNPC() or UseOldDamageIntervalRules) then return MobGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating); end - - local d = 10; - local dmg_interval = (damage - min_damage) / 19.0; - local dmg_bonus = min_damage - dmg_interval; + + local d = 10; + local dmg_interval = (damage - min_damage) / 19.0; + local dmg_bonus = min_damage - dmg_interval; local spellMeleeMit = (defender:GetSpellBonuses():MeleeMitigationEffect() + defender:GetItemBonuses():MeleeMitigationEffect() + defender:GetAABonuses():MeleeMitigationEffect()) / 100.0; if (defender:GetClass() == Class.WARRIOR) then spellMeleeMit = spellMeleeMit - 0.05; end - - dmg_bonus = dmg_bonus - (dmg_bonus * (defender:GetItemBonuses():MeleeMitigation() / 100.0)); - dmg_interval = dmg_interval + (dmg_interval * spellMeleeMit); + + dmg_bonus = dmg_bonus - (dmg_bonus * (defender:GetItemBonuses():MeleeMitigation() / 100.0)); + dmg_interval = dmg_interval + (dmg_interval * spellMeleeMit); local mit_roll = Random.Real(0, mitigation_rating); local atk_roll = Random.Real(0, attack_rating); if (atk_roll > mit_roll) then - local a_diff = atk_roll - mit_roll; - local thac0 = attack_rating * ACthac0Factor; + local a_diff = atk_roll - mit_roll; + local thac0 = attack_rating * ACthac0Factor; local thac0cap = attacker:GetLevel() * 9 + 20; if (thac0 > thac0cap) then thac0 = thac0cap; @@ -605,8 +762,8 @@ function ClientGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation d = d + (10 * (a_diff / thac0)); elseif (mit_roll > atk_roll) then - local m_diff = mit_roll - atk_roll; - local thac20 = mitigation_rating * ACthac20Factor; + local m_diff = mit_roll - atk_roll; + local thac20 = mitigation_rating * ACthac20Factor; local thac20cap = defender:GetLevel() * 9 + 20; if (thac20 > thac20cap) then thac20 = thac20cap; @@ -624,14 +781,23 @@ function ClientGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation return math.floor(dmg_bonus + dmg_interval * d); end -function MobGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating) - local d = 10.0; +-- Source Function: Mob::GetMeleeMitDmg() +function MobGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_rating, attack_rating) + local d = 10.0; local mit_roll = Random.Real(0, mitigation_rating); local atk_roll = Random.Real(0, attack_rating); + eq.log_combat( + string.format("[%s] [Mob::GetMeleeMitDmg] MitigationRoll [%02f] AtkRoll [%02f]", + defender:GetCleanName(), + mit_roll, + atk_roll + ) + ); + if (atk_roll > mit_roll) then - local a_diff = atk_roll - mit_roll; - local thac0 = attack_rating * ACthac0Factor; + local a_diff = atk_roll - mit_roll; + local thac0 = attack_rating * ACthac0Factor; local thac0cap = attacker:GetLevel() * 9 + 20; if (thac0 > thac0cap) then thac0 = thac0cap; @@ -639,8 +805,8 @@ function MobGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_ra d = d - (10.0 * (a_diff / thac0)); elseif (mit_roll > atk_roll) then - local m_diff = mit_roll - atk_roll; - local thac20 = mitigation_rating * ACthac20Factor; + local m_diff = mit_roll - atk_roll; + local thac20 = mitigation_rating * ACthac20Factor; local thac20cap = defender:GetLevel() * 9 + 20; if (thac20 > thac20cap) then thac20 = thac20cap; @@ -654,26 +820,64 @@ function MobGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_ra elseif (d > 20.0) then d = 20.0; end - + local interval = (damage - min_damage) / 20.0; - damage = damage - (math.floor(d) * interval); - damage = damage - (min_damage * defender:GetItemBonuses():MeleeMitigation() / 100); - damage = damage + (damage * (defender:GetSpellBonuses():MeleeMitigationEffect() + defender:GetItemBonuses():MeleeMitigationEffect() + defender:GetAABonuses():MeleeMitigationEffect()) / 100); - + + eq.log_combat( + string.format("[%s] [Mob::GetMeleeMitDmg] Interval [%02f] d [%02f]", + defender:GetCleanName(), + interval, + d + ) + ); + + damage = damage - (math.floor(d) * interval); + + eq.log_combat( + string.format("[%s] [Mob::GetMeleeMitDmg] Damage [%02f] Post Interval", + defender:GetCleanName(), + damage + ) + ); + + damage = damage - (min_damage * defender:GetItemBonuses():MeleeMitigation() / 100); + + eq.log_combat( + string.format("[%s] [Mob::GetMeleeMitDmg] Damage [%02f] Mitigation [%i] Post Mitigation MinDmg", + defender:GetCleanName(), + damage, + defender:GetItemBonuses():MeleeMitigation() + ) + ); + + -- Changed from positive to negative per original + damage = damage - (damage * (defender:GetSpellBonuses():MeleeMitigationEffect() + defender:GetItemBonuses():MeleeMitigationEffect() + defender:GetAABonuses():MeleeMitigationEffect()) / 100); + + eq.log_combat( + string.format("[%s] [Mob::GetMeleeMitDmg] Damage [%02f] SpellMit [%i] ItemMit [%i] AAMit [%i] Post All Mit Bonuses", + defender:GetCleanName(), + damage, + defender:GetSpellBonuses():MeleeMitigationEffect(), + defender:GetItemBonuses():MeleeMitigationEffect(), + defender:GetAABonuses():MeleeMitigationEffect() + ) + ); + return damage; end +-- Source Function: Client::GetRawACNoShield() function GetRawACNoShield(self) - self = self:CastToClient(); - - local ac = self:GetItemBonuses():AC() + self:GetSpellBonuses():AC() + self:GetAABonuses():AC(); + self = self:CastToClient(); + + local ac = self:GetItemBonuses():AC() + self:GetSpellBonuses():AC() + self:GetAABonuses():AC(); local shield_ac = 0; - local inst = self:GetInventory():GetItem(Slot.Secondary); - + local inst = self:GetInventory():GetItem(Slot.Secondary); + if inst.valid then if inst:GetItem():ItemType() == 8 then shield_ac = inst:GetItem():AC(); - + for i = 1, 6 do local augment = inst:GetAugment(i - 1); if augment.valid then @@ -682,33 +886,45 @@ function GetRawACNoShield(self) end end end - + ac = ac - shield_ac; + + eq.log_combat( + string.format("[%s] [Client::GetRawACNoShield] AC [%i] ItemAC [%i] SpellAC [%i] AAAC [%i]", + self:GetCleanName(), + ac, + self:GetItemBonuses():AC(), + self:GetSpellBonuses():AC(), + self:GetAABonuses():AC() + ) + ); + return ac, shield_ac; end +-- Source Function: Mob::GetDamageTable() function GetDamageTable(attacker, skill) if not attacker:IsClient() then return 100; end - + if attacker:GetLevel() <= 51 then - local ret_table = 0; + local ret_table = 0; local str_over_75 = 0; if attacker:GetSTR() > 75 then str_over_75 = attacker:GetSTR() - 75; end - + if str_over_75 > 255 then ret_table = (attacker:GetSkill(skill) + 255) / 2; else ret_table = (attacker:GetSkill(skill) + str_over_75) / 2; end - + if ret_table < 100 then return 100; end - + return ret_table; elseif attacker:GetLevel() >= 90 then if attacker:GetClass() == 7 then @@ -718,7 +934,7 @@ function GetDamageTable(attacker, skill) end else local dmg_table = { 275, 275, 275, 275, 275, 280, 280, 280, 280, 285, 285, 285, 290, 290, 295, 295, 300, 300, 300, 305, 305, 305, 310, 310, 315, 315, 320, 320, 320, 325, 325, 325, 330, 330, 335, 335, 340, 340, 340 }; - + if attacker:GetClass() == 7 then local monkDamageTableBonus = 20; return (dmg_table[attacker:GetLevel() - 50] * (100 + monkDamageTableBonus) / 100); @@ -729,26 +945,56 @@ function GetDamageTable(attacker, skill) return 100; end +-- Source Function: N/A - Not used function ApplyDamageTable(e) e.IgnoreDefault = true; return e; end +-- Source Function: Mob::CommonOutgoingHitSuccess() function CommonOutgoingHitSuccess(e) e = ApplyMeleeDamageBonus(e); + + eq.log_combat( + string.format("[%s] [Mob::CommonOutgoingHitSuccess] Dmg [%i] Post ApplyMeleeDamageBonus", + e.self:GetCleanName(), + e.hit.damage_done + ) + ); + e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * e.other:GetSkillDmgTaken(e.hit.skill) / 100) + (e.self:GetSkillDmgAmt(e.hit.skill) + e.other:GetFcDamageAmtIncoming(e.self, 0, true, e.hit.skill)); + + eq.log_combat( + string.format("[%s] [Mob::CommonOutgoingHitSuccess] Dmg [%i] SkillDmgTaken [%i] SkillDmgtAmt [%i] FcDmgAmtIncoming [%i] Post DmgCalcs", + e.self:GetCleanName(), + e.hit.damage_done, + e.other:GetSkillDmgTaken(e.hit.skill), + e.self:GetSkillDmgAmt(e.hit.skill), + e.other:GetFcDamageAmtIncoming(e.self, 0, true, e.hit.skill) + ) + ); + e = TryCriticalHit(e); - e.self:CheckNumHitsRemaining(5, -1, 65535); + e.self:CheckNumHitsRemaining(5, -1, 65535); e.IgnoreDefault = true; return e; end +-- Source Function: Mob::ApplyMeleeDamageBonus() function ApplyMeleeDamageBonus(e) local dmgbonusmod = e.self:GetMeleeDamageMod_SE(e.hit.skill); if (opts) then dmgbonusmod = dmgbonusmod + e.opts.melee_damage_bonus_flat; end + eq.log_combat( + string.format("[%s] [Mob::ApplyMeleeDamageBonus] DmgBonusMod [%i] Dmg [%i]", + e.self:GetCleanName(), + dmgbonusmod, + e.hit.damage_done + ) + ); + e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * dmgbonusmod / 100); return e; -end +end \ No newline at end of file From c48e8d88236335d241591450d60a5de06323b1f8 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Thu, 2 Jan 2020 20:04:37 -0600 Subject: [PATCH 072/157] Tweak logging code --- zone/embxs.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/zone/embxs.cpp b/zone/embxs.cpp index 59bfb333a..9b6fae20e 100644 --- a/zone/embxs.cpp +++ b/zone/embxs.cpp @@ -92,39 +92,43 @@ XS(XS_EQEmuIO_PRINT) // Perl_croak(aTHX_ "Usage: EQEmuIO::PRINT(@strings)"); int r; - for(r = 1; r < items; r++) { + for (r = 1; r < items; r++) { char *str = SvPV_nolen(ST(r)); char *cur = str; - + /* Strip newlines from log message 'str' */ *std::remove(str, str + strlen(str), '\n') = '\0'; std::string log_string = str; - if (log_string.find("did not return a true") != std::string::npos) + if (log_string.find("did not return a true") != std::string::npos) { return; + } - if (log_string.find("is experimental") != std::string::npos) + if (log_string.find("is experimental") != std::string::npos) { return; + } int i; int pos = 0; int len = 0; - for(i = 0; *cur != '\0'; i++, cur++) { - if(*cur == '\n') { + + for (i = 0; *cur != '\0'; i++, cur++) { + if (*cur == '\n') { LogQuests(str); len = 0; - pos = i+1; - } else { + pos = i + 1; + } + else { len++; } } - if(len > 0) { - LogQuests(str); + if (!log_string.empty()) { + LogQuests(log_string); } - } - - XSRETURN_EMPTY; + } + + XSRETURN_EMPTY; } #endif //EMBPERL_IO_CAPTURE From d6d361f5eb931d8bb8f62f7cbb1cb5dce2574e5a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Thu, 2 Jan 2020 20:10:14 -0600 Subject: [PATCH 073/157] Adjust logging in legacy_combat.lua [skip ci] --- utils/mods/legacy_combat.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/mods/legacy_combat.lua b/utils/mods/legacy_combat.lua index 9baf918a8..c04644a8d 100644 --- a/utils/mods/legacy_combat.lua +++ b/utils/mods/legacy_combat.lua @@ -439,7 +439,7 @@ function TryPetCriticalHit(self, defender, hit) eq.log_combat( string.format("[%s] [Mob::TryPetCriticalHit] CritPetChance [%i] CritChanceBonus [%i] | Bonuses AA [%i] Item [%i] Spell [%i]", - e.self:GetCleanName(), + self:GetCleanName(), CritPetChance, CritChanceBonus, owner:GetAABonuses():PetCriticalHit(), @@ -454,7 +454,7 @@ function TryPetCriticalHit(self, defender, hit) eq.log_combat( string.format("[%s] [Mob::TryPetCriticalHit] critChance [%i] PostCalcs", - e.self:GetCleanName(), + self:GetCleanName(), critChance ) ); @@ -471,7 +471,7 @@ function TryPetCriticalHit(self, defender, hit) eq.log_combat( string.format("[%s] [Mob::TryPetCriticalHit] critMod [%i] DmgMod [%i] DamageDone [%i]", - e.self:GetCleanName(), + self:GetCleanName(), critMod, GetCritDmgMod(self, hit.skill), hit.damage_done From 40595de46b9d90e2fa5e7bf2e378382c0392cf1b Mon Sep 17 00:00:00 2001 From: Akkadius Date: Thu, 2 Jan 2020 22:27:53 -0600 Subject: [PATCH 074/157] Fix fmt bindings --- zone/embxs.cpp | 4 ++-- zone/quest_interface.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/embxs.cpp b/zone/embxs.cpp index 9b6fae20e..4571a8f8f 100644 --- a/zone/embxs.cpp +++ b/zone/embxs.cpp @@ -115,7 +115,7 @@ XS(XS_EQEmuIO_PRINT) for (i = 0; *cur != '\0'; i++, cur++) { if (*cur == '\n') { - LogQuests(str); + LogQuests("{}", str); len = 0; pos = i + 1; } @@ -124,7 +124,7 @@ XS(XS_EQEmuIO_PRINT) } } if (!log_string.empty()) { - LogQuests(log_string); + LogQuests("{}", log_string); } } diff --git a/zone/quest_interface.h b/zone/quest_interface.h index 4b5183cfb..63310efae 100644 --- a/zone/quest_interface.h +++ b/zone/quest_interface.h @@ -85,7 +85,7 @@ public: } virtual void AddError(std::string error) { - LogQuests(error); + LogQuests("{}", error); errors_.push_back(error); if(errors_.size() > 30) { From a94072e3ea1e1b7f05846c06b16cf7dde71a203e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 3 Jan 2020 15:49:20 -0500 Subject: [PATCH 075/157] Fix CheckFlee logic This /should/ be correct. But it doesn't explain other issues we've seen on some servers --- zone/fearpath.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 3f14b059a..615d3adc0 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -108,8 +108,12 @@ void Mob::CheckFlee() { } // If we got here we are allowed to roll on flee chance if there is not other hated NPC's in the area. - - if(RuleB(Combat, FleeIfNotAlone) || GetSpecialAbility(ALWAYS_FLEE) || zone->random.Roll(flee_chance) && entity_list.GetHatedCount(hate_top, this, true) == 0) { + // ALWAYS_FLEE, skip roll + // if FleeIfNotAlone is true, we skip alone check + // roll chance + if (GetSpecialAbility(ALWAYS_FLEE) || + ((RuleB(Combat, FleeIfNotAlone) || entity_list.GetHatedCount(hate_top, this, true) == 0) && + zone->random.Roll(flee_chance))) { currently_fleeing = true; StartFleeing(); } From 3a10131a007fffc75779ee868cf2545941c65dee Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 3 Jan 2020 15:50:10 -0500 Subject: [PATCH 076/157] Add missing currently_fleeing flag to sanity check --- zone/fearpath.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 615d3adc0..cf54f140d 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -75,6 +75,7 @@ void Mob::CheckFlee() { // Sanity Check this should never happen... if(!hate_top) { + currently_fleeing = true; StartFleeing(); return; } From 9dda9098a0a7b5b37f8877ad9cbc3e2c9b26d894 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 3 Jan 2020 17:23:51 -0600 Subject: [PATCH 077/157] Add logging for flee --- common/eqemu_logsys.h | 2 + common/eqemu_logsys_log_aliases.h | 10 ++++ zone/fearpath.cpp | 79 +++++++++++++++++++++---------- zone/spell_effects.cpp | 7 ++- 4 files changed, 69 insertions(+), 29 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index fc1dd36f2..184ccffe2 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -112,6 +112,7 @@ namespace Logs { AICastBeneficialClose, AoeCast, EntityManagement, + Flee, MaxCategoryID /* Don't Remove this */ }; @@ -183,6 +184,7 @@ namespace Logs { "AI Cast Beneficial Close", "AOE Cast", "Entity Management", + "Flee", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 24abfdfc9..138a6f851 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -541,6 +541,16 @@ OutF(LogSys, Logs::Detail, Logs::EntityManagement, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogFlee(message, ...) do {\ + if (LogSys.log_settings[Logs::Flee].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Flee, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogFleeDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Flee].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Flee, __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__);\ diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index cf54f140d..162a4e895 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -30,51 +30,65 @@ extern Zone* zone; #define FEAR_PATHING_DEBUG //this is called whenever we are damaged to process possible fleeing -void Mob::CheckFlee() { +void Mob::CheckFlee() +{ // if mob is dead why would you run? - if(GetHP() == 0) { + if (GetHP() == 0) { return; } // if were already fleeing, don't need to check more... - if(flee_mode && currently_fleeing) { + if (flee_mode && currently_fleeing) { return; } //dont bother if we are immune to fleeing - if(GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) { + if (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) { + LogFlee("Mob [{}] is immune to fleeing via special ability or spell bonus", GetCleanName()); return; } // Check if Flee Timer is cleared - if(!flee_timer.Check()) { + if (!flee_timer.Check()) { return; } - int hpratio = GetIntHPRatio(); - int fleeratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists - Mob *hate_top = GetHateTop(); + int hp_ratio = GetIntHPRatio(); + int flee_ratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists + Mob *hate_top = GetHateTop(); + + LogFlee("Mob [{}] hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio); // Sanity Check for race conditions - if(hate_top == nullptr) { + if (hate_top == nullptr) { return; } // If no special flee_percent check for Gray or Other con rates - if(GetLevelCon(hate_top->GetLevel(), GetLevel()) == CON_GRAY && fleeratio == 0 && RuleB(Combat, FleeGray) && GetLevel() <= RuleI(Combat, FleeGrayMaxLevel)) { - fleeratio = RuleI(Combat, FleeGrayHPRatio); - } else if(fleeratio == 0) { - fleeratio = RuleI(Combat, FleeHPRatio ); + if (GetLevelCon(hate_top->GetLevel(), GetLevel()) == CON_GRAY && flee_ratio == 0 && RuleB(Combat, FleeGray) && + GetLevel() <= RuleI(Combat, FleeGrayMaxLevel)) { + flee_ratio = RuleI(Combat, FleeGrayHPRatio); + LogFlee("Mob [{}] using combat flee gray hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio); + } + else if (flee_ratio == 0) { + flee_ratio = RuleI(Combat, FleeHPRatio); + LogFlee("Mob [{}] using combat flee hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio); } - // Mob does not have low enough health to flee - if(hpratio >= fleeratio) { + bool mob_has_low_enough_health_to_flee = hp_ratio >= flee_ratio; + if (mob_has_low_enough_health_to_flee) { + LogFlee( + "Mob [{}] does not have low enough health to flee | hp_ratio [{}] flee_ratio [{}]", + GetCleanName(), + hp_ratio, + flee_ratio + ); return; } // Sanity Check this should never happen... - if(!hate_top) { + if (!hate_top) { currently_fleeing = true; StartFleeing(); return; @@ -82,14 +96,14 @@ void Mob::CheckFlee() { int other_ratio = hate_top->GetIntHPRatio(); // If the Client is nearing death the NPC will not flee and instead try to kill the client. - if(other_ratio < 20) { + if (other_ratio < 20) { return; } // Flee Chance checking based on con. uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel()); - int flee_chance; - switch(con) { + int flee_chance; + switch (con) { //these values are not 100% researched case CON_GRAY: flee_chance = 100; @@ -108,13 +122,29 @@ void Mob::CheckFlee() { break; } + LogFlee( + "Post con-switch | Mob [{}] con [{}] hp_ratio [{}] flee_ratio [{}] flee_chance [{}]", + GetCleanName(), + hp_ratio, + flee_ratio + ); + // If we got here we are allowed to roll on flee chance if there is not other hated NPC's in the area. // ALWAYS_FLEE, skip roll // if FleeIfNotAlone is true, we skip alone check // roll chance if (GetSpecialAbility(ALWAYS_FLEE) || - ((RuleB(Combat, FleeIfNotAlone) || entity_list.GetHatedCount(hate_top, this, true) == 0) && - zone->random.Roll(flee_chance))) { + ((RuleB(Combat, FleeIfNotAlone) || entity_list.GetHatedCount(hate_top, this, true) == 0) && + zone->random.Roll(flee_chance))) { + + LogFlee( + "Passed all checks to flee | Mob [{}] con [{}] hp_ratio [{}] flee_ratio [{}] flee_chance [{}]", + GetCleanName(), + hp_ratio, + flee_ratio, + flee_chance + ); + currently_fleeing = true; StartFleeing(); } @@ -160,7 +190,8 @@ void Mob::ProcessFlee() } } -void Mob::CalculateNewFearpoint() { +void Mob::CalculateNewFearpoint() +{ if (RuleB(Pathing, Fear) && zone->pathing) { auto Node = zone->pathing->GetRandomLocation(glm::vec3(GetX(), GetY(), GetZ())); if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) { @@ -170,9 +201,7 @@ void Mob::CalculateNewFearpoint() { return; } - Log(Logs::Detail, - Logs::Pathing, - "No path found to selected node during CalculateNewFearpoint."); + LogPathing("No path found to selected node during CalculateNewFearpoint."); } } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2014a643a..0f29c38ce 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4011,10 +4011,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) break; } - case SE_ImmuneFleeing: - { - if(RuleB(Combat, EnableFearPathing)){ - if(flee_mode) { + case SE_ImmuneFleeing: { + if (RuleB(Combat, EnableFearPathing)) { + if (flee_mode) { currently_fleeing = true; CheckFlee(); break; From 662048cedcd739487758a512b0ce527c1de7ea8f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 3 Jan 2020 17:25:53 -0600 Subject: [PATCH 078/157] Make the logs even better! [skip ci] --- zone/fearpath.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 162a4e895..cedf1d410 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -125,8 +125,10 @@ void Mob::CheckFlee() LogFlee( "Post con-switch | Mob [{}] con [{}] hp_ratio [{}] flee_ratio [{}] flee_chance [{}]", GetCleanName(), + con, hp_ratio, - flee_ratio + flee_ratio, + flee_chance ); // If we got here we are allowed to roll on flee chance if there is not other hated NPC's in the area. @@ -140,6 +142,7 @@ void Mob::CheckFlee() LogFlee( "Passed all checks to flee | Mob [{}] con [{}] hp_ratio [{}] flee_ratio [{}] flee_chance [{}]", GetCleanName(), + con, hp_ratio, flee_ratio, flee_chance From e27c4f4f7c83a9102881f51f622b7ab45c3db787 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 3 Jan 2020 17:48:41 -0600 Subject: [PATCH 079/157] Lets fix some overflows [skip ci] --- zone/mob.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index cdd6f7041..f0fc79548 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -495,8 +495,8 @@ public: inline Mob* GetTarget() const { return target; } virtual void SetTarget(Mob* mob); inline bool HasTargetReflection() const { return (target && target != this && target->target == this); } - virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)current_hp/max_hp*100); } - virtual inline int GetIntHPRatio() const { return max_hp == 0 ? 0 : static_cast(current_hp * 100 / max_hp); } + virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float) current_hp / max_hp * 100); } + virtual inline int GetIntHPRatio() const { return max_hp == 0 ? 0 : static_cast(GetHPRatio()); } inline int32 GetAC() const { return AC; } inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; } inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } From 958a6d939ec7b02d0457edb6f71772aa9405547e Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 3 Jan 2020 18:10:30 -0600 Subject: [PATCH 080/157] Have MySQL log timing display correctly --- common/dbcore.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/dbcore.cpp b/common/dbcore.cpp index 2c46f298a..46c7fbe12 100644 --- a/common/dbcore.cpp +++ b/common/dbcore.cpp @@ -146,7 +146,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo LogF( Logs::General, Logs::MySQLQuery, - "{0} ({1} row{2} returned) ({3}ms)", + "{0} ({1} row{2} returned) ({3}s)", query, requestResult.RowCount(), requestResult.RowCount() == 1 ? "" : "s", @@ -157,7 +157,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo LogF( Logs::General, Logs::MySQLQuery, - "{0} ({1} row{2} affected) ({3}ms)", + "{0} ({1} row{2} affected) ({3}s)", query, requestResult.RowsAffected(), requestResult.RowsAffected() == 1 ? "" : "s", From 6243a316442612eb252ea4e4d922f8c0791d0c8a Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 3 Jan 2020 19:12:55 -0800 Subject: [PATCH 081/157] Added ability to use default looping behavior of libuv, in use in zone now --- common/event/event_loop.h | 4 ++++ zone/main.cpp | 24 ++---------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/common/event/event_loop.h b/common/event/event_loop.h index 268f78c4d..295c0532d 100644 --- a/common/event/event_loop.h +++ b/common/event/event_loop.h @@ -21,6 +21,10 @@ namespace EQ uv_run(&m_loop, UV_RUN_NOWAIT); } + void Run() { + uv_run(&m_loop, UV_RUN_DEFAULT); + } + uv_loop_t* Handle() { return &m_loop; } private: diff --git a/zone/main.cpp b/zone/main.cpp index 9ee3eda25..6d3eee96c 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -548,29 +548,9 @@ int main(int argc, char** argv) { }; EQ::Timer process_timer(loop_fn); - process_timer.Start(1000, true); + process_timer.Start(32, true); - while (RunLoops) { - bool previous_loaded = is_zone_loaded && numclients > 0; - EQ::EventLoop::Get().Process(); - - bool current_loaded = is_zone_loaded && numclients > 0; - if (previous_loaded && !current_loaded) { - process_timer.Stop(); - process_timer.Start(1000, true); - } - else if (!previous_loaded && current_loaded) { - process_timer.Stop(); - process_timer.Start(32, true); - } - - if (current_loaded) { - Sleep(1); - } - else { - Sleep(10); - } - } + EQ::EventLoop::Get().Run(); entity_list.Clear(); entity_list.RemoveAllEncounters(); // gotta do it manually or rewrite lots of shit :P From 8ad11d0f857f75158ad09e908c77102299acf671 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 3 Jan 2020 22:31:13 -0500 Subject: [PATCH 082/157] Fix #reloadworld message. --- zone/command.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 9740e0811..a326f4999 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -4252,10 +4252,15 @@ void command_corpsefix(Client *c, const Seperator *sep) void command_reloadworld(Client *c, const Seperator *sep) { - c->Message(Chat::White, "Reloading quest cache and repopping zones worldwide."); + int world_repop = atoi(sep->arg[1]); + if (world_repop == 0) + c->Message(Chat::White, "Reloading quest cache worldwide."); + else + c->Message(Chat::White, "Reloading quest cache and repopping zones worldwide."); + auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer; - RW->Option = ((atoi(sep->arg[1]) == 1) ? 1 : 0); + RW->Option = world_repop; worldserver.SendPacket(pack); safe_delete(pack); } From e5ec277b5e894e307124ab3aff97e67fa3b248bc Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 4 Jan 2020 01:56:38 -0500 Subject: [PATCH 083/157] Temporary bot impl until further work can be done --- zone/bot.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ zone/entity.h | 2 ++ 2 files changed, 46 insertions(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index b7be0c962..b5628ecb4 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2211,6 +2211,17 @@ bool Bot::Process() return false; } + if (mob_scan_close.Check()) { + LogAIScanClose( + "is_moving [{}] bot [{}] timer [{}]", + moving ? "true" : "false", + GetCleanName(), + mob_scan_close.GetDuration() + ); + + entity_list.ScanCloseClientMobs(close_mobs, this); + } + SpellProcess(); if(tic_timer.Check()) { @@ -9377,6 +9388,39 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { return; } +/** + * @param close_mobs + * @param scanning_mob + */ +void EntityList::ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob) +{ + float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); + + close_mobs.clear(); + + for (auto& e : mob_list) { + auto mob = e.second; + + if (!mob->IsClient()) { + continue; + } + + if (mob->GetID() <= 0) { + continue; + } + + float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); + if (distance <= scan_range) { + close_mobs.insert(std::pair(mob->GetID(), mob)); + } + else if (mob->GetAggroRange() >= scan_range) { + close_mobs.insert(std::pair(mob->GetID(), mob)); + } + } + + LogAIScanClose("Close Client Mob List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); +} + uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets) { uint8 needHealed = 0; Group *g = nullptr; diff --git a/zone/entity.h b/zone/entity.h index ea97446a6..a296ca794 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -579,6 +579,8 @@ private: bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff + + void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob); private: std::list bot_list; #endif From eed4a648b4f004645cd6cfe9d2cd9c4d823d2f61 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sat, 4 Jan 2020 16:17:25 -0500 Subject: [PATCH 084/157] Added addition usage message for #grid for #grid show. --- zone/command.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/command.cpp b/zone/command.cpp index a326f4999..cf8e47d6a 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2503,6 +2503,7 @@ void command_grid(Client *c, const Seperator *sep) else { c->Message(Chat::White, "Usage: #grid add/delete grid_num wandertype pausetype"); c->Message(Chat::White, "Usage: #grid max - displays the highest grid ID used in this zone (for add)"); + c->Message(Chat::White, "Usage: #grid show - displays wp nodes as boxes"); } } From 852d951b650e1be3fd6ea02ae7863111e31f87a1 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 6 Jan 2020 15:41:05 -0500 Subject: [PATCH 085/157] Reworked EVENT_COMMAND handling and addressed script commands not present during help use --- zone/bot_command.cpp | 6 ++++++ zone/client.cpp | 11 ++++++----- zone/command.cpp | 6 ++++++ zone/embparser.cpp | 15 +++++++++++---- zone/event_codes.h | 1 + zone/lua_parser.cpp | 4 +++- zone/lua_parser_events.cpp | 19 +++++++++++++++++++ zone/lua_parser_events.h | 2 ++ 8 files changed, 54 insertions(+), 10 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 8030d4898..dc9d40aab 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -3414,6 +3414,12 @@ void bot_command_help(Client *c, const Seperator *sep) c->Message(m_usage, "%c%s - %s", BOT_COMMAND_CHAR, command_iter.first.c_str(), command_iter.second->desc == nullptr ? "[no description]" : command_iter.second->desc); ++bot_commands_shown; } + if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, sep->msg, 0); + if (i >= 1) { + bot_commands_shown += i; + } + } c->Message(m_message, "%d bot command%s listed.", bot_commands_shown, bot_commands_shown != 1 ? "s" : ""); c->Message(m_note, "type %ccommand [help | usage] for more information", BOT_COMMAND_CHAR); } diff --git a/zone/client.cpp b/zone/client.cpp index f77dc1fd4..d258ae7bf 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1119,14 +1119,11 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s break; } - if (EQEmu::ProfanityManager::IsCensorshipActive()) - EQEmu::ProfanityManager::RedactMessage(message); - #ifdef BOTS if (message[0] == BOT_COMMAND_CHAR) { if (bot_command_dispatch(this, message) == -2) { - if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { - int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0); + if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + int i = parse->EventPlayer(EVENT_BOT_COMMAND, this, message, 0); if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { Message(Chat::Red, "Bot command '%s' not recognized.", message); } @@ -1140,6 +1137,10 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } #endif + if (EQEmu::ProfanityManager::IsCensorshipActive()) { + EQEmu::ProfanityManager::RedactMessage(message); + } + Mob* sender = this; if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) sender = GetPet(); diff --git a/zone/command.cpp b/zone/command.cpp index cf8e47d6a..1f5da287c 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -783,6 +783,12 @@ void command_help(Client *c, const Seperator *sep) commands_shown++; c->Message(Chat::White, " %c%s %s", COMMAND_CHAR, cur->first.c_str(), cur->second->desc == nullptr?"":cur->second->desc); } + if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { + int i = parse->EventPlayer(EVENT_COMMAND, c, sep->msg, 0); + if (i >= 1) { + commands_shown += i; + } + } c->Message(Chat::White, "%d command%s listed.", commands_shown, commands_shown!=1?"s":""); } diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 920d3a249..0f6fffd69 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -98,7 +98,7 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_DUEL_LOSE", "EVENT_ENCOUNTER_LOAD", "EVENT_ENCOUNTER_UNLOAD", - "EVENT_SAY", + "EVENT_COMMAND", "EVENT_DROP_ITEM", "EVENT_DESTROY_ITEM", "EVENT_FEIGN_DEATH", @@ -119,6 +119,7 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_DEATH_ZONE", "EVENT_USE_SKILL", "EVENT_COMBINE_VALIDATE", + "EVENT_BOT_COMMAND" }; PerlembParser::PerlembParser() : perl(nullptr) @@ -1542,9 +1543,9 @@ void PerlembParser::ExportEventVariables( } case EVENT_COMMAND: { - ExportVar(package_name.c_str(), "text", data); - ExportVar(package_name.c_str(), "data", "0"); - ExportVar(package_name.c_str(), "langid", "0"); + Seperator sep(data); + ExportVar(package_name.c_str(), "command", (sep.arg[0] + 1)); + ExportVar(package_name.c_str(), "args", (sep.argnum > 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); break; } @@ -1610,6 +1611,12 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "tradeskill_id", tradeskill_id.c_str()); break; } + case EVENT_BOT_COMMAND: { + Seperator sep(data); + ExportVar(package_name.c_str(), "bot_command", (sep.arg[0] + 1)); + ExportVar(package_name.c_str(), "args", (sep.argnum > 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); + break; + } default: { break; diff --git a/zone/event_codes.h b/zone/event_codes.h index 4560cc767..110101cf9 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -87,6 +87,7 @@ typedef enum { EVENT_DEATH_ZONE, EVENT_USE_SKILL, EVENT_COMBINE_VALIDATE, + EVENT_BOT_COMMAND, _LargestEventID } QuestEventID; diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 437bc5121..620ee14d1 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -124,7 +124,8 @@ const char *LuaEvents[_LargestEventID] = { "event_spawn_zone", "event_death_zone", "event_use_skill", - "event_combine_validate" + "event_combine_validate", + "event_bot_command" }; extern Zone *zone; @@ -208,6 +209,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet; PlayerArgumentDispatch[EVENT_USE_SKILL] = handle_player_use_skill; PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate; + PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command; ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index f4f1dd3af..3ca9edad7 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -539,6 +539,25 @@ void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* lua_setfield(L, -2, "tradeskill_id"); } +void handle_player_bot_command(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers) { + Seperator sep(data.c_str(), ' ', 10, 100, true); + std::string bot_command(sep.arg[0] + 1); + lua_pushstring(L, bot_command.c_str()); + lua_setfield(L, -2, "bot_command"); + + luabind::adl::object args = luabind::newtable(L); + int max_args = sep.GetMaxArgNum(); + for (int i = 1; i < max_args; ++i) { + if (strlen(sep.arg[i]) > 0) { + args[i] = std::string(sep.arg[i]); + } + } + + args.push(L); + lua_setfield(L, -2, "args"); +} + //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers) { diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 0054b31e1..9eeeb11cb 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -99,6 +99,8 @@ void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client std::vector *extra_pointers); void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers); +void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector *extra_pointers); //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, From 77ae4f0c3fff7286ee3afd10f9e68f5a077df16c Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 7 Jan 2020 16:09:14 -0600 Subject: [PATCH 086/157] Update missing aliases for when log compilation flag is turned off [skip ci] --- common/eqemu_logsys_log_aliases.h | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 138a6f851..1c003fff9 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -842,6 +842,42 @@ #define LogStatusDetail(message, ...) do {\ } while (0) +#define LogAIScanClose(message, ...) do {\ +} while (0) + +#define LogAIScanCloseDetail(message, ...) do {\ +} while (0) + +#define LogAIYellForHelp(message, ...) do {\ +} while (0) + +#define LogAIYellForHelpDetail(message, ...) do {\ +} while (0) + +#define LogAICastBeneficialClose(message, ...) do {\ +} while (0) + +#define LogAICastBeneficialCloseDetail(message, ...) do {\ +} while (0) + +#define LogAoeCast(message, ...) do {\ +} while (0) + +#define LogAoeCastDetail(message, ...) do {\ +} while (0) + +#define LogEntityManagement(message, ...) do {\ +} while (0) + +#define LogEntityManagementDetail(message, ...) do {\ +} while (0) + +#define LogFlee(message, ...) do {\ +} while (0) + +#define LogFleeDetail(message, ...) do {\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ } while (0) From b9e87abb3cb63ecf9f343ec6bbbbd508dee4ca2f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Jan 2020 02:54:29 -0600 Subject: [PATCH 087/157] Implement Character Soft Deletes --- common/database.cpp | 121 ++++--- common/database.h | 2 +- common/ruletypes.h | 1 + common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../2020_01_10_character_soft_deletes.sql | 1 + world/worlddb.cpp | 334 ++++++++++-------- world/worlddb.h | 2 +- 8 files changed, 269 insertions(+), 195 deletions(-) create mode 100644 utils/sql/git/required/2020_01_10_character_soft_deletes.sql diff --git a/common/database.cpp b/common/database.cpp index fde091322..2723b8102 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -341,65 +341,88 @@ bool Database::ReserveName(uint32 account_id, char* name) { return true; } -/* - Delete the character with the name "name" - returns false on failure, true otherwise -*/ -bool Database::DeleteCharacter(char *name) { - uint32 charid = 0; - if(!name || !strlen(name)) { +/** + * @param character_name + * @return + */ +bool Database::DeleteCharacter(char *character_name) { + uint32 character_id = 0; + if(!character_name || !strlen(character_name)) { LogInfo("DeleteCharacter: request to delete without a name (empty char slot)"); return false; } - LogInfo("Database::DeleteCharacter name : [{}]", name); - /* Get id from character_data before deleting record so we can clean up the rest of the tables */ - std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", name); - auto results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { charid = atoi(row[0]); } - if (charid <= 0){ - LogError("Database::DeleteCharacter :: Character ({}) not found, stopping delete...", name); + std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", character_name); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + character_id = atoi(row[0]); + } + + if (character_id <= 0) { + LogError("DeleteCharacter | Invalid Character ID [{}]", character_name); return false; } - query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", charid); QueryDatabase(query); + std::string delete_type = "hard-deleted"; + if (RuleB(Character, SoftDeletes)) { + delete_type = "soft-deleted"; + std::string query = fmt::format( + SQL( + UPDATE + character_data + SET + deleted_at = NOW() + WHERE + id = '{}' + ), + character_id + ); + + QueryDatabase(query); + + return true; + } + + 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); #ifdef BOTS query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", charid); // note: only use of GetMobTypeById() #else - query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", charid); + query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", character_id); #endif QueryDatabase(query); diff --git a/common/database.h b/common/database.h index c84b5301c..f569da20b 100644 --- a/common/database.h +++ b/common/database.h @@ -107,7 +107,7 @@ public: bool AddToNameFilter(const char* name); bool CreateCharacter(uint32 account_id, char* name, uint16 gender, uint16 race, uint16 class_, uint8 str, uint8 sta, uint8 cha, uint8 dex, uint8 int_, uint8 agi, uint8 wis, uint8 face); - bool DeleteCharacter(char* name); + bool DeleteCharacter(char* character_name); bool MoveCharacterToZone(const char* charname, const char* zonename); bool MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid); bool MoveCharacterToZone(uint32 iCharID, const char* iZonename); diff --git a/common/ruletypes.h b/common/ruletypes.h index c6e99e5f4..3de392d45 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -158,6 +158,7 @@ RULE_BOOL(Character, AllowCrossClassTrainers, false, "") 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_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/common/version.h b/common/version.h index 17e69601a..0f659d57b 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 9145 +#define CURRENT_BINARY_DATABASE_VERSION 9146 #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 e68450e7d..aa7897e7f 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -399,6 +399,7 @@ 9143|2019_09_16_account_table_changes.sql|SHOW COLUMNS FROM `account` LIKE 'ls_id'|empty| 9144|2019_11_09_logsys_description_update.sql|SELECT * FROM db_version WHERE version >= 9143|empty| 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| # 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_10_character_soft_deletes.sql b/utils/sql/git/required/2020_01_10_character_soft_deletes.sql new file mode 100644 index 000000000..17496b141 --- /dev/null +++ b/utils/sql/git/required/2020_01_10_character_soft_deletes.sql @@ -0,0 +1 @@ +ALTER TABLE `character_data` ADD COLUMN `deleted_at` datetime NULL DEFAULT NULL; \ No newline at end of file diff --git a/world/worlddb.cpp b/world/worlddb.cpp index fbd4cdf94..3682b93f6 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -31,23 +31,27 @@ extern std::vector character_create_allocations; extern std::vector character_create_race_class_combos; -// the current stuff is at the bottom of this function -void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit) +/** + * @param account_id + * @param out_app + * @param client_version_bit + */ +void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit) { - /* Set Character Creation Limit */ - EQEmu::versions::ClientVersion client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(clientVersionBit); + EQEmu::versions::ClientVersion size_t character_limit = EQEmu::constants::StaticLookup(client_version)->CharacterCreationLimit; // Validate against absolute server max if (character_limit > EQEmu::constants::CHARACTER_CREATION_LIMIT) character_limit = EQEmu::constants::CHARACTER_CREATION_LIMIT; + } // Force Titanium clients to use '8' - if (client_version == EQEmu::versions::ClientVersion::Titanium) + if (client_version == EQEmu::versions::ClientVersion::Titanium) { character_limit = 8; - - /* Get Character Info */ - std::string cquery = StringFormat( + } + + std::string character_list_query = StringFormat( "SELECT " "`id`, " // 0 "name, " // 1 @@ -71,237 +75,281 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou "zone_id " // 19 "FROM " "character_data " - "WHERE `account_id` = %i ORDER BY `name` LIMIT %u", accountID, character_limit); - auto results = database.QueryDatabase(cquery); + "WHERE `account_id` = %i AND deleted_at IS NULL ORDER BY `name` LIMIT %u", + account_id, + character_limit + ); + + auto results = database.QueryDatabase(character_list_query); size_t character_count = results.RowCount(); if (character_count == 0) { - *outApp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); - CharacterSelect_Struct *cs = (CharacterSelect_Struct *)(*outApp)->pBuffer; - cs->CharCount = 0; + *out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); + CharacterSelect_Struct *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer; + cs->CharCount = 0; cs->TotalChars = character_limit; return; } size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count); - *outApp = new EQApplicationPacket(OP_SendCharInfo, packet_size); + *out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size); - unsigned char *buff_ptr = (*outApp)->pBuffer; - CharacterSelect_Struct *cs = (CharacterSelect_Struct *)buff_ptr; + unsigned char *buff_ptr = (*out_app)->pBuffer; + CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr; - cs->CharCount = character_count; + cs->CharCount = character_count; cs->TotalChars = character_limit; buff_ptr += sizeof(CharacterSelect_Struct); for (auto row = results.begin(); row != results.end(); ++row) { - CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr; - PlayerProfile_Struct pp; - EQEmu::InventoryProfile inv; + CharacterSelectEntry_Struct *p_character_select_entry_struct = (CharacterSelectEntry_Struct *) buff_ptr; + PlayerProfile_Struct player_profile_struct; + EQEmu::InventoryProfile inventory_profile; - pp.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version)); - inv.SetInventoryVersion(client_version); - inv.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support + player_profile_struct.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version)); + inventory_profile.SetInventoryVersion(client_version); + inventory_profile.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support - uint32 character_id = (uint32)atoi(row[0]); - uint8 has_home = 0; - uint8 has_bind = 0; + uint32 character_id = (uint32) atoi(row[0]); + uint8 has_home = 0; + uint8 has_bind = 0; - memset(&pp, 0, sizeof(PlayerProfile_Struct)); - - /* Fill CharacterSelectEntry_Struct */ - memset(cse->Name, 0, sizeof(cse->Name)); - strcpy(cse->Name, row[1]); - cse->Class = (uint8)atoi(row[4]); - cse->Race = (uint32)atoi(row[3]); - cse->Level = (uint8)atoi(row[5]); - cse->ShroudClass = cse->Class; - cse->ShroudRace = cse->Race; - cse->Zone = (uint16)atoi(row[19]); - cse->Instance = 0; - cse->Gender = (uint8)atoi(row[2]); - cse->Face = (uint8)atoi(row[15]); + memset(&player_profile_struct, 0, sizeof(PlayerProfile_Struct)); - for (uint32 matslot = 0; matslot < EQEmu::textures::materialCount; matslot++) { // Processed below - cse->Equip[matslot].Material = 0; - cse->Equip[matslot].Unknown1 = 0; - cse->Equip[matslot].EliteModel = 0; - cse->Equip[matslot].HerosForgeModel = 0; - cse->Equip[matslot].Unknown2 = 0; - cse->Equip[matslot].Color = 0; - } + memset(p_character_select_entry_struct->Name, 0, sizeof(p_character_select_entry_struct->Name)); + strcpy(p_character_select_entry_struct->Name, row[1]); + p_character_select_entry_struct->Class = (uint8) atoi(row[4]); + p_character_select_entry_struct->Race = (uint32) atoi(row[3]); + p_character_select_entry_struct->Level = (uint8) atoi(row[5]); + p_character_select_entry_struct->ShroudClass = p_character_select_entry_struct->Class; + p_character_select_entry_struct->ShroudRace = p_character_select_entry_struct->Race; + p_character_select_entry_struct->Zone = (uint16) atoi(row[19]); + p_character_select_entry_struct->Instance = 0; + p_character_select_entry_struct->Gender = (uint8) atoi(row[2]); + p_character_select_entry_struct->Face = (uint8) atoi(row[15]); - cse->Unknown15 = 0xFF; - cse->Unknown19 = 0xFF; - cse->DrakkinTattoo = (uint32)atoi(row[17]); - cse->DrakkinDetails = (uint32)atoi(row[18]); - cse->Deity = (uint32)atoi(row[6]); - cse->PrimaryIDFile = 0; // Processed Below - cse->SecondaryIDFile = 0; // Processed Below - cse->HairColor = (uint8)atoi(row[9]); - cse->BeardColor = (uint8)atoi(row[10]); - cse->EyeColor1 = (uint8)atoi(row[11]); - cse->EyeColor2 = (uint8)atoi(row[12]); - cse->HairStyle = (uint8)atoi(row[13]); - cse->Beard = (uint8)atoi(row[14]); - cse->GoHome = 0; // Processed Below - cse->Tutorial = 0; // Processed Below - cse->DrakkinHeritage = (uint32)atoi(row[16]); - cse->Unknown1 = 0; - cse->Enabled = 1; - cse->LastLogin = (uint32)atoi(row[7]); // RoF2 value: 1212696584 - cse->Unknown2 = 0; - /* Fill End */ + for (uint32 material_slot = 0; material_slot < EQEmu::textures::materialCount; material_slot++) { + p_character_select_entry_struct->Equip[material_slot].Material = 0; + p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0; + p_character_select_entry_struct->Equip[material_slot].EliteModel = 0; + p_character_select_entry_struct->Equip[material_slot].HerosForgeModel = 0; + p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0; + p_character_select_entry_struct->Equip[material_slot].Color = 0; + } + + p_character_select_entry_struct->Unknown15 = 0xFF; + p_character_select_entry_struct->Unknown19 = 0xFF; + p_character_select_entry_struct->DrakkinTattoo = (uint32) atoi(row[17]); + p_character_select_entry_struct->DrakkinDetails = (uint32) atoi(row[18]); + p_character_select_entry_struct->Deity = (uint32) atoi(row[6]); + p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below + p_character_select_entry_struct->SecondaryIDFile = 0; // Processed Below + p_character_select_entry_struct->HairColor = (uint8) atoi(row[9]); + p_character_select_entry_struct->BeardColor = (uint8) atoi(row[10]); + p_character_select_entry_struct->EyeColor1 = (uint8) atoi(row[11]); + p_character_select_entry_struct->EyeColor2 = (uint8) atoi(row[12]); + p_character_select_entry_struct->HairStyle = (uint8) atoi(row[13]); + p_character_select_entry_struct->Beard = (uint8) atoi(row[14]); + p_character_select_entry_struct->GoHome = 0; // Processed Below + p_character_select_entry_struct->Tutorial = 0; // Processed Below + p_character_select_entry_struct->DrakkinHeritage = (uint32) atoi(row[16]); + p_character_select_entry_struct->Unknown1 = 0; + p_character_select_entry_struct->Enabled = 1; + p_character_select_entry_struct->LastLogin = (uint32) atoi(row[7]); // RoF2 value: 1212696584 + p_character_select_entry_struct->Unknown2 = 0; if (RuleB(World, EnableReturnHomeButton)) { int now = time(nullptr); - if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) - cse->GoHome = 1; + if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) { + p_character_select_entry_struct->GoHome = 1; + } } - if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) { - cse->Tutorial = 1; + if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial))) { + p_character_select_entry_struct->Tutorial = 1; } - /* Set Bind Point Data for any character that may possibly be missing it for any reason */ - cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5", character_id); - auto results_bind = database.QueryDatabase(cquery); - auto bind_count = results_bind.RowCount(); - for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { + /** + * Bind + */ + character_list_query = StringFormat( + "SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5", + character_id + ); + auto results_bind = database.QueryDatabase(character_list_query); + auto bind_count = results_bind.RowCount(); + for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { if (row_b[6] && atoi(row_b[6]) == 4) { has_home = 1; // If our bind count is less than 5, we need to actually make use of this data so lets parse it if (bind_count < 5) { - pp.binds[4].zoneId = atoi(row_b[0]); - pp.binds[4].instance_id = atoi(row_b[1]); - pp.binds[4].x = atof(row_b[2]); - pp.binds[4].y = atof(row_b[3]); - pp.binds[4].z = atof(row_b[4]); - pp.binds[4].heading = atof(row_b[5]); + player_profile_struct.binds[4].zoneId = atoi(row_b[0]); + player_profile_struct.binds[4].instance_id = atoi(row_b[1]); + player_profile_struct.binds[4].x = atof(row_b[2]); + player_profile_struct.binds[4].y = atof(row_b[3]); + player_profile_struct.binds[4].z = atof(row_b[4]); + player_profile_struct.binds[4].heading = atof(row_b[5]); } } - if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; } + if (row_b[6] && atoi(row_b[6]) == 0) { has_bind = 1; } } if (has_home == 0 || has_bind == 0) { - cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", - cse->Class, cse->Deity, cse->Race); - auto results_bind = database.QueryDatabase(cquery); - for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { + character_list_query = StringFormat( + "SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", + p_character_select_entry_struct->Class, + p_character_select_entry_struct->Deity, + p_character_select_entry_struct->Race + ); + auto results_bind = database.QueryDatabase(character_list_query); + for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { /* If a bind_id is specified, make them start there */ if (atoi(row_d[1]) != 0) { - pp.binds[4].zoneId = (uint32)atoi(row_d[1]); - GetSafePoints(pp.binds[4].zoneId, 0, &pp.binds[4].x, &pp.binds[4].y, &pp.binds[4].z); + player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[1]); + GetSafePoints(player_profile_struct.binds[4].zoneId, 0, &player_profile_struct.binds[4].x, &player_profile_struct.binds[4].y, &player_profile_struct.binds[4].z); } - /* Otherwise, use the zone and coordinates given */ + /* Otherwise, use the zone and coordinates given */ else { - pp.binds[4].zoneId = (uint32)atoi(row_d[0]); + player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[0]); float x = atof(row_d[2]); float y = atof(row_d[3]); float z = atof(row_d[4]); - if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } - pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z; + if (x == 0 && y == 0 && z == 0) { GetSafePoints(player_profile_struct.binds[4].zoneId, 0, &x, &y, &z); } + player_profile_struct.binds[4].x = x; + player_profile_struct.binds[4].y = y; + player_profile_struct.binds[4].z = z; } } - pp.binds[0] = pp.binds[4]; + player_profile_struct.binds[0] = player_profile_struct.binds[4]; /* If no home bind set, set it */ if (has_home == 0) { - std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" + std::string query = StringFormat( + "REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", - character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 4); - auto results_bset = QueryDatabase(query); + character_id, + player_profile_struct.binds[4].zoneId, + 0, + player_profile_struct.binds[4].x, + player_profile_struct.binds[4].y, + player_profile_struct.binds[4].z, + player_profile_struct.binds[4].heading, + 4 + ); + auto results_bset = QueryDatabase(query); } /* If no regular bind set, set it */ if (has_bind == 0) { - std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" + std::string query = StringFormat( + "REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", - character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0); - auto results_bset = QueryDatabase(query); + character_id, + player_profile_struct.binds[0].zoneId, + 0, + player_profile_struct.binds[0].x, + player_profile_struct.binds[0].y, + player_profile_struct.binds[0].z, + player_profile_struct.binds[0].heading, + 0 + ); + auto results_bset = QueryDatabase(query); } } /* If our bind count is less than 5, then we have null data that needs to be filled in. */ if (bind_count < 5) { // we know that home and main bind must be valid here, so we don't check those // we also use home to fill in the null data like live does. - for (int i = 1; i < 4; i++) { - if (pp.binds[i].zoneId != 0) // we assume 0 is the only invalid one ... + for (int i = 1; i < 4; i++) { + if (player_profile_struct.binds[i].zoneId != 0) { // we assume 0 is the only invalid one ... continue; + } - std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" + std::string query = StringFormat( + "REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", - character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, i); - auto results_bset = QueryDatabase(query); + character_id, + player_profile_struct.binds[4].zoneId, + 0, + player_profile_struct.binds[4].x, + player_profile_struct.binds[4].y, + player_profile_struct.binds[4].z, + player_profile_struct.binds[4].heading, + i + ); + auto results_bset = QueryDatabase(query); } } - /* Bind End */ - /* Load Character Material Data for Char Select */ - cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); - auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; - for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { + character_list_query = StringFormat( + "SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", + character_id + ); + auto results_b = database.QueryDatabase(character_list_query); + uint8 slot = 0; + for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { slot = atoi(row_b[0]); - pp.item_tint.Slot[slot].Red = atoi(row_b[1]); - pp.item_tint.Slot[slot].Green = atoi(row_b[2]); - pp.item_tint.Slot[slot].Blue = atoi(row_b[3]); - pp.item_tint.Slot[slot].UseTint = atoi(row_b[4]); + player_profile_struct.item_tint.Slot[slot].Red = atoi(row_b[1]); + player_profile_struct.item_tint.Slot[slot].Green = atoi(row_b[2]); + player_profile_struct.item_tint.Slot[slot].Blue = atoi(row_b[3]); + player_profile_struct.item_tint.Slot[slot].UseTint = atoi(row_b[4]); } - /* Character Material Data End */ - /* Load Inventory */ - // If we ensure that the material data is updated appropriately, we can do away with inventory loads - if (GetCharSelInventory(accountID, cse->Name, &inv)) { - const EQEmu::ItemData* item = nullptr; - const EQEmu::ItemInstance* inst = nullptr; - int16 invslot = 0; + if (GetCharSelInventory(account_id, p_character_select_entry_struct->Name, &inventory_profile)) { + const EQEmu::ItemData *item = nullptr; + const EQEmu::ItemInstance *inst = nullptr; + int16 inventory_slot = 0; for (uint32 matslot = EQEmu::textures::textureBegin; matslot < EQEmu::textures::materialCount; matslot++) { - invslot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot); - if (invslot == INVALID_INDEX) { continue; } - inst = inv.GetItem(invslot); - if (inst == nullptr) { continue; } + inventory_slot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot); + if (inventory_slot == INVALID_INDEX) { continue; } + inst = inventory_profile.GetItem(inventory_slot); + if (inst == nullptr) { + continue; + } item = inst->GetItem(); - if (item == nullptr) { continue; } + if (item == nullptr) { + continue; + } if (matslot > 6) { - uint32 idfile = 0; + uint32 item_id_file = 0; // Weapon Models if (inst->GetOrnamentationIDFile() != 0) { - idfile = inst->GetOrnamentationIDFile(); - cse->Equip[matslot].Material = idfile; + item_id_file = inst->GetOrnamentationIDFile(); + p_character_select_entry_struct->Equip[matslot].Material = item_id_file; } else { if (strlen(item->IDFile) > 2) { - idfile = atoi(&item->IDFile[2]); - cse->Equip[matslot].Material = idfile; + item_id_file = atoi(&item->IDFile[2]); + p_character_select_entry_struct->Equip[matslot].Material = item_id_file; } } if (matslot == EQEmu::textures::weaponPrimary) { - cse->PrimaryIDFile = idfile; + p_character_select_entry_struct->PrimaryIDFile = item_id_file; } else { - cse->SecondaryIDFile = idfile; + p_character_select_entry_struct->SecondaryIDFile = item_id_file; } } else { uint32 color = 0; - if (pp.item_tint.Slot[matslot].UseTint) { - color = pp.item_tint.Slot[matslot].Color; + if (player_profile_struct.item_tint.Slot[matslot].UseTint) { + color = player_profile_struct.item_tint.Slot[matslot].Color; } else { color = inst->GetColor(); } // Armor Materials/Models - cse->Equip[matslot].Material = item->Material; - cse->Equip[matslot].EliteModel = item->EliteMaterial; - cse->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot); - cse->Equip[matslot].Color = color; + p_character_select_entry_struct->Equip[matslot].Material = item->Material; + p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial; + p_character_select_entry_struct->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot); + p_character_select_entry_struct->Equip[matslot].Color = color; } } } else { - printf("Error loading inventory for %s\n", cse->Name); + printf("Error loading inventory for %s\n", p_character_select_entry_struct->Name); } - /* Load Inventory End */ buff_ptr += sizeof(CharacterSelectEntry_Struct); } diff --git a/world/worlddb.h b/world/worlddb.h index 036c0dc40..e367803ec 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -30,7 +30,7 @@ struct CharacterSelect_Struct; class WorldDatabase : public SharedDatabase { public: bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc, bool isTitanium); - void GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit); + void GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit); int MoveCharacterToBind(int CharID, uint8 bindnum = 0); void GetLauncherList(std::vector &result); From f81b9d82448b666585591b8513f126f3fddf6f8d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Jan 2020 02:54:34 -0600 Subject: [PATCH 088/157] Update worlddb.cpp --- world/worlddb.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 3682b93f6..f45207273 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -39,10 +39,10 @@ extern std::vector character_create_race_class_combos; void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit) { EQEmu::versions::ClientVersion + client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(client_version_bit); size_t character_limit = EQEmu::constants::StaticLookup(client_version)->CharacterCreationLimit; - - // Validate against absolute server max - if (character_limit > EQEmu::constants::CHARACTER_CREATION_LIMIT) + + if (character_limit > EQEmu::constants::CHARACTER_CREATION_LIMIT) { character_limit = EQEmu::constants::CHARACTER_CREATION_LIMIT; } From d1fb74ff5f17e61774ca0623041fb796e3bfb7e5 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 10 Jan 2020 02:59:52 -0600 Subject: [PATCH 089/157] Fix bot table --- common/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/database.cpp b/common/database.cpp index 2723b8102..f6adc3659 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -420,7 +420,7 @@ bool Database::DeleteCharacter(char *character_name) { 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); #ifdef BOTS - query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", charid); // note: only use of GetMobTypeById() + 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 From 3c6cdd09051544beb15361d3c5d92b6f65bb06cf Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 11 Jan 2020 18:02:01 -0600 Subject: [PATCH 090/157] Update character_table_list.txt for now until replaced [skip ci] --- utils/sql/character_table_list.txt | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/utils/sql/character_table_list.txt b/utils/sql/character_table_list.txt index 8a47abf67..385827379 100644 --- a/utils/sql/character_table_list.txt +++ b/utils/sql/character_table_list.txt @@ -1,9 +1,16 @@ +aa_timers +account +account_ip +account_flags +account_rewards +adventure_details adventure_stats +buyer char_recipe_list -character_auras character_activities character_alt_currency character_alternate_abilities +character_auras character_bandolier character_bind character_buffs @@ -20,15 +27,22 @@ character_leadership_abilities character_material character_memmed_spells character_pet_buffs +character_pet_info character_pet_inventory character_potionbelt character_skills character_spells +character_tasks character_tribute completed_tasks +data_buckets faction_values friends +guild_bank guild_members +guild_ranks +guild_relations +guilds instance_list_player inventory inventory_snapshots @@ -36,6 +50,9 @@ keyring mail player_titlesets quest_globals +sharedbank timers titles -zone_flags +trader +trader_audit +zone_flags" \ No newline at end of file From 9dacd0bd7a17f48966449b8c354127c353f848fa Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 11 Jan 2020 18:19:27 -0600 Subject: [PATCH 091/157] Remove aa_timers from character tables [skip ci] --- common/database_schema.h | 1 - utils/scripts/schema.xml | 5738 ---------------------------- utils/sql/character_table_list.txt | 1 - 3 files changed, 5740 deletions(-) delete mode 100644 utils/scripts/schema.xml diff --git a/common/database_schema.h b/common/database_schema.h index 6033e85b5..73637c877 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -33,7 +33,6 @@ namespace DatabaseSchema { static std::vector GetPlayerTables() { std::vector tables = { - "aa_timers", "account", "account_ip", "account_flags", diff --git a/utils/scripts/schema.xml b/utils/scripts/schema.xml deleted file mode 100644 index dcfa687be..000000000 --- a/utils/scripts/schema.xml +++ /dev/null @@ -1,5738 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - -
diff --git a/utils/sql/character_table_list.txt b/utils/sql/character_table_list.txt index 385827379..ab6610dab 100644 --- a/utils/sql/character_table_list.txt +++ b/utils/sql/character_table_list.txt @@ -1,4 +1,3 @@ -aa_timers account account_ip account_flags From 6c2100a650f7a78cc96a43a5c290b605da5e2382 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 12 Jan 2020 01:32:51 -0500 Subject: [PATCH 092/157] Fix for perl EVENT_COMMAND not exporting single arguments --- zone/embparser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 0f6fffd69..ef1b404a2 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -1545,7 +1545,7 @@ void PerlembParser::ExportEventVariables( case EVENT_COMMAND: { Seperator sep(data); ExportVar(package_name.c_str(), "command", (sep.arg[0] + 1)); - ExportVar(package_name.c_str(), "args", (sep.argnum > 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); + ExportVar(package_name.c_str(), "args", (sep.argnum >= 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); break; } @@ -1614,7 +1614,7 @@ void PerlembParser::ExportEventVariables( case EVENT_BOT_COMMAND: { Seperator sep(data); ExportVar(package_name.c_str(), "bot_command", (sep.arg[0] + 1)); - ExportVar(package_name.c_str(), "args", (sep.argnum > 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); + ExportVar(package_name.c_str(), "args", (sep.argnum >= 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); break; } From 306a08b9ac428a4e8527adb9f9439bdfaa47d835 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 12 Jan 2020 19:37:53 -0600 Subject: [PATCH 093/157] Unify / streamline scanning logic calls, do a manual scan on enter and cleanup logging --- zone/client_packet.cpp | 2 ++ zone/client_process.cpp | 23 +---------------------- zone/entity.cpp | 7 ++++++- zone/npc.cpp | 6 ------ 4 files changed, 9 insertions(+), 29 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4291cd054..36081601d 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -905,6 +905,8 @@ void Client::CompleteConnect() entity_list.RefreshClientXTargets(this); worldserver.RequestTellQueue(GetName()); + + entity_list.ScanCloseMobs(close_mobs, this); } // connecting opcode handlers diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 14b2036cf..d8ec451a9 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -256,28 +256,7 @@ bool Client::Process() { * Used in aggro checks */ if (mob_close_scan_timer.Check()) { - close_mobs.clear(); - - float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); - auto &mob_list = entity_list.GetMobList(); - - for (auto itr : mob_list) { - Mob *mob = itr.second; - float distance = DistanceSquared(m_Position, mob->GetPosition()); - - if (mob->GetID() <= 0) { - continue; - } - - if (mob->IsNPC() || mob->IsClient()) { - if (distance <= scan_range) { - close_mobs.insert(std::pair(mob->GetID(), mob)); - } - else if ((mob->GetAggroRange() * mob->GetAggroRange()) > scan_range) { - close_mobs.insert(std::pair(mob->GetID(), mob)); - } - } - } + entity_list.ScanCloseMobs(close_mobs, this); } bool may_use_attacks = false; diff --git a/zone/entity.cpp b/zone/entity.cpp index adb29011f..dc90a41b1 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2654,7 +2654,12 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mo } } - LogAIScanClose("Close List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); + LogAIScanClose( + "[{}] Scanning Close List | list_size [{}] moving [{}]", + scanning_mob->GetCleanName(), + close_mobs.size(), + scanning_mob->IsMoving() ? "true" : "false" + ); } bool EntityList::RemoveMerc(uint16 delete_id) diff --git a/zone/npc.cpp b/zone/npc.cpp index 8b4996177..ed5f37bb5 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -705,12 +705,6 @@ bool NPC::Process() SpellProcess(); if (mob_scan_close.Check()) { - LogAIScanClose( - "is_moving [{}] npc [{}] timer [{}]", - moving ? "true" : "false", - GetCleanName(), - mob_scan_close.GetDuration() - ); entity_list.ScanCloseMobs(close_mobs, this); From 6366a3fa38696e20fcfcc3f4490ee763be405bba Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 12 Jan 2020 21:11:43 -0500 Subject: [PATCH 094/157] Fix for silent saylinks and EVENT_COMMAND calls --- zone/client_packet.cpp | 17 +++++++++++++++++ zone/embparser.cpp | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4291cd054..2be564058 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8286,6 +8286,15 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) if (GetTarget() && GetTarget()->IsNPC()) { if (silentsaylink) { parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); + + if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) { + parse->EventPlayer(EVENT_COMMAND, this, response.substr(1).c_str(), 0); + } +#ifdef BOTS + else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + parse->EventPlayer(EVENT_BOT_COMMAND, this, response.substr(1).c_str(), 0); + } +#endif parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); } else { @@ -8296,6 +8305,14 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) } else { if (silentsaylink) { + if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) { + parse->EventPlayer(EVENT_COMMAND, this, response.substr(1).c_str(), 0); + } +#ifdef BOTS + else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + parse->EventPlayer(EVENT_BOT_COMMAND, this, response.substr(1).c_str(), 0); + } +#endif parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); } else { diff --git a/zone/embparser.cpp b/zone/embparser.cpp index ef1b404a2..f89c09ad4 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -1546,6 +1546,9 @@ void PerlembParser::ExportEventVariables( Seperator sep(data); ExportVar(package_name.c_str(), "command", (sep.arg[0] + 1)); ExportVar(package_name.c_str(), "args", (sep.argnum >= 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); + ExportVar(package_name.c_str(), "data", objid); + ExportVar(package_name.c_str(), "text", data); + ExportVar(package_name.c_str(), "langid", extradata); break; } @@ -1615,6 +1618,9 @@ void PerlembParser::ExportEventVariables( Seperator sep(data); ExportVar(package_name.c_str(), "bot_command", (sep.arg[0] + 1)); ExportVar(package_name.c_str(), "args", (sep.argnum >= 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); + ExportVar(package_name.c_str(), "data", objid); + ExportVar(package_name.c_str(), "text", data); + ExportVar(package_name.c_str(), "langid", extradata); break; } From c438819ed666edca68936754e749e0392025cd7f Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 12 Jan 2020 22:40:05 -0500 Subject: [PATCH 095/157] Fix for command redirect of '#bot' using EVENT_COMMAND invocation --- zone/command.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 1f5da287c..e3f884481 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -13270,8 +13270,8 @@ void command_bot(Client *c, const Seperator *sep) } if (bot_command_dispatch(c, bot_message.c_str()) == -2) { - if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { - int i = parse->EventPlayer(EVENT_COMMAND, c, bot_message, 0); + if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, bot_message, 0); if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { c->Message(Chat::Red, "Bot command '%s' not recognized.", bot_message.c_str()); } From 9bdb70b2f0a5358a4cd2e8865b171758135d56e8 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 13 Jan 2020 01:47:39 -0500 Subject: [PATCH 096/157] Fatality! (Fix for event_say parse events not observing the correct parser for their situation) --- zone/client.cpp | 27 +++++++++++++++++++++------ zone/client_packet.cpp | 8 ++++++-- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index d258ae7bf..9ea27869a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1106,14 +1106,22 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s case ChatChannel_Say: { /* Say */ if(message[0] == COMMAND_CHAR) { if(command_dispatch(this, message) == -2) { - if(parse->PlayerHasQuestSub(EVENT_COMMAND)) { + if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0); - if(i == 0 && !RuleB(Chat, SuppressCommandErrors)) { + if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { Message(Chat::Red, "Command '%s' not recognized.", message); } - } else { - if(!RuleB(Chat, SuppressCommandErrors)) + } + else if (parse->PlayerHasQuestSub(EVENT_SAY)) { + int i = parse->EventPlayer(EVENT_SAY, this, message, 0); + if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { Message(Chat::Red, "Command '%s' not recognized.", message); + } + } + else { + if (!RuleB(Chat, SuppressCommandErrors)) { + Message(Chat::Red, "Command '%s' not recognized.", message); + } } } break; @@ -1128,9 +1136,16 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s Message(Chat::Red, "Bot command '%s' not recognized.", message); } } - else { - if (!RuleB(Chat, SuppressCommandErrors)) + else if (parse->PlayerHasQuestSub(EVENT_SAY)) { + int i = parse->EventPlayer(EVENT_SAY, this, message, 0); + if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { Message(Chat::Red, "Bot command '%s' not recognized.", message); + } + } + else { + if (!RuleB(Chat, SuppressCommandErrors)) { + Message(Chat::Red, "Bot command '%s' not recognized.", message); + } } } break; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 8d583985d..6a8ef9dd3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8297,7 +8297,9 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) parse->EventPlayer(EVENT_BOT_COMMAND, this, response.substr(1).c_str(), 0); } #endif - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + else { + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } } else { Message(Chat::LightGray, "You say, '%s'", response.c_str()); @@ -8315,7 +8317,9 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) parse->EventPlayer(EVENT_BOT_COMMAND, this, response.substr(1).c_str(), 0); } #endif - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + else { + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } } else { Message(Chat::LightGray, "You say, '%s'", response.c_str()); From 61790ef195c29d0be7419123e9796fff631d3b60 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 13 Jan 2020 19:01:42 -0500 Subject: [PATCH 097/157] Wasn't quite dead... (Removed substring call from silent saylink parsing) --- zone/client_packet.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6a8ef9dd3..ebc6bd38a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8290,11 +8290,11 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) { - parse->EventPlayer(EVENT_COMMAND, this, response.substr(1).c_str(), 0); + parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0); } #ifdef BOTS else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { - parse->EventPlayer(EVENT_BOT_COMMAND, this, response.substr(1).c_str(), 0); + parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0); } #endif else { @@ -8310,11 +8310,11 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) else { if (silentsaylink) { if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) { - parse->EventPlayer(EVENT_COMMAND, this, response.substr(1).c_str(), 0); + parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0); } #ifdef BOTS else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { - parse->EventPlayer(EVENT_BOT_COMMAND, this, response.substr(1).c_str(), 0); + parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0); } #endif else { From 331032f4f4342f6dff01f59f23eed2f9e5173b60 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 14 Jan 2020 16:14:19 -0600 Subject: [PATCH 098/157] Push a scan when mobs first construct [skip ci] --- zone/mob.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/mob.cpp b/zone/mob.cpp index 8b8758771..94b53d1f9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -459,6 +459,8 @@ Mob::Mob( #ifdef BOTS m_manual_follow = false; #endif + + mob_scan_close.Trigger(); } Mob::~Mob() From 79db824a3c7c279d09520cd1d6eb5d06f12c4dd5 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 15 Jan 2020 02:56:00 -0500 Subject: [PATCH 099/157] Fix for bots update reporting errors on server start-up after newer update applied [skip ci] --- utils/sql/git/bots/bots_db_update_manifest.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 0a1e4466a..1e18516b7 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -21,9 +21,9 @@ 9020|2018_08_13_bots_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `bot_step` = 0|not_empty| 9021|2018_10_09_bots_owner_options.sql|SHOW TABLES LIKE 'bot_owner_options'|empty| 9022|2019_02_07_bots_stance_type_update.sql|SELECT * FROM `bot_spell_casting_chances` WHERE `spell_type_index` = '255' AND `class_id` = '255' AND `stance_index` = '0'|not_empty| -9023|2019_06_22_bots_owner_option_stats_update.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'stats_update'|empty| +9023|2019_06_22_bots_owner_option_stats_update.sql|SELECT * FROM db_version WHERE bots_version >= 9023|empty| 9024|2019_06_27_bots_pet_get_lost.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'petgetlost'|empty| -9025|2019_08_26_bots_owner_option_spawn_message.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'spawn_message_enabled'|empty| +9025|2019_08_26_bots_owner_option_spawn_message.sql|SELECT * FROM db_version WHERE bots_version >= 9025|empty| 9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty| # Upgrade conditions: From d5067c4c3a87790c81a1c69fa4c47e91cdf5fc02 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 15 Jan 2020 19:30:47 -0500 Subject: [PATCH 100/157] Treat bots like PCs for pcnpc_only_flag --- zone/effects.cpp | 6 ++++-- zone/entity.cpp | 4 ++-- zone/spells.cpp | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 5528f784a..7e2ecdbc7 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -809,11 +809,13 @@ void EntityList::AESpell( * 1 = PC * 2 = NPC */ - if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc()) { + if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc() && + !current_mob->IsBot()) { continue; } - if (spells[spell_id].pcnpc_only_flag == 2 && (current_mob->IsClient() || current_mob->IsMerc())) { + if (spells[spell_id].pcnpc_only_flag == 2 && + (current_mob->IsClient() || current_mob->IsMerc() || current_mob->IsBot())) { continue; } diff --git a/zone/entity.cpp b/zone/entity.cpp index dc90a41b1..3d926e651 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4960,10 +4960,10 @@ void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radiu continue; } // check PC/NPC only flag 1 = PCs, 2 = NPCs - if (pcnpc == 1 && !ptr->IsClient() && !ptr->IsMerc()) { + if (pcnpc == 1 && !ptr->IsClient() && !ptr->IsMerc() && !ptr->IsBot()) { ++it; continue; - } else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc())) { + } else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc() || ptr->IsBot())) { ++it; continue; } diff --git a/zone/spells.cpp b/zone/spells.cpp index afeac90e9..a1e926a1c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3455,9 +3455,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // 1 = PCs, 2 = NPCs if (spells[spell_id].pcnpc_only_flag && spells[spell_id].targettype != ST_AETargetHateList && spells[spell_id].targettype != ST_HateList) { - if (spells[spell_id].pcnpc_only_flag == 1 && !spelltar->IsClient() && !spelltar->IsMerc()) + if (spells[spell_id].pcnpc_only_flag == 1 && !spelltar->IsClient() && !spelltar->IsMerc() && !spelltar->IsBot()) return false; - else if (spells[spell_id].pcnpc_only_flag == 2 && (spelltar->IsClient() || spelltar->IsMerc())) + else if (spells[spell_id].pcnpc_only_flag == 2 && (spelltar->IsClient() || spelltar->IsMerc() || spelltar->IsBot())) return false; } From 28b0526857e6d9c082fd792410a44d328883498f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 15:16:21 -0500 Subject: [PATCH 101/157] Change NPCType::deity to uint32 to match client data type Fixes overflow warning in bot.cpp Shouldn't need DeityAgnostic_LB anymore either --- zone/zonedump.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/zonedump.h b/zone/zonedump.h index 302d4ee25..6010c9480 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -44,7 +44,7 @@ struct NPCType uint16 race; uint8 class_; uint8 bodytype; // added for targettype support - uint8 deity; //not loaded from DB + uint32 deity; //not loaded from DB uint8 level; uint32 npc_id; uint8 texture; From 7ce88b30ad9b5a1e595c73aa7aaab7da413d1c29 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 15:47:25 -0500 Subject: [PATCH 102/157] Remove Bot::BotfocusType This was just a maintenance burden keeping it in sync with focusType (and it wasn't!) --- zone/bot.cpp | 110 +++++++++++++++++++++++++------------------------- zone/bot.h | 40 ++---------------- zone/pets.cpp | 2 +- 3 files changed, 59 insertions(+), 93 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b5628ecb4..ccb3e99df 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5234,7 +5234,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b return false; } -int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 spell_id) +int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 spell_id) { const SPDat_Spell_Struct &spell = spells[spell_id]; int32 value = 0; @@ -5557,8 +5557,8 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 return (value * lvlModifier / 100); } -int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { - if (IsBardSong(spell_id) && bottype != BotfocusFcBaseEffects) +int32 Bot::GetBotFocusEffect(focusType bottype, uint16 spell_id) { + if (IsBardSong(spell_id) && bottype != focusFcBaseEffects) return 0; int32 realTotal = 0; @@ -5567,7 +5567,7 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { bool rand_effectiveness = false; //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance - if((bottype == BotfocusManaCost || bottype == BotfocusImprovedHeal || bottype == BotfocusImprovedDamage) && RuleB(Spells, LiveLikeFocusEffects)) + if((bottype == focusManaCost || bottype == focusImprovedHeal || bottype == focusImprovedDamage) && RuleB(Spells, LiveLikeFocusEffects)) rand_effectiveness = true; //Check if item focus effect exists for the client. @@ -5708,16 +5708,16 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { } } - if(bottype == BotfocusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) + if(bottype == focusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) return 100; - if(bottype == BotfocusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id))) + if(bottype == focusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id))) return 0; return (realTotal + realTotal2); } -int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus) { +int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus) { if(!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) return 0; @@ -5847,7 +5847,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel return 0; break; case SE_ImprovedDamage: - if (bottype == BotfocusImprovedDamage) { + if (bottype == focusImprovedDamage) { if(best_focus) { if (focus_spell.base2[i] != 0) value = focus_spell.base2[i]; @@ -5861,7 +5861,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } break; case SE_ImprovedHeal: - if (bottype == BotfocusImprovedHeal) { + if (bottype == focusImprovedHeal) { if(best_focus) { if (focus_spell.base2[i] != 0) value = focus_spell.base2[i]; @@ -5875,7 +5875,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } break; case SE_ReduceManaCost: - if (bottype == BotfocusManaCost) { + if (bottype == focusManaCost) { if(best_focus) { if (focus_spell.base2[i] != 0) value = focus_spell.base2[i]; @@ -5889,39 +5889,39 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } break; case SE_IncreaseSpellHaste: - if (bottype == BotfocusSpellHaste && focus_spell.base[i] > value) + if (bottype == focusSpellHaste && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_IncreaseSpellDuration: - if (bottype == BotfocusSpellDuration && focus_spell.base[i] > value) + if (bottype == focusSpellDuration && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_SpellDurationIncByTic: - if (bottype == BotfocusSpellDurByTic && focus_spell.base[i] > value) + if (bottype == focusSpellDurByTic && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_SwarmPetDuration: - if (bottype == BotfocusSwarmPetDuration && focus_spell.base[i] > value) + if (bottype == focusSwarmPetDuration && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_IncreaseRange: - if (bottype == BotfocusRange && focus_spell.base[i] > value) + if (bottype == focusRange && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_ReduceReagentCost: - if (bottype == BotfocusReagentCost && focus_spell.base[i] > value) + if (bottype == focusReagentCost && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_PetPowerIncrease: - if (bottype == BotfocusPetPower && focus_spell.base[i] > value) + if (bottype == focusPetPower && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_SpellResistReduction: - if (bottype == BotfocusResistRate && focus_spell.base[i] > value) + if (bottype == focusResistRate && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_SpellHateMod: - if (bottype == BotfocusSpellHateMod) { + if (bottype == focusSpellHateMod) { if(value != 0) { if(value > 0) { if(focus_spell.base[i] > value) @@ -5936,12 +5936,12 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } break; case SE_ReduceReuseTimer: { - if(bottype == BotfocusReduceRecastTime) + if(bottype == focusReduceRecastTime) value = (focus_spell.base[i] / 1000); break; } case SE_TriggerOnCast: { - if(bottype == BotfocusTriggerOnCast) { + if(bottype == focusTriggerOnCast) { if(zone->random.Int(0, 100) <= focus_spell.base[i]) value = focus_spell.base2[i]; else @@ -5950,24 +5950,24 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel break; } case SE_FcSpellVulnerability: { - if(bottype == BotfocusSpellVulnerability) + if(bottype == focusSpellVulnerability) value = focus_spell.base[i]; break; } case SE_BlockNextSpellFocus: { - if(bottype == BotfocusBlockNextSpell) { + if(bottype == focusBlockNextSpell) { if(zone->random.Int(1, 100) <= focus_spell.base[i]) value = 1; } break; } case SE_FcTwincast: { - if(bottype == BotfocusTwincast) + if(bottype == focusTwincast) value = focus_spell.base[i]; break; } case SE_SympatheticProc: { - if(bottype == BotfocusSympatheticProc) { + if(bottype == focusSympatheticProc) { float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]); if(zone->random.Real(0, 1) <= ProcChance) value = focus_id; @@ -5977,49 +5977,49 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel break; } case SE_FcDamageAmt: { - if(bottype == BotfocusFcDamageAmt) + if(bottype == focusFcDamageAmt) value = focus_spell.base[i]; break; } case SE_FcDamageAmtCrit: { - if(bottype == BotfocusFcDamageAmtCrit) + if(bottype == focusFcDamageAmtCrit) value = focus_spell.base[i]; break; } case SE_FcHealAmtIncoming: - if(bottype == BotfocusFcHealAmtIncoming) + if(bottype == focusFcHealAmtIncoming) value = focus_spell.base[i]; break; case SE_FcHealPctCritIncoming: - if (bottype == BotfocusFcHealPctCritIncoming) + if (bottype == focusFcHealPctCritIncoming) value = focus_spell.base[i]; break; case SE_FcHealAmtCrit: - if(bottype == BotfocusFcHealAmtCrit) + if(bottype == focusFcHealAmtCrit) value = focus_spell.base[i]; break; case SE_FcHealAmt: - if(bottype == BotfocusFcHealAmt) + if(bottype == focusFcHealAmt) value = focus_spell.base[i]; break; case SE_FcHealPctIncoming: - if(bottype == BotfocusFcHealPctIncoming) + if(bottype == focusFcHealPctIncoming) value = focus_spell.base[i]; break; case SE_FcBaseEffects: { - if (bottype == BotfocusFcBaseEffects) + if (bottype == focusFcBaseEffects) value = focus_spell.base[i]; break; } case SE_FcDamagePctCrit: { - if(bottype == BotfocusFcDamagePctCrit) + if(bottype == focusFcDamagePctCrit) value = focus_spell.base[i]; break; } case SE_FcIncreaseNumHits: { - if(bottype == BotfocusIncreaseNumHits) + if(bottype == focusIncreaseNumHits) value = focus_spell.base[i]; break; @@ -6559,14 +6559,14 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { int32 Bot::CheckAggroAmount(uint16 spellid) { int32 AggroAmount = Mob::CheckAggroAmount(spellid, nullptr); - int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); + int32 focusAggro = GetBotFocusEffect(focusSpellHateMod, spellid); AggroAmount = (AggroAmount * (100 + focusAggro) / 100); return AggroAmount; } int32 Bot::CheckHealAggroAmount(uint16 spellid, Mob *target, uint32 heal_possible) { int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, target, heal_possible); - int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); + int32 focusAggro = GetBotFocusEffect(focusSpellHateMod, spellid); AggroAmount = (AggroAmount * (100 + focusAggro) / 100); return AggroAmount; } @@ -6840,7 +6840,7 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { bool Critical = false; int32 value_BaseEffect = 0; - value_BaseEffect = (value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id) / 100)); + value_BaseEffect = (value + (value*GetBotFocusEffect(focusFcBaseEffects, spell_id) / 100)); // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) value -= ((GetLevel() - 40) * 20); @@ -6868,16 +6868,16 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { ratio += RuleI(Spells, WizCritRatio); if (Critical) { value = (value_BaseEffect * ratio / 100); - value += (value_BaseEffect * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100); - value += (int(value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100) * ratio / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100); + value += (int(value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100) * ratio / 100); if (target) { value += (int(value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100) * ratio / 100); value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= (GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id) * ratio / 100); + value -= (GetBotFocusEffect(focusFcDamageAmtCrit, spell_id) * ratio / 100); - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) value += (GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value) * ratio / 100); @@ -6889,15 +6889,15 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } value = value_BaseEffect; - value += (value_BaseEffect * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100); - value += (value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100); if (target) { value += (value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100); value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmtCrit, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); @@ -6912,9 +6912,9 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int32 chance = 0; int8 modifier = 1; bool Critical = false; - value_BaseEffect = (value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id) / 100)); + value_BaseEffect = (value + (value*GetBotFocusEffect(focusFcBaseEffects, spell_id) / 100)); value = value_BaseEffect; - value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id) / 100); + value += int(value_BaseEffect*GetBotFocusEffect(focusImprovedHeal, spell_id) / 100); if(spells[spell_id].buffduration < 1) { chance += (itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance); chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); @@ -6927,8 +6927,8 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { } value *= modifier; - value += (GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier); - value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); + value += (GetBotFocusEffect(focusFcHealAmtCrit, spell_id) * modifier); + value += GetBotFocusEffect(focusFcHealAmt, spell_id); value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) @@ -6953,7 +6953,7 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { int32 cast_reducer = 0; - cast_reducer += GetBotFocusEffect(BotfocusSpellHaste, spell_id); + cast_reducer += GetBotFocusEffect(focusSpellHaste, spell_id); uint8 botlevel = GetLevel(); uint8 botclass = GetClass(); if (botlevel >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) && (botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD )) @@ -7088,7 +7088,7 @@ int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { } } - int32 focus_redux = GetBotFocusEffect(BotfocusManaCost, spell_id); + int32 focus_redux = GetBotFocusEffect(focusManaCost, spell_id); if(focus_redux > 0) PercentManaReduction += zone->random.Real(1, (double)focus_redux); @@ -7115,14 +7115,14 @@ int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { float Bot::GetActSpellRange(uint16 spell_id, float range) { float extrange = 100; - extrange += GetBotFocusEffect(BotfocusRange, spell_id); + extrange += GetBotFocusEffect(focusRange, spell_id); return ((range * extrange) / 100); } int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) { int increase = 100; - increase += GetBotFocusEffect(BotfocusSpellDuration, spell_id); - int tic_inc = 0; tic_inc = GetBotFocusEffect(BotfocusSpellDurByTic, spell_id); + increase += GetBotFocusEffect(focusSpellDuration, spell_id); + int tic_inc = 0; tic_inc = GetBotFocusEffect(focusSpellDurByTic, spell_id); if(IsBeneficialSpell(spell_id)) { switch (GetAA(aaSpellCastingReinforcement)) { diff --git a/zone/bot.h b/zone/bot.h index 97bb6892c..b456fd77f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -99,40 +99,6 @@ class Bot : public NPC { friend class Mob; public: // Class enums - enum BotfocusType { //focus types - BotfocusSpellHaste = 1, - BotfocusSpellDuration, - BotfocusRange, - BotfocusReagentCost, - BotfocusManaCost, - BotfocusImprovedHeal, - BotfocusImprovedDamage, - BotfocusImprovedDOT, //i dont know about this... - BotfocusFcDamagePctCrit, - BotfocusImprovedUndeadDamage, - BotfocusPetPower, - BotfocusResistRate, - BotfocusSpellHateMod, - BotfocusTriggerOnCast, - BotfocusSpellVulnerability, - BotfocusTwincast, - BotfocusSympatheticProc, - BotfocusFcDamageAmt, - BotfocusFcDamageAmtCrit, - BotfocusSpellDurByTic, - BotfocusSwarmPetDuration, - BotfocusReduceRecastTime, - BotfocusBlockNextSpell, - BotfocusFcHealPctIncoming, - BotfocusFcDamageAmtIncoming, - BotfocusFcHealAmtIncoming, - BotfocusFcBaseEffects, - BotfocusIncreaseNumHits, - BotfocusFcHealPctCritIncoming, - BotfocusFcHealAmt, - BotfocusFcHealAmtCrit, - }; - enum BotTradeType { // types of trades a bot can do BotTradeClientNormal, BotTradeClientNoDropNoTrade @@ -636,9 +602,9 @@ protected: virtual void PetAIProcess(); virtual void BotMeditate(bool isSitting); virtual bool CheckBotDoubleAttack(bool Triple = false); - virtual int32 GetBotFocusEffect(BotfocusType bottype, uint16 spell_id); - virtual int32 CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false); - virtual int32 CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 spell_id); + virtual int32 GetBotFocusEffect(focusType bottype, uint16 spell_id); + virtual int32 CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false); + virtual int32 CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 spell_id); virtual void PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client); virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); diff --git a/zone/pets.cpp b/zone/pets.cpp index 438509b77..b4897940a 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -199,7 +199,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, } #ifdef BOTS else if (this->IsBot()) - act_power = CastToBot()->GetBotFocusEffect(Bot::BotfocusPetPower, spell_id); + act_power = CastToBot()->GetBotFocusEffect(focusPetPower, spell_id); #endif } else if (petpower > 0) From 607379110bdae825821a7b5acee8d2cebfc87c5f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 16:00:38 -0500 Subject: [PATCH 103/157] Add some focus effects bots were missing --- zone/bot.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ccb3e99df..5579b4bc5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5388,6 +5388,10 @@ int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 sp if (type == focusImprovedDamage && base1 > value) value = base1; break; + case SE_ImprovedDamage2: + if (type == focusImprovedDamage2 && base1 > value) + value = base1; + break; case SE_ImprovedHeal: if (type == focusImprovedHeal && base1 > value) value = base1; @@ -5499,6 +5503,11 @@ int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 sp value = base1; break; } + case SE_FcDamageAmt2: { + if(type == focusFcDamageAmt2) + value = base1; + break; + } case SE_FcDamageAmtCrit: { if(type == focusFcDamageAmtCrit) value = base1; @@ -5567,7 +5576,7 @@ int32 Bot::GetBotFocusEffect(focusType bottype, uint16 spell_id) { bool rand_effectiveness = false; //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance - if((bottype == focusManaCost || bottype == focusImprovedHeal || bottype == focusImprovedDamage) && RuleB(Spells, LiveLikeFocusEffects)) + if(RuleB(Spells, LiveLikeFocusEffects) && (bottype == focusManaCost || bottype == focusImprovedHeal || bottype == focusImprovedDamage || bottype == focusImprovedDamage2 || bottype == focusResistRate)) rand_effectiveness = true; //Check if item focus effect exists for the client. @@ -5860,6 +5869,20 @@ int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_i value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); } break; + case SE_ImprovedDamage2: + if (bottype == focusImprovedDamage2) { + if(best_focus) { + if (focus_spell.base2[i] != 0) + value = focus_spell.base2[i]; + else + value = focus_spell.base[i]; + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) + value = focus_spell.base[i]; + else + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + break; case SE_ImprovedHeal: if (bottype == focusImprovedHeal) { if(best_focus) { @@ -5981,6 +6004,11 @@ int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_i value = focus_spell.base[i]; break; } + case SE_FcDamageAmt2: { + if(bottype == focusFcDamageAmt2) + value = focus_spell.base[i]; + break; + } case SE_FcDamageAmtCrit: { if(bottype == focusFcDamageAmtCrit) value = focus_spell.base[i]; @@ -6869,6 +6897,7 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { if (Critical) { value = (value_BaseEffect * ratio / 100); value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage2, spell_id) / 100); value += (int(value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100) * ratio / 100); if (target) { value += (int(value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100) * ratio / 100); @@ -6878,6 +6907,7 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= (GetBotFocusEffect(focusFcDamageAmtCrit, spell_id) * ratio / 100); value -= GetBotFocusEffect(focusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmt2, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) value += (GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value) * ratio / 100); @@ -6890,6 +6920,7 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value = value_BaseEffect; value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage2, spell_id) / 100); value += (value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100); if (target) { value += (value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100); @@ -6898,6 +6929,7 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetBotFocusEffect(focusFcDamageAmtCrit, spell_id); value -= GetBotFocusEffect(focusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmt2, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); From 099da513ff2b41ea97cb1563a5c8fb56db9d7d7e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 16:18:08 -0500 Subject: [PATCH 104/157] Remove some unused misc.cpp functions (encode/decode) These were unused and throwing warnings, so throw them away! --- common/misc.cpp | 61 ------------------------------------------------- common/misc.h | 7 ------ 2 files changed, 68 deletions(-) diff --git a/common/misc.cpp b/common/misc.cpp index 792dfcbab..b9dc31d28 100644 --- a/common/misc.cpp +++ b/common/misc.cpp @@ -19,9 +19,6 @@ #include "types.h" #include -#define ENC(c) (((c) & 0x3f) + ' ') -#define DEC(c) (((c) - ' ') & 0x3f) - std::map DBFieldNames; #ifndef WIN32 @@ -333,64 +330,6 @@ void LoadItemDBFieldNames() { DBFieldNames[113]="unknown115"; // ? (end quote) } -void encode_length(unsigned long length, char *out) -{ -char buf[4]; - memcpy(buf,&length,sizeof(unsigned long)); - encode_chunk(buf,3,out); -} - -unsigned long encode(char *in, unsigned long length, char *out) -{ -unsigned long used=0,len=0; - while(used> 2); - *(out+1)=ENC((in[0] << 4)|(((len<2 ? 0 : in[1]) >> 4) & 0xF)); - *(out+2)=ENC(((len<2 ? 0 : in[1]) << 2)|(((len<3 ? 0 : in[2]) >> 6) & 0x3)); - *(out+3)=ENC((len<3 ? 0 : in[2])); -} - -void decode_chunk(char *in, char *out) -{ - *out = DEC(*in) << 2 | DEC(in[1]) >> 4; - *(out+1) = DEC(in[1]) << 4 | DEC(in[2]) >> 2; - *(out+2) = DEC(in[2]) << 6 | DEC(in[3]); -} - void dump_message_column(unsigned char *buffer, unsigned long length, std::string leader, FILE *to) { unsigned long i,j; diff --git a/common/misc.h b/common/misc.h index b33f2f32d..a099ab823 100644 --- a/common/misc.h +++ b/common/misc.h @@ -17,13 +17,6 @@ int Tokenize(std::string s, std::map & tokens, char delim='|'); void LoadItemDBFieldNames(); -void encode_length(unsigned long length, char *out); -unsigned long decode_length(char *in); -unsigned long encode(char *in, unsigned long length, char *out); -void decode(char *in, char *out); -void encode_chunk(char *in, int len, char *out); -void decode_chunk(char *in, char *out); - #ifndef WIN32 int print_stacktrace(); #endif From a307747c2954dc2e0509e40664c80206e24781c4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 16:51:24 -0500 Subject: [PATCH 105/157] Nuke unused command_itemtest --- zone/command.cpp | 19 ------------------- zone/command.h | 1 - 2 files changed, 20 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index e3f884481..877529c2b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2173,25 +2173,6 @@ void command_spoff(Client *c, const Seperator *sep) safe_delete(outapp); } -void command_itemtest(Client *c, const Seperator *sep) -{ - char chBuffer[8192] = {0}; - //Using this to determine new item layout - FILE* f = nullptr; - if (!(f = fopen("c:\\EQEMUcvs\\ItemDump.txt", "rb"))) { - c->Message(Chat::Red, "Error: Could not open c:\\EQEMUcvs\\ItemDump.txt"); - return; - } - - fread(chBuffer, sizeof(chBuffer), sizeof(char), f); - fclose(f); - - auto outapp = new EQApplicationPacket(OP_ItemLinkResponse, strlen(chBuffer) + 5); - memcpy(&outapp->pBuffer[4], chBuffer, strlen(chBuffer)); - c->QueuePacket(outapp); - safe_delete(outapp); -} - void command_gassign(Client *c, const Seperator *sep) { if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC() && c->GetTarget()->CastToNPC()->GetSpawnPointID() > 0) { diff --git a/zone/command.h b/zone/command.h index 1c9d8fe7c..d64950f9c 100644 --- a/zone/command.h +++ b/zone/command.h @@ -150,7 +150,6 @@ void command_ipc(Client *c, const Seperator *sep); void command_iplookup(Client *c, const Seperator *sep); void command_iteminfo(Client *c, const Seperator *sep); void command_itemsearch(Client *c, const Seperator *sep); -void command_itemtest(Client *c, const Seperator *sep); void command_kick(Client *c, const Seperator *sep); void command_killallnpcs(Client *c, const Seperator *sep); void command_kill(Client *c, const Seperator *sep); From 945ca1278c202172b930f35e7c15a8ee4093d414 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 17:02:35 -0500 Subject: [PATCH 106/157] Remove some unnecessary this == null checks --- common/servertalk.h | 3 --- zone/mob.h | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index 3aef636ca..b9cb6202e 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -262,9 +262,6 @@ public: } ServerPacket* Copy() { - if (this == 0) { - return 0; - } ServerPacket* ret = new ServerPacket(this->opcode, this->size); if (this->size) memcpy(ret->pBuffer, this->pBuffer, this->size); diff --git a/zone/mob.h b/zone/mob.h index f0fc79548..d48f47e6d 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -624,8 +624,7 @@ public: //AI static uint32 GetLevelCon(uint8 mylevel, uint8 iOtherLevel); - inline uint32 GetLevelCon(uint8 iOtherLevel) const { - return this ? GetLevelCon(GetLevel(), iOtherLevel) : CON_GRAY; } + inline uint32 GetLevelCon(uint8 iOtherLevel) const { return GetLevelCon(GetLevel(), iOtherLevel); } virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false, uint16 spell_id = SPELL_UNKNOWN, bool pet_comand = false); bool RemoveFromHateList(Mob* mob); From e6a14beb2e9bf33aa93c0cfa01cef29e8868e295 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 17:06:11 -0500 Subject: [PATCH 107/157] Remove another unnecessary this == null check --- common/ptimer.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/common/ptimer.cpp b/common/ptimer.cpp index 032ac5945..e6338bdf9 100644 --- a/common/ptimer.cpp +++ b/common/ptimer.cpp @@ -190,10 +190,6 @@ bool PersistentTimer::Clear(Database *db) { /* This function checks if the timer triggered */ bool PersistentTimer::Expired(Database *db, bool iReset) { - if (this == nullptr) { - LogError("Null timer during ->Check()!?\n"); - return(true); - } uint32 current_time = get_current_time(); if (current_time-start_time >= timer_time) { if (enabled && iReset) { From e4c4e5edb2238d3a74e09364f422bae9faf77903 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 17:17:31 -0500 Subject: [PATCH 108/157] References shouldn't be null --- common/linked_list.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/common/linked_list.h b/common/linked_list.h index 246677db4..8ac4b51d8 100644 --- a/common/linked_list.h +++ b/common/linked_list.h @@ -278,12 +278,6 @@ void LinkedListIterator::Replace(const TYPE& new_data) template void LinkedListIterator::Reset() { - if (!(&list)) - { - current_element=0; - return; - } - if (dir == FORWARD) { current_element = list.first; From 14402c9c4103d3a9308ef72405015cb4bbd89b75 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 17:44:46 -0500 Subject: [PATCH 109/157] Fix unreachable switch statements in console_server_connection.cpp --- common/net/console_server_connection.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/common/net/console_server_connection.cpp b/common/net/console_server_connection.cpp index 795c73140..aab26d188 100644 --- a/common/net/console_server_connection.cpp +++ b/common/net/console_server_connection.cpp @@ -116,17 +116,21 @@ bool EQ::Net::ConsoleServerConnection::SendChannelMessage(const ServerChannelMes } switch (scm->chan_num) { - if (RuleB(Chat, ServerWideAuction)) { - case 4: { + case 4: { + if (RuleB(Chat, ServerWideAuction)) { QueueMessage(fmt::format("{0} auctions, '{1}'", scm->from, scm->message)); break; + } else { // I think we want default action in this case? + return false; } } - if (RuleB(Chat, ServerWideOOC)) { - case 5: { + case 5: { + if (RuleB(Chat, ServerWideOOC)) { QueueMessage(fmt::format("{0} says ooc, '{1}'", scm->from, scm->message)); break; + } else { // I think we want default action in this case? + return false; } } From baf4cc62eb2e8f081e2a4b7cba479dcd623ed580 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 18:04:20 -0500 Subject: [PATCH 110/157] Fix format truncation in RoF2 OP_Trader --- common/patches/rof2.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 11bb99af7..36257771b 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -37,6 +37,7 @@ #include #include #include +#include namespace RoF2 @@ -3548,7 +3549,7 @@ namespace RoF2 { eq->items[i].Unknown18 = 0; if (i < 80) { - snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", emu->SerialNumber[i]); + snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016" PRId64, emu->SerialNumber[i]); eq->ItemCost[i] = emu->ItemCost[i]; } else { From 139b6c34e512904b082f45cc52de3518750a2949 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 19:03:36 -0500 Subject: [PATCH 111/157] Change things -Wcatch-value complains about to references --- common/eqemu_config.h | 2 +- common/json_config.cpp | 10 +++--- common/net/websocket_server.cpp | 10 +++--- world/web_interface.cpp | 12 +++---- zone/lua_client.cpp | 14 ++++----- zone/lua_general.cpp | 38 +++++++++++----------- zone/lua_mob.cpp | 56 ++++++++++++++++----------------- 7 files changed, 71 insertions(+), 71 deletions(-) diff --git a/common/eqemu_config.h b/common/eqemu_config.h index e75737d76..a4fb7797f 100644 --- a/common/eqemu_config.h +++ b/common/eqemu_config.h @@ -165,7 +165,7 @@ class EQEmuConfig fconfig >> _config->_root; _config->parse_config(); } - catch (std::exception) { + catch (std::exception &) { return false; } return true; diff --git a/common/json_config.cpp b/common/json_config.cpp index d262038f6..5f4c47b71 100644 --- a/common/json_config.cpp +++ b/common/json_config.cpp @@ -32,7 +32,7 @@ EQ::JsonConfigFile EQ::JsonConfigFile::Load( try { ifs >> ret.m_root; } - catch (std::exception) { + catch (std::exception &) { return ret; } @@ -81,7 +81,7 @@ std::string EQ::JsonConfigFile::GetVariableString( return m_root[title][parameter].asString(); } } - catch (std::exception) { + catch (std::exception &) { return default_value; } @@ -105,7 +105,7 @@ int EQ::JsonConfigFile::GetVariableInt( return m_root[title][parameter].asInt(); } } - catch (std::exception) { + catch (std::exception &) { return default_value; } @@ -129,7 +129,7 @@ bool EQ::JsonConfigFile::GetVariableBool( return m_root[title][parameter].asBool(); } } - catch (std::exception) { + catch (std::exception &) { return default_value; } @@ -153,7 +153,7 @@ double EQ::JsonConfigFile::GetVariableDouble( return m_root[title][parameter].asDouble(); } } - catch (std::exception) { + catch (std::exception &) { return default_value; } diff --git a/common/net/websocket_server.cpp b/common/net/websocket_server.cpp index de1a0cf99..b46fc7953 100644 --- a/common/net/websocket_server.cpp +++ b/common/net/websocket_server.cpp @@ -61,7 +61,7 @@ EQ::Net::WebsocketServer::WebsocketServer(const std::string &addr, int port) auto &connection = iter->second; connection->GetWebsocketConnection()->ping("keepalive"); } - catch (std::exception) { + catch (std::exception &) { iter->second->GetTCPConnection()->Disconnect(); } @@ -157,7 +157,7 @@ void EQ::Net::WebsocketServer::DispatchEvent(WebsocketSubscriptionEvent evt, Jso } } } - catch (std::exception) { + catch (std::exception &) { } } @@ -190,7 +190,7 @@ Json::Value EQ::Net::WebsocketServer::Login(WebsocketServerConnection *connectio return ret; } - catch (std::exception) { + catch (std::exception &) { throw WebsocketException("Unable to process login request"); } } @@ -212,7 +212,7 @@ Json::Value EQ::Net::WebsocketServer::Subscribe(WebsocketServerConnection *conne catch (WebsocketException &ex) { throw ex; } - catch (std::exception) { + catch (std::exception &) { throw WebsocketException("Unable to process unsubscribe request"); } } @@ -234,7 +234,7 @@ Json::Value EQ::Net::WebsocketServer::Unsubscribe(WebsocketServerConnection *con catch (WebsocketException &ex) { throw ex; } - catch (std::exception) { + catch (std::exception &) { throw WebsocketException("Unable to process unsubscribe request"); } } diff --git a/world/web_interface.cpp b/world/web_interface.cpp index 191d8cca1..773e955b6 100644 --- a/world/web_interface.cpp +++ b/world/web_interface.cpp @@ -24,7 +24,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p) std::stringstream ss(json_str); ss >> root; } - catch (std::exception) { + catch (std::exception &) { SendError("Could not parse request"); return; } @@ -40,7 +40,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p) return; } } - catch (std::exception) { + catch (std::exception &) { SendError("Invalid request: method not supplied"); return; } @@ -49,7 +49,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p) try { params = root["params"]; } - catch (std::exception) { + catch (std::exception &) { params = nullptr; } @@ -57,7 +57,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p) try { id = root["id"].asString(); } - catch (std::exception) { + catch (std::exception &) { id = ""; } @@ -82,7 +82,7 @@ void WebInterface::Send(const Json::Value &value) p.PutString(0, ss.str()); m_connection->Send(ServerOP_WebInterfaceCall, p); } - catch (std::exception) { + catch (std::exception &) { //Log error } } @@ -116,7 +116,7 @@ void WebInterface::SendEvent(const Json::Value &value) p.PutString(0, ss.str()); m_connection->Send(ServerOP_WebInterfaceEvent, p); } - catch (std::exception) { + catch (std::exception &) { //Log error } } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 64ce0cafa..14c009b12 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1397,7 +1397,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { if (luabind::type(cur) != LUA_TNIL) { try { copper = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -1405,7 +1405,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { if (luabind::type(cur) != LUA_TNIL) { try { silver = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -1413,7 +1413,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { if (luabind::type(cur) != LUA_TNIL) { try { gold = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -1421,7 +1421,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { if (luabind::type(cur) != LUA_TNIL) { try { platinum = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -1429,7 +1429,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { if (luabind::type(cur) != LUA_TNIL) { try { itemid = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -1437,7 +1437,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { if (luabind::type(cur) != LUA_TNIL) { try { exp = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -1445,7 +1445,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { if (luabind::type(cur) != LUA_TNIL) { try { faction = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 7f714fd6b..ff0c6b10a 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -571,7 +571,7 @@ void lua_task_selector(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { cur_value = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } else { count = i - 1; @@ -601,7 +601,7 @@ void lua_enable_task(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { cur_value = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } else { count = i - 1; @@ -628,7 +628,7 @@ void lua_disable_task(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { cur_value = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } else { count = i - 1; @@ -1156,7 +1156,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { spawn2_id = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1167,7 +1167,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { spawngroup_id = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1178,7 +1178,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { x = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1189,7 +1189,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { y = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1200,7 +1200,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { z = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1211,7 +1211,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { heading = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1222,7 +1222,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { respawn = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1233,7 +1233,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { variance = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1244,7 +1244,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { timeleft = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1252,7 +1252,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { grid = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1260,7 +1260,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { condition_id = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1268,7 +1268,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { condition_min_value = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1276,7 +1276,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { enabled = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1284,7 +1284,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { animation = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1399,7 +1399,7 @@ void lua_update_zone_header(std::string type, std::string value) { try { \ npc_type->name = luabind::object_cast(cur); \ } \ - catch(luabind::cast_failed) { \ + catch(luabind::cast_failed &) { \ npc_type->size = default_value; \ } \ } \ @@ -1415,7 +1415,7 @@ void lua_update_zone_header(std::string type, std::string value) { std::string tmp = luabind::object_cast(cur); \ strncpy(npc_type->name, tmp.c_str(), str_length); \ } \ - catch(luabind::cast_failed) { \ + catch(luabind::cast_failed &) { \ strncpy(npc_type->name, default_value, str_length); \ } \ } \ diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 7d769b8dd..4860ae1e8 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -113,7 +113,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.armor_pen_flat = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -121,7 +121,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.crit_flat = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -129,7 +129,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.damage_flat = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -137,7 +137,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.hate_flat = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -145,7 +145,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.armor_pen_percent = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -153,7 +153,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.crit_percent = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -161,7 +161,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.damage_percent = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -169,7 +169,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.hate_percent = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } } @@ -785,7 +785,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj if (luabind::type(cur) != LUA_TNIL) { try { journal_opts.speak_mode = static_cast(luabind::object_cast(cur)); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -793,7 +793,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj if (luabind::type(cur) != LUA_TNIL) { try { journal_opts.journal_mode = static_cast(luabind::object_cast(cur)); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -801,7 +801,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj if (luabind::type(cur) != LUA_TNIL) { try { journal_opts.language = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -809,7 +809,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj if (luabind::type(cur) != LUA_TNIL) { try { journal_opts.message_type = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } } @@ -1568,7 +1568,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { race = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1576,7 +1576,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { gender = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1584,7 +1584,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { texture = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1592,7 +1592,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { helmtexture = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1600,7 +1600,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { haircolor = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1608,7 +1608,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { beardcolor = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1616,7 +1616,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { eyecolor1 = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1624,7 +1624,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { eyecolor2 = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1632,7 +1632,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { hairstyle = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1640,7 +1640,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { luclinface = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1648,7 +1648,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { beard = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1656,7 +1656,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { aa_title = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1664,7 +1664,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { drakkin_heritage = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1672,7 +1672,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { drakkin_tattoo = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1680,7 +1680,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { drakkin_details = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1688,7 +1688,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { size = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } From 4241fba7e2217dc802ba72f6991622fdb6e73266 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 19:10:04 -0500 Subject: [PATCH 112/157] Forgot 2 exceptions --- common/net/daybreak_connection.cpp | 2 +- common/string_util.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/net/daybreak_connection.cpp b/common/net/daybreak_connection.cpp index b10203854..8448049f5 100644 --- a/common/net/daybreak_connection.cpp +++ b/common/net/daybreak_connection.cpp @@ -399,7 +399,7 @@ void EQ::Net::DaybreakConnection::Process() ProcessQueue(); } - catch (std::exception ex) { + catch (std::exception &ex) { if (m_owner->m_on_error_message) { m_owner->m_on_error_message(fmt::format("Error processing connection: {0}", ex.what())); } diff --git a/common/string_util.cpp b/common/string_util.cpp index b48bee1b3..df3790def 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -222,7 +222,7 @@ bool StringIsNumber(const std::string &s) { auto r = stod(s); return true; } - catch (std::exception) { + catch (std::exception &) { return false; } } From e1408ede6b871dcdc80461b5760d1467bddcd445 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 16 Jan 2020 19:22:17 -0500 Subject: [PATCH 113/157] Remove AdventureManager::Load since it doesn't do anything --- world/adventure_manager.cpp | 99 ------------------------------------- world/adventure_manager.h | 1 - world/main.cpp | 1 - 3 files changed, 101 deletions(-) diff --git a/world/adventure_manager.cpp b/world/adventure_manager.cpp index 65e264a19..c3e70b7ed 100644 --- a/world/adventure_manager.cpp +++ b/world/adventure_manager.cpp @@ -2119,102 +2119,3 @@ void AdventureManager::Save() } } -void AdventureManager::Load() -{ - //disabled for now - return; - - char *data = nullptr; - FILE *f = fopen("adventure_state.dat", "r"); - if(f) - { - fseek(f, 0, SEEK_END); - long length = ftell(f); - if(length > 0) - { - data = new char[length]; - fseek(f, 0, SEEK_SET); - fread(data, length, 1, f); - } - fclose(f); - } - - if(data) - { - char *ptr = data; - - int number_of_adventures = *((int*)ptr); - ptr += sizeof(int); - - for(int i = 0; i < number_of_adventures; ++i) - { - int count = *((int*)ptr); - ptr += sizeof(int); - - int a_count = *((int*)ptr); - ptr += sizeof(int); - - int template_id = *((int*)ptr); - ptr += sizeof(int); - - int status = *((int*)ptr); - ptr += sizeof(int); - - int instance_id = *((int*)ptr); - ptr += sizeof(int); - - int rem_time = *((int*)ptr); - ptr += sizeof(int); - - int num_players = *((int*)ptr); - ptr += sizeof(int); - - AdventureTemplate *t = GetAdventureTemplate(template_id); - if(t) - { - auto adv = - new Adventure(t, count, a_count, (AdventureStatus)status, instance_id, rem_time); - for(int j = 0; j < num_players; ++j) - { - adv->AddPlayer((const char*)ptr, false); - ptr += strlen((const char*)ptr); - ptr += 1; - } - adventure_list.push_back(adv); - } - else - { - for(int j = 0; j < num_players; ++j) - { - ptr += strlen((const char*)ptr); - ptr += 1; - } - } - } - - int number_of_finished = *((int*)ptr); - ptr += sizeof(int); - - for(int k = 0; k < number_of_finished; ++k) - { - AdventureFinishEvent afe; - afe.win = *((bool*)ptr); - ptr += sizeof(bool); - - afe.points = *((int*)ptr); - ptr += sizeof(int); - - afe.theme = *((int*)ptr); - ptr += sizeof(int); - - afe.name = (const char*)ptr; - ptr += strlen((const char*)ptr); - ptr += 1; - - finished_list.push_back(afe); - } - - safe_delete_array(data); - } -} - diff --git a/world/adventure_manager.h b/world/adventure_manager.h index 5c9a4e560..ae4bf950f 100644 --- a/world/adventure_manager.h +++ b/world/adventure_manager.h @@ -34,7 +34,6 @@ public: void AddFinishedEvent(AdventureFinishEvent fe) { finished_list.push_back(fe); Save(); } bool PopFinishedEvent(const char *name, AdventureFinishEvent &fe); void Save(); - void Load(); Adventure **GetFinishedAdventures(const char *player, int &count); Adventure *GetActiveAdventure(const char *player); diff --git a/world/main.cpp b/world/main.cpp index 0ab5f2089..72cc6d391 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -396,7 +396,6 @@ int main(int argc, char** argv) { LogInfo("Unable to load adventure templates"); } - adventure_manager.Load(); adventure_manager.LoadLeaderboardInfo(); LogInfo("Purging expired instances"); From a9ef2474d478c2c1cb5eee2332f09c4e74e5e5d9 Mon Sep 17 00:00:00 2001 From: Uleat Date: Thu, 16 Jan 2020 20:49:03 -0500 Subject: [PATCH 114/157] Updated eqemu_server.pl to use the appveyor archive when setting up bots [skip ci] --- utils/scripts/eqemu_server.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 658f3709d..76084f23f 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -813,7 +813,7 @@ sub fetch_utility_scripts { sub setup_bots { if ($OS eq "Windows") { - fetch_latest_windows_binaries_bots(); + fetch_latest_windows_appveyor_bots(); } if ($OS eq "Linux") { build_linux_source("bots"); @@ -821,7 +821,7 @@ sub setup_bots { bots_db_management(); run_database_check(); - print "Bots should be setup, run your server and the #bot command should be available in-game\n"; + print "Bots should be setup, run your server and the bot command should be available in-game (type '^help')\n"; } sub show_menu_prompt { From d47bf6a73b266d06326d11952bb732422969f7f1 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 17 Jan 2020 14:35:32 -0500 Subject: [PATCH 115/157] Update waypoints.cpp Fixed log message to be correct. --- zone/waypoints.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 10e4a11f3..b6033744c 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -159,7 +159,7 @@ void NPC::PauseWandering(int pausetime) if (GetGrid() != 0) { moving = false; DistractedFromGrid = true; - LogPathing("Paused Wandering requested. Grid [{}]. Resuming in [{}] ms (0=not until told)", GetGrid(), pausetime); + LogPathing("Paused Wandering requested. Grid [{}]. Resuming in [{}] seconds (0=not until told)", GetGrid(), pausetime); StopNavigation(); if (pausetime < 1) { // negative grid number stops him dead in his tracks until ResumeWandering() SetGrid(0 - GetGrid()); From feefd7a23b98483028183312890247252f3d55d3 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 17 Jan 2020 17:30:38 -0500 Subject: [PATCH 116/157] Update default NPC:NPCGatePercent value to something more live like --- common/ruletypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 3de392d45..dab1d02e1 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -519,7 +519,7 @@ RULE_INT(NPC, NPCToNPCAggroTimerMin, 500, "") RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000, "") RULE_BOOL(NPC, UseClassAsLastName, true, "Uses class archetype as LastName for npcs with none") RULE_BOOL(NPC, NewLevelScaling, true, "Better level scaling, use old if new formulas would break your server") -RULE_INT(NPC, NPCGatePercent, 5, "% at which the NPC Will attempt to gate at") +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") From 43df845233613237a93247c2a6fcd8ffb5d8eecf Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 18 Jan 2020 21:42:51 -0500 Subject: [PATCH 117/157] Fix issue with overflow in Mob::SendHPUpdate --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 94b53d1f9..af8aa25ab 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1369,7 +1369,7 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal } } - int8 current_hp_percent = static_cast(max_hp == 0 ? 0 : static_cast(current_hp * 100 / max_hp)); + auto current_hp_percent = GetIntHPRatio(); Log(Logs::General, Logs::HPUpdate, From 8eb60302a284da606eff3f94c1ce38f47d0c5877 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sun, 19 Jan 2020 16:03:09 -0500 Subject: [PATCH 118/157] Update attack.cpp Fix to Monk Mitigation. Divided weight by 10 to convert to stones. --- zone/attack.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index a48f4008e..911f22dfb 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -724,7 +724,7 @@ int Mob::GetClassRaceACBonus() hardcap = 32; softcap = 15; } - int weight = IsClient() ? CastToClient()->CalcCurrentWeight() : 0; + int weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10 : 0; if (weight < hardcap - 1) { int temp = level + 5; if (weight > softcap) { @@ -5491,4 +5491,4 @@ int32 Mob::GetHPRegen() const int32 Mob::GetManaRegen() const { return mana_regen; -} \ No newline at end of file +} From 6f73278cf858935f58d736600e3c4c50aefe044e Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 19 Jan 2020 21:57:28 -0600 Subject: [PATCH 119/157] Fix annoying aura crash that has been around for a year and a half, add aura logging, utilize close lists --- common/eqemu_logsys.h | 2 + common/eqemu_logsys_log_aliases.h | 16 + zone/aura.cpp | 673 +++++++++++++++++++----------- zone/aura.h | 2 +- zone/entity.cpp | 18 + zone/entity.h | 2 +- zone/mob.cpp | 2 + 7 files changed, 471 insertions(+), 244 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 184ccffe2..9fa164d98 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -113,6 +113,7 @@ namespace Logs { AoeCast, EntityManagement, Flee, + Aura, MaxCategoryID /* Don't Remove this */ }; @@ -185,6 +186,7 @@ namespace Logs { "AOE Cast", "Entity Management", "Flee", + "Aura", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 1c003fff9..ccfc54f3a 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -551,6 +551,16 @@ OutF(LogSys, Logs::Detail, Logs::Flee, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogAura(message, ...) do {\ + if (LogSys.log_settings[Logs::Aura].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Aura, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAuraDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Aura].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Aura, __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__);\ @@ -878,6 +888,12 @@ #define LogFleeDetail(message, ...) do {\ } while (0) +#define LogAura(message, ...) do {\ +} while (0) + +#define LogAuraDetail(message, ...) do {\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ } while (0) diff --git a/zone/aura.cpp b/zone/aura.cpp index 0830816c1..50026f998 100644 --- a/zone/aura.cpp +++ b/zone/aura.cpp @@ -6,8 +6,9 @@ #include "raids.h" Aura::Aura(NPCType *type_data, Mob *owner, AuraRecord &record) - : NPC(type_data, 0, owner->GetPosition(), GravityBehavior::Flying), spell_id(record.spell_id), distance(record.distance), - remove_timer(record.duration), movement_timer(100), process_timer(1000), aura_id(-1) + : NPC(type_data, 0, owner->GetPosition(), GravityBehavior::Flying), spell_id(record.spell_id), + distance(record.distance), + remove_timer(record.duration), movement_timer(100), process_timer(1000), aura_id(-1) { GiveNPCTypeData(type_data); // we will delete this later on m_owner = owner->GetID(); @@ -17,42 +18,48 @@ Aura::Aura(NPCType *type_data, Mob *owner, AuraRecord &record) cast_timer.Disable(); // we don't want to be enabled yet } - if (record.aura_type < static_cast(AuraType::Max)) + if (record.aura_type < static_cast(AuraType::Max)) { type = static_cast(record.aura_type); - else + } + else { type = AuraType::OnAllGroupMembers; + } - if (record.spawn_type < static_cast(AuraSpawns::Max)) + if (record.spawn_type < static_cast(AuraSpawns::Max)) { spawn_type = static_cast(record.spawn_type); - else + } + else { spawn_type = AuraSpawns::GroupMembers; + } - if (record.movement < static_cast(AuraMovement::Max)) + if (record.movement < static_cast(AuraMovement::Max)) { movement_type = static_cast(record.movement); - else + } + else { movement_type = AuraMovement::Follow; + } switch (type) { - case AuraType::OnAllFriendlies: - process_func = &Aura::ProcessOnAllFriendlies; - break; - case AuraType::OnAllGroupMembers: - process_func = &Aura::ProcessOnAllGroupMembers; - break; - case AuraType::OnGroupMembersPets: - process_func = &Aura::ProcessOnGroupMembersPets; - break; - case AuraType::Totem: - process_func = &Aura::ProcessTotem; - break; - case AuraType::EnterTrap: - process_func = &Aura::ProcessEnterTrap; - break; - case AuraType::ExitTrap: - process_func = &Aura::ProcessExitTrap; - break; - default: - process_func = nullptr; + case AuraType::OnAllFriendlies: + process_func = &Aura::ProcessOnAllFriendlies; + break; + case AuraType::OnAllGroupMembers: + process_func = &Aura::ProcessOnAllGroupMembers; + break; + case AuraType::OnGroupMembersPets: + process_func = &Aura::ProcessOnGroupMembersPets; + break; + case AuraType::Totem: + process_func = &Aura::ProcessTotem; + break; + case AuraType::EnterTrap: + process_func = &Aura::ProcessEnterTrap; + break; + case AuraType::ExitTrap: + process_func = &Aura::ProcessExitTrap; + break; + default: + process_func = nullptr; } } @@ -64,9 +71,9 @@ Mob *Aura::GetOwner() // not 100% sure how this one should work and PVP affects ... void Aura::ProcessOnAllFriendlies(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); std::set delayed_remove; - bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter for (auto &e : mob_list) { auto mob = e.second; @@ -74,13 +81,16 @@ void Aura::ProcessOnAllFriendlies(Mob *owner) auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // we are already on the list, let's check for removal - if (DistanceSquared(GetPosition(), mob->GetPosition()) > distance) + if (DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { delayed_remove.insert(mob->GetID()); - } else { // not on list, lets check if we're in range + } + } + else { // not on list, lets check if we're in range if (DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } @@ -88,30 +98,34 @@ void Aura::ProcessOnAllFriendlies(Mob *owner) for (auto &e : delayed_remove) { auto mob = entity_list.GetMob(e); - if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + if (mob != nullptr && is_buff) { // some auras cast instant spells so no need to remove mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } casted_on.erase(e); } // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it - if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) { cast_timer.Start(); + } - if (!cast_timer.Enabled() || !cast_timer.Check()) + if (!cast_timer.Enabled() || !cast_timer.Check()) { return; + } for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { SpellFinished(spell_id, mob); + } } } void Aura::ProcessOnAllGroupMembers(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); std::set delayed_remove; - bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter if (owner->IsRaidGrouped() && owner->IsClient()) { // currently raids are just client, but safety check auto raid = owner->GetRaid(); @@ -126,9 +140,12 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) auto idx = raid->GetPlayerIndex(c); if (c->GetID() == m_owner) { return DistanceSquared(GetPosition(), c->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), c->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), c->GetPosition()) > distance) { return false; } return true; @@ -138,9 +155,12 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient()); if (m->GetOwner()->GetID() == m_owner) { return DistanceSquared(GetPosition(), m->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { return false; } return true; @@ -148,14 +168,18 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) auto verify_raid_client_swarm = [&raid, &group_id, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); - if (owner == nullptr) + if (owner == nullptr) { return false; + } auto idx = raid->GetPlayerIndex(owner->CastToClient()); if (owner->GetID() == m_owner) { return DistanceSquared(GetPosition(), n->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { return false; } return true; @@ -164,40 +188,52 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) for (auto &e : mob_list) { auto mob = e.second; // step 1: check if we're already managing this NPC's buff - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // verify still good! if (mob->IsClient()) { - if (!verify_raid_client(mob->CastToClient())) - delayed_remove.insert(mob->GetID()); - } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { - if (!verify_raid_client_pet(mob)) - delayed_remove.insert(mob->GetID()); - } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { - auto npc = mob->CastToNPC(); - if (!verify_raid_client_swarm(npc)) + if (!verify_raid_client(mob->CastToClient())) { delayed_remove.insert(mob->GetID()); + } } - } else { // we're not on it! + else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { + if (!verify_raid_client_pet(mob)) { + delayed_remove.insert(mob->GetID()); + } + } + else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + auto npc = mob->CastToNPC(); + if (!verify_raid_client_swarm(npc)) { + delayed_remove.insert(mob->GetID()); + } + } + } + else { // we're not on it! if (mob->IsClient() && verify_raid_client(mob->CastToClient())) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { + } + } + else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + } + } + else if (mob->IsNPC() && mob->IsPetOwnerClient()) { auto npc = mob->CastToNPC(); if (verify_raid_client_swarm(npc)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } } - } else if (owner->IsGrouped()) { + } + else if (owner->IsGrouped()) { auto group = owner->GetGroup(); if (group == nullptr) { // uh oh owner->RemoveAura(GetID(), false, true); @@ -207,107 +243,133 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) // lambdas to make for loop less ugly auto verify_group_pet = [&group, this](Mob *m) { auto owner = m->GetOwner(); - if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance) + if (owner != nullptr && group->IsGroupMember(owner) && + DistanceSquared(GetPosition(), m->GetPosition()) <= distance) { return true; + } return false; }; auto verify_group_swarm = [&group, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); - if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance) + if (owner != nullptr && group->IsGroupMember(owner) && + DistanceSquared(GetPosition(), n->GetPosition()) <= distance) { return true; + } return false; }; for (auto &e : mob_list) { auto mob = e.second; - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // make sure we're still valid if (mob->IsPet()) { - if (!verify_group_pet(mob)) + if (!verify_group_pet(mob)) { delayed_remove.insert(mob->GetID()); - } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { - if (!verify_group_swarm(mob->CastToNPC())) + } + } + else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { + if (!verify_group_swarm(mob->CastToNPC())) { delayed_remove.insert(mob->GetID()); - } else if (!group->IsGroupMember(mob) || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { + } + } + else if (!group->IsGroupMember(mob) || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { delayed_remove.insert(mob->GetID()); } - } else { // not on, check if we should be! + } + else { // not on, check if we should be! if (mob->IsPet() && verify_group_pet(mob)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { + } + } + else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (group->IsGroupMember(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + } + } + else if (group->IsGroupMember(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } - } else { - auto verify_solo = [&owner, this](Mob *m) { - if (m->IsPet() && m->GetOwnerID() == owner->GetID()) + } + else { + auto verify_solo = [&owner, this](Mob *m) { + if (m->IsPet() && m->GetOwnerID() == owner->GetID()) { return true; - else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == owner->GetID()) + } + else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == owner->GetID()) { return true; - else if (m->GetID() == owner->GetID()) + } + else if (m->GetID() == owner->GetID()) { return true; - else + } + else { return false; + } }; for (auto &e : mob_list) { - auto mob = e.second; - auto it = casted_on.find(mob->GetID()); + auto mob = e.second; + auto it = casted_on.find(mob->GetID()); bool good = verify_solo(mob); if (it != casted_on.end()) { // make sure still valid if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { delayed_remove.insert(mob->GetID()); } - } else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + } + else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } for (auto &e : delayed_remove) { auto mob = entity_list.GetMob(e); - if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + if (mob != nullptr && is_buff) { // some auras cast instant spells so no need to remove mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } casted_on.erase(e); } // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it - if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) { cast_timer.Start(); + } - if (!cast_timer.Enabled() || !cast_timer.Check()) + if (!cast_timer.Enabled() || !cast_timer.Check()) { return; + } // some auras have to recast (DRU for example, non-buff too) for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { SpellFinished(spell_id, mob); + } } } void Aura::ProcessOnGroupMembersPets(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this,distance); std::set delayed_remove; - bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter // This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura) - auto group_member = owner->GetOwnerOrSelf(); + auto group_member = owner->GetOwnerOrSelf(); - if (group_member->IsRaidGrouped() && group_member->IsClient()) { // currently raids are just client, but safety check + if (group_member->IsRaidGrouped() && + group_member->IsClient()) { // currently raids are just client, but safety check auto raid = group_member->GetRaid(); if (raid == nullptr) { // well shit owner->RemoveAura(GetID(), false, true); @@ -320,9 +382,12 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient()); if (m->GetOwner()->GetID() == group_member->GetID()) { return DistanceSquared(GetPosition(), m->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { return false; } return true; @@ -330,14 +395,18 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) auto verify_raid_client_swarm = [&raid, &group_id, &group_member, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); - if (owner == nullptr) + if (owner == nullptr) { return false; + } auto idx = raid->GetPlayerIndex(owner->CastToClient()); if (owner->GetID() == group_member->GetID()) { return DistanceSquared(GetPosition(), n->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { return false; } return true; @@ -346,35 +415,44 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) for (auto &e : mob_list) { auto mob = e.second; // step 1: check if we're already managing this NPC's buff - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // verify still good! if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { - if (!verify_raid_client_pet(mob)) - delayed_remove.insert(mob->GetID()); - } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { - auto npc = mob->CastToNPC(); - if (!verify_raid_client_swarm(npc)) + if (!verify_raid_client_pet(mob)) { delayed_remove.insert(mob->GetID()); + } } - } else { // we're not on it! + else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + auto npc = mob->CastToNPC(); + if (!verify_raid_client_swarm(npc)) { + delayed_remove.insert(mob->GetID()); + } + } + } + else { // we're not on it! if (mob->IsClient()) { continue; // never hit client - } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { + } + else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + } + } + else if (mob->IsNPC() && mob->IsPetOwnerClient()) { auto npc = mob->CastToNPC(); if (verify_raid_client_swarm(npc)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } } - } else if (group_member->IsGrouped()) { + } + else if (group_member->IsGrouped()) { auto group = group_member->GetGroup(); if (group == nullptr) { // uh oh owner->RemoveAura(GetID(), false, true); @@ -384,111 +462,131 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) // lambdas to make for loop less ugly auto verify_group_pet = [&group, this](Mob *m) { auto owner = m->GetOwner(); - if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance) - return true; - return false; + return owner != nullptr && group->IsGroupMember(owner) && + DistanceSquared(GetPosition(), m->GetPosition()) <= distance; }; auto verify_group_swarm = [&group, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); - if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance) - return true; - return false; + return owner != nullptr && group->IsGroupMember(owner) && + DistanceSquared(GetPosition(), n->GetPosition()) <= distance; }; for (auto &e : mob_list) { auto mob = e.second; - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // make sure we're still valid if (mob->IsPet()) { - if (!verify_group_pet(mob)) - delayed_remove.insert(mob->GetID()); - } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { - if (!verify_group_swarm(mob->CastToNPC())) + if (!verify_group_pet(mob)) { delayed_remove.insert(mob->GetID()); + } } - } else { // not on, check if we should be! + else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { + if (!verify_group_swarm(mob->CastToNPC())) { + delayed_remove.insert(mob->GetID()); + } + } + } + else { // not on, check if we should be! if (mob->IsClient()) { continue; - } else if (mob->IsPet() && verify_group_pet(mob)) { + } + else if (mob->IsPet() && verify_group_pet(mob)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { + } + } + else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } - } else { - auto verify_solo = [&group_member, this](Mob *m) { - if (m->IsPet() && m->GetOwnerID() == group_member->GetID()) + } + else { + auto verify_solo = [&group_member, this](Mob *m) { + if (m->IsPet() && m->GetOwnerID() == group_member->GetID()) { return true; - else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == group_member->GetID()) + } + else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == group_member->GetID()) { return true; - else + } + else { return false; + } }; for (auto &e : mob_list) { - auto mob = e.second; - auto it = casted_on.find(mob->GetID()); + auto mob = e.second; + auto it = casted_on.find(mob->GetID()); bool good = verify_solo(mob); if (it != casted_on.end()) { // make sure still valid if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { delayed_remove.insert(mob->GetID()); } - } else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + } + else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } for (auto &e : delayed_remove) { auto mob = entity_list.GetMob(e); - if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + if (mob != nullptr && is_buff) { // some auras cast instant spells so no need to remove mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } casted_on.erase(e); } // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it - if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) { cast_timer.Start(); + } - if (!cast_timer.Enabled() || !cast_timer.Check()) + if (!cast_timer.Enabled() || !cast_timer.Check()) { return; + } // some auras have to recast (DRU for example, non-buff too) for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { SpellFinished(spell_id, mob); + } } } void Aura::ProcessTotem(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); std::set delayed_remove; - bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter for (auto &e : mob_list) { auto mob = e.second; - if (mob == this) + if (mob == this) { continue; - if (mob == owner) + } + if (mob == owner) { continue; + } if (owner->IsAttackAllowed(mob)) { // might need more checks ... bool in_range = DistanceSquared(GetPosition(), mob->GetPosition()) <= distance; - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { - if (!in_range) + if (!in_range) { delayed_remove.insert(mob->GetID()); - } else if (in_range) { + } + } + else if (in_range) { casted_on.insert(mob->GetID()); SpellFinished(spell_id, mob); } @@ -497,33 +595,38 @@ void Aura::ProcessTotem(Mob *owner) for (auto &e : delayed_remove) { auto mob = entity_list.GetMob(e); - if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + if (mob != nullptr && is_buff) { // some auras cast instant spells so no need to remove mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } casted_on.erase(e); } // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it - if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) { cast_timer.Start(); + } - if (!cast_timer.Enabled() || !cast_timer.Check()) + if (!cast_timer.Enabled() || !cast_timer.Check()) { return; + } for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { SpellFinished(spell_id, mob); + } } } void Aura::ProcessEnterTrap(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); for (auto &e : mob_list) { auto mob = e.second; - if (mob == this) + if (mob == this) { continue; + } // might need more checks ... if (owner->IsAttackAllowed(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { SpellFinished(spell_id, mob); @@ -535,23 +638,25 @@ void Aura::ProcessEnterTrap(Mob *owner) void Aura::ProcessExitTrap(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); for (auto &e : mob_list) { auto mob = e.second; - if (mob == this) + if (mob == this) { continue; + } // might need more checks ... if (owner->IsAttackAllowed(mob)) { bool in_range = DistanceSquared(GetPosition(), mob->GetPosition()) <= distance; - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { if (!in_range) { SpellFinished(spell_id, mob); owner->RemoveAura(GetID(), false); // if we're a buff we don't want to strip :P break; } - } else if (in_range) { + } + else if (in_range) { casted_on.insert(mob->GetID()); } } @@ -562,9 +667,14 @@ void Aura::ProcessExitTrap(Mob *owner) // and hard to reason about void Aura::ProcessSpawns() { - const auto &clients = entity_list.GetClientList(); - for (auto &e : clients) { - auto c = e.second; + const auto &clients = entity_list.GetCloseMobList(this, distance); + for (auto &e : clients) { + if (!e.second->IsClient()) { + continue; + } + + auto c = e.second->CastToClient(); + bool spawned = spawned_for.find(c->GetID()) != spawned_for.end(); if (ShouldISpawnFor(c)) { if (!spawned) { @@ -574,21 +684,22 @@ void Aura::ProcessSpawns() SendArmorAppearance(c); spawned_for.insert(c->GetID()); } - } else if (spawned) { + } + else if (spawned) { EQApplicationPacket app; CreateDespawnPacket(&app, false); c->QueuePacket(&app); spawned_for.erase(c->GetID()); } } - return; } bool Aura::Process() { // Aura::Depop clears buffs - if (p_depop) + if (p_depop) { return false; + } auto owner = entity_list.GetMob(m_owner); if (owner == nullptr) { @@ -604,7 +715,7 @@ bool Aura::Process() if (movement_type == AuraMovement::Follow && GetPosition() != owner->GetPosition() && movement_timer.Check()) { m_Position = owner->GetPosition(); auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - auto spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; + auto spu = (PlayerPositionUpdateServer_Struct *) app->pBuffer; MakeSpawnUpdate(spu); auto it = spawned_for.begin(); while (it != spawned_for.end()) { @@ -612,7 +723,8 @@ bool Aura::Process() if (client) { client->QueuePacket(app); ++it; - } else { + } + else { it = spawned_for.erase(it); } } @@ -620,14 +732,17 @@ bool Aura::Process() } // TODO: waypoints? - if (!process_timer.Check()) + if (!process_timer.Check()) { return true; + } - if (spawn_type != AuraSpawns::Noone) - ProcessSpawns(); // bit of a hack + if (spawn_type != AuraSpawns::Noone) { + ProcessSpawns(); + } // bit of a hack - if (process_func) + if (process_func) { process_func(*this, owner); + } // TODO: quest calls return true; @@ -635,49 +750,61 @@ bool Aura::Process() bool Aura::ShouldISpawnFor(Client *c) { - if (spawn_type == AuraSpawns::Noone) + if (spawn_type == AuraSpawns::Noone) { return false; + } - if (spawn_type == AuraSpawns::Everyone) + if (spawn_type == AuraSpawns::Everyone) { return true; + } // hey, it's our owner! - if (c->GetID() == m_owner) + if (c->GetID() == m_owner) { return true; + } // so this one is a bit trickier auto owner = GetOwner(); - if (owner == nullptr) - return false; // hmm + if (owner == nullptr) { + return false; + } // hmm owner = owner->GetOwnerOrSelf(); // pet auras we need the pet's owner - if (owner == nullptr) // shouldn't really be needed + if (owner == nullptr) { // shouldn't really be needed return false; + } // gotta check again for pet aura case -.- - if (owner == c) + if (owner == c) { return true; + } if (owner->IsRaidGrouped() && owner->IsClient()) { auto raid = owner->GetRaid(); - if (raid == nullptr) - return false; // hmm - auto group_id = raid->GetGroup(owner->CastToClient()); - if (group_id == 0xFFFFFFFF) // owner handled above, and they're in a raid and groupless + if (raid == nullptr) { return false; + } // hmm + auto group_id = raid->GetGroup(owner->CastToClient()); + if (group_id == 0xFFFFFFFF) { // owner handled above, and they're in a raid and groupless + return false; + } auto idx = raid->GetPlayerIndex(c); - if (idx == 0xFFFFFFFF) // they're not in our raid! + if (idx == 0xFFFFFFFF) { // they're not in our raid! return false; + } - if (raid->members[idx].GroupNumber != group_id) // in our raid, but not our group + if (raid->members[idx].GroupNumber != group_id) { // in our raid, but not our group return false; + } return true; // we got here so we know that 1 they're in our raid and 2 they're in our group! - } else if (owner->IsGrouped()) { + } + else if (owner->IsGrouped()) { auto group = owner->GetGroup(); - if (group == nullptr) - return false; // hmm + if (group == nullptr) { + return false; + } // hmm // easy, in our group return group->IsGroupMember(c); @@ -693,22 +820,23 @@ void Aura::Depop(bool skip_strip) if (!skip_strip && IsBuffSpell(spell_id)) { for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } } } casted_on.clear(); p_depop = true; } -// This creates an aura from a casted spell void Mob::MakeAura(uint16 spell_id) { // TODO: verify room in AuraMgr - if (!IsValidSpell(spell_id)) + if (!IsValidSpell(spell_id)) { return; + } - AuraRecord record; + AuraRecord record{}; if (!database.GetAuraEntry(spell_id, record)) { Message(Chat::Red, "Unable to find data for aura %s", spells[spell_id].name); LogError("Unable to find data for aura [{}], check auras table", spell_id); @@ -718,7 +846,7 @@ void Mob::MakeAura(uint16 spell_id) if (!IsValidSpell(record.spell_id)) { Message(Chat::Red, "Casted spell (%d) is not valid for aura %s", record.spell_id, spells[spell_id].name); LogError("Casted spell ([{}]) is not valid for aura [{}], check auras table", - record.spell_id, spell_id); + record.spell_id, spell_id); return; } @@ -729,23 +857,28 @@ void Mob::MakeAura(uint16 spell_id) bool trap = false; switch (static_cast(record.aura_type)) { - case AuraType::ExitTrap: - case AuraType::EnterTrap: - case AuraType::Totem: - trap = true; - break; - default: - trap = false; - break; + case AuraType::ExitTrap: + case AuraType::EnterTrap: + case AuraType::Totem: + trap = true; + break; + default: + trap = false; + break; } - if (!CanSpawnAura(trap)) + if (!CanSpawnAura(trap)) { return; + } const auto base = database.LoadNPCTypesData(record.npc_type); if (base == nullptr) { Message(Chat::Red, "Unable to load NPC data for aura %s", spells[spell_id].teleport_zone); - LogError("Unable to load NPC data for aura [{}] (NPC ID [{}]), check auras and npc_types tables", spells[spell_id].teleport_zone, record.npc_type); + LogError( + "Unable to load NPC data for aura [{}] (NPC ID [{}]), check auras and npc_types tables", + spells[spell_id].teleport_zone, + record.npc_type + ); return; } @@ -756,65 +889,82 @@ void Mob::MakeAura(uint16 spell_id) auto npc = new Aura(npc_type, this, record); npc->SetAuraID(spell_id); - if (trap) - npc->TryMoveAlong(5.0f, 0.0f, false); // try to place 5 units in front + if (trap) { + npc->TryMoveAlong(5.0f, 0.0f, false); + } // try to place 5 units in front entity_list.AddNPC(npc, false); - if (trap) + if (trap) { AddTrap(npc, record); - else + } + else { AddAura(npc, record); + } } bool ZoneDatabase::GetAuraEntry(uint16 spell_id, AuraRecord &record) { - auto query = StringFormat("SELECT npc_type, name, spell_id, distance, aura_type, spawn_type, movement, " - "duration, icon, cast_time FROM auras WHERE type='%d'", - spell_id); + auto query = StringFormat( + "SELECT npc_type, name, spell_id, distance, aura_type, spawn_type, movement, " + "duration, icon, cast_time FROM auras WHERE type='%d'", + spell_id + ); auto results = QueryDatabase(query); - if (!results.Success()) + if (!results.Success()) { return false; + } - if (results.RowCount() != 1) + if (results.RowCount() != 1) { return false; + } auto row = results.begin(); record.npc_type = atoi(row[0]); strn0cpy(record.name, row[1], 64); - record.spell_id = atoi(row[2]); - record.distance = atoi(row[3]); + record.spell_id = atoi(row[2]); + record.distance = atoi(row[3]); record.distance *= record.distance; // so we can avoid sqrt - record.aura_type = atoi(row[4]); + record.aura_type = atoi(row[4]); record.spawn_type = atoi(row[5]); - record.movement = atoi(row[6]); - record.duration = atoi(row[7]) * 1000; // DB is in seconds - record.icon = atoi(row[8]); - record.cast_time = atoi(row[9]) * 1000; // DB is in seconds + record.movement = atoi(row[6]); + record.duration = atoi(row[7]) * 1000; // DB is in seconds + record.icon = atoi(row[8]); + record.cast_time = atoi(row[9]) * 1000; // DB is in seconds return true; } void Mob::AddAura(Aura *aura, AuraRecord &record) { + LogAura( + "[AddAura] aura owner [{}] spawn_id [{}] aura_name [{}]", + GetCleanName(), + aura->GetID(), + aura->GetCleanName() + ); + // this is called only when it's safe assert(aura != nullptr); strn0cpy(aura_mgr.auras[aura_mgr.count].name, aura->GetCleanName(), 64); aura_mgr.auras[aura_mgr.count].spawn_id = aura->GetID(); - aura_mgr.auras[aura_mgr.count].aura = aura; - if (record.icon == -1) + aura_mgr.auras[aura_mgr.count].aura = aura; + if (record.icon == -1) { aura_mgr.auras[aura_mgr.count].icon = spells[record.spell_id].new_icon; - else + } + else { aura_mgr.auras[aura_mgr.count].icon = record.icon; + } + if (IsClient()) { - auto outapp = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraCreate_Struct)); - auto aura_create = (AuraCreate_Struct *)outapp->pBuffer; + auto outapp = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraCreate_Struct)); + auto aura_create = (AuraCreate_Struct *) outapp->pBuffer; aura_create->action = 0; - aura_create->type = 1; // this can be 0 sometimes too + aura_create->type = 1; // this can be 0 sometimes too strn0cpy(aura_create->aura_name, aura_mgr.auras[aura_mgr.count].name, 64); aura_create->entity_id = aura_mgr.auras[aura_mgr.count].spawn_id; - aura_create->icon = aura_mgr.auras[aura_mgr.count].icon; + aura_create->icon = aura_mgr.auras[aura_mgr.count].icon; CastToClient()->FastQueuePacket(&outapp); } // we can increment this now @@ -823,15 +973,24 @@ void Mob::AddAura(Aura *aura, AuraRecord &record) void Mob::AddTrap(Aura *aura, AuraRecord &record) { + LogAura( + "[AddTrap] aura owner [{}] spawn_id [{}] aura_name [{}]", + GetCleanName(), + aura->GetID(), + aura->GetCleanName() + ); + // this is called only when it's safe assert(aura != nullptr); strn0cpy(trap_mgr.auras[trap_mgr.count].name, aura->GetCleanName(), 64); trap_mgr.auras[trap_mgr.count].spawn_id = aura->GetID(); - trap_mgr.auras[trap_mgr.count].aura = aura; - if (record.icon == -1) + trap_mgr.auras[trap_mgr.count].aura = aura; + if (record.icon == -1) { trap_mgr.auras[trap_mgr.count].icon = spells[record.spell_id].new_icon; - else + } + else { trap_mgr.auras[trap_mgr.count].icon = record.icon; + } // doesn't send to client trap_mgr.count++; } @@ -841,7 +1000,8 @@ bool Mob::CanSpawnAura(bool trap) if (trap && !HasFreeTrapSlots()) { MessageString(Chat::SpellFailure, NO_MORE_TRAPS); return false; - } else if (!trap && !HasFreeAuraSlots()) { + } + else if (!trap && !HasFreeAuraSlots()) { MessageString(Chat::SpellFailure, NO_MORE_AURAS); return false; } @@ -861,8 +1021,16 @@ void Mob::RemoveAllAuras() // this is sent on camp/zone, so it just despawns? if (aura_mgr.count) { for (auto &e : aura_mgr.auras) { - if (e.aura) + if (e.aura) { + LogAura( + "[RemoveAllAuras] aura owner [{}] spawn_id [{}] aura_name [{}]", + GetCleanName(), + e.spawn_id, + e.name + ); + e.aura->Depop(); + } } } @@ -870,8 +1038,16 @@ void Mob::RemoveAllAuras() if (trap_mgr.count) { for (auto &e : trap_mgr.auras) { - if (e.aura) + if (e.aura) { + LogAura( + "[RemoveAllAuras] trap owner [{}] spawn_id [{}] aura_name [{}]", + GetCleanName(), + e.spawn_id, + e.name + ); + e.aura->Depop(); + } } } @@ -883,24 +1059,36 @@ void Mob::RemoveAura(int spawn_id, bool skip_strip, bool expired) for (int i = 0; i < aura_mgr.count; ++i) { auto &aura = aura_mgr.auras[i]; if (aura.spawn_id == spawn_id) { - if (aura.aura) + LogAura( + "[RemoveAura] mob [{}] spawn_id [{}] skip_strip [{}] expired [{}]", + GetCleanName(), + spawn_id, + skip_strip ? "true" : "false", + expired ? "true" : "false" + ); + + if (aura.aura) { aura.aura->Depop(skip_strip); + } if (expired && IsClient()) { + // TODO: verify color CastToClient()->SendColoredText( - Chat::Yellow, StringFormat("%s has expired.", aura.name)); // TODO: verify color + Chat::Yellow, + StringFormat("%s has expired.", aura.name) + ); // need to update client UI too auto app = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraDestory_Struct)); - auto ads = (AuraDestory_Struct *)app->pBuffer; - ads->action = 1; // delete + auto ads = (AuraDestory_Struct *) app->pBuffer; + ads->action = 1; // delete ads->entity_id = spawn_id; CastToClient()->QueuePacket(app); safe_delete(app); } while (aura_mgr.count - 1 > i) { i++; - aura.spawn_id = aura_mgr.auras[i].spawn_id; - aura.icon = aura_mgr.auras[i].icon; - aura.aura = aura_mgr.auras[i].aura; + aura.spawn_id = aura_mgr.auras[i].spawn_id; + aura.icon = aura_mgr.auras[i].icon; + aura.aura = aura_mgr.auras[i].aura; aura_mgr.auras[i].aura = nullptr; strn0cpy(aura.name, aura_mgr.auras[i].name, 64); } @@ -912,16 +1100,18 @@ void Mob::RemoveAura(int spawn_id, bool skip_strip, bool expired) for (int i = 0; i < trap_mgr.count; ++i) { auto &aura = trap_mgr.auras[i]; if (aura.spawn_id == spawn_id) { - if (aura.aura) + if (aura.aura) { aura.aura->Depop(skip_strip); - if (expired && IsClient()) + } + if (expired && IsClient()) { CastToClient()->SendColoredText( - Chat::Yellow, StringFormat("%s has expired.", aura.name)); // TODO: verify color + Chat::Yellow, StringFormat("%s has expired.", aura.name)); + } // TODO: verify color while (trap_mgr.count - 1 > i) { i++; - aura.spawn_id = trap_mgr.auras[i].spawn_id; - aura.icon = trap_mgr.auras[i].icon; - aura.aura = trap_mgr.auras[i].aura; + aura.spawn_id = trap_mgr.auras[i].spawn_id; + aura.icon = trap_mgr.auras[i].icon; + aura.aura = trap_mgr.auras[i].aura; trap_mgr.auras[i].aura = nullptr; strn0cpy(aura.name, trap_mgr.auras[i].name, 64); } @@ -930,6 +1120,5 @@ void Mob::RemoveAura(int spawn_id, bool skip_strip, bool expired) } } - return; } diff --git a/zone/aura.h b/zone/aura.h index ff4f2d51c..ae4cd0a4a 100644 --- a/zone/aura.h +++ b/zone/aura.h @@ -73,7 +73,7 @@ private: int m_owner; int aura_id; // spell ID of the aura spell -1 if aura isn't from a casted spell int spell_id; // spell we cast - int distance; // distance we remove + float distance; // distance we remove Timer remove_timer; // when we depop Timer process_timer; // rate limit process calls Timer cast_timer; // some auras pulse diff --git a/zone/entity.cpp b/zone/entity.cpp index 3d926e651..b13fb5649 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2624,6 +2624,24 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob) return false; } +/** + * @param mob + * @return + */ +void EntityList::RemoveAuraFromMobs(Mob *aura) +{ + LogEntityManagement( + "Attempting to remove aura [{}] from mobs entity_id ({})", + aura->GetCleanName(), + aura->GetID() + ); + + for (auto &it : mob_list) { + auto mob = it.second; + mob->RemoveAura(aura->GetID()); + } +} + /** * @param close_mobs * @param scanning_mob diff --git a/zone/entity.h b/zone/entity.h index a296ca794..505f34963 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -293,6 +293,7 @@ public: bool RemoveObject(uint16 delete_id); bool RemoveProximity(uint16 delete_npc_id); bool RemoveMobFromCloseLists(Mob *mob); + void RemoveAuraFromMobs(Mob *aura); void RemoveAllMobs(); void RemoveAllClients(); void RemoveAllNPCs(); @@ -584,7 +585,6 @@ private: private: std::list bot_list; #endif - }; class BulkZoneSpawnPacket { diff --git a/zone/mob.cpp b/zone/mob.cpp index af8aa25ab..71e876938 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -503,6 +503,8 @@ Mob::~Mob() UninitializeBuffSlots(); entity_list.RemoveMobFromCloseLists(this); + entity_list.RemoveAuraFromMobs(this); + close_mobs.clear(); #ifdef BOTS From 9cc73f2b4a3dcfc6588712aad452c728a0762ed8 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 20 Jan 2020 14:24:29 -0500 Subject: [PATCH 120/157] Fix formula for mana There were errors in the old formula for wis/int values over 201. --- zone/client_mods.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 1b8735d7d..2d61a59ec 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -610,14 +610,13 @@ int32 Client::CalcBaseMana() case 'I': WisInt = GetINT(); if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + ConvertedWisInt = WisInt; + int over200 = WisInt; if (WisInt > 100) { - ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); - if (WisInt > 201) { - ConvertedWisInt -= ((WisInt - 201) * 5 / 4); + if (WisInt > 200) { + over200 = (WisInt - 200) / -2 + WisInt; } - } - else { - ConvertedWisInt = WisInt; + ConvertedWisInt = (3 * over200 - 300) / 2 + over200; } auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) { @@ -643,14 +642,13 @@ int32 Client::CalcBaseMana() case 'W': WisInt = GetWIS(); if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + ConvertedWisInt = WisInt; + int over200 = WisInt; if (WisInt > 100) { - ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); - if (WisInt > 201) { - ConvertedWisInt -= ((WisInt - 201) * 5 / 4); + if (WisInt > 200) { + over200 = (WisInt - 200) / -2 + WisInt; } - } - else { - ConvertedWisInt = WisInt; + ConvertedWisInt = (3 * over200 - 300) / 2 + over200; } auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) { From 8e6dd638ffc5a7660d284e48dac4728436b0e173 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Mon, 20 Jan 2020 20:20:06 -0500 Subject: [PATCH 121/157] Implement SE_SummonCorpseZone (SPA 388). - This SPA summons all of a targeted group or raid group member's corpses from anywhere in the world. - Example Spell 16247 (Summon Remains) --- common/spdat.h | 2 +- zone/spell_effects.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++ zone/string_ids.h | 2 ++ zone/zonedb.cpp | 41 +++++++++++++++++++++++++++++++++ zone/zonedb.h | 2 ++ 5 files changed, 98 insertions(+), 1 deletion(-) diff --git a/common/spdat.h b/common/spdat.h index 416f63561..c7c6027c6 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -607,7 +607,7 @@ typedef enum { #define SE_LimitSpellGroup 385 // implemented - Limits to spell group(ie type 3 reuse reduction augs that are class specific and thus all share s SG) #define SE_CastOnCurer 386 // implemented - Casts a spell on the person curing #define SE_CastOnCure 387 // implemented - Casts a spell on the cured person -//#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA) +#define SE_SummonCorpseZone 388 // implemented - summons a corpse from any zone(nec AA) #define SE_FcTimerRefresh 389 // implemented - Refresh spell icons //#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited. #define SE_LimitManaMax 391 // implemented diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 0f29c38ce..2a30cf924 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1820,6 +1820,58 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + case SE_SummonCorpseZone: + { + if (IsClient()) { + Client* client_target = this->CastToClient(); + if(client_target->IsGrouped()) { + Group* group = client_target->GetGroup(); + if(!group->IsGroupMember(caster)) { + if (caster != this) { + caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); + break; + } + } + } else if (caster) { + if(caster->IsRaidGrouped()) { + Raid *raid = caster->GetRaid(); + uint32 group_id = raid->GetGroup(caster->GetName()); + if(group_id > 0 && group_id < MAX_RAID_GROUPS) { + if(raid->GetGroup(client_target->GetName()) != group_id) { + caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); + break; + } + } + } else { + if(caster != this) { + caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); + break; + } + } + } + + if(client_target) { + if(database.CountCharacterCorpses(client_target->CharacterID()) == 0) { + if (caster == this) { + Message(Chat::Yellow, "You have no corpses to summon."); + } else { + caster->Message(Chat::Yellow, "%s has no corpses to summon.", client_target->GetCleanName()); + } + } else { + if (caster == this) { + Message(Chat::Spells, "Summoning your corpses."); + } else { + caster->MessageString(Chat::Spells, SUMMONING_CORPSE_ZONE, client_target->GetCleanName()); + } + client_target->SummonAllCorpses(client_target->GetPosition()); + } + } else { + MessageString(Chat::Spells, TARGET_NOT_FOUND); + LogError("[{}] attempted to cast spell id [{}] with spell effect SE_SummonCorpseZone, but could not cast target into a Client object", GetCleanName(), spell_id); + } + } + break; + } case SE_AddMeleeProc: case SE_WeaponProc: { diff --git a/zone/string_ids.h b/zone/string_ids.h index acab3df22..0948d13a8 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -65,6 +65,7 @@ #define SILENCED_STRING 207 //You *CANNOT* cast spells, you have been silenced! #define CANNOT_AFFECT_PC 210 //That spell can not affect this target PC. #define SPELL_NEED_TAR 214 //You must first select a target for this spell! +#define SUMMON_ONLY_GROUP_CORPSE 215 //You must first target a living group member whose corpse you wish to summon. #define ONLY_ON_CORPSES 221 //This spell only works on corpses. #define CANT_DRAIN_SELF 224 //You can't drain yourself! #define CORPSE_NOT_VALID 230 //This corpse is not valid. @@ -169,6 +170,7 @@ #define PVP_ON 552 //You are now player kill and follow the ways of Discord. #define GENERIC_STRINGID_SAY 554 //%1 says '%T2' #define CANNOT_WAKE 555 //%1 tells you, 'I am unable to wake %2, master.' +#define SUMMONING_CORPSE_ZONE 596 //Summoning %1's corpse(s). #define PET_HOLD_SET_ON 698 //The pet hold mode has been set to on. #define PET_HOLD_SET_OFF 699 //The pet hold mode has been set to off. #define PET_FOCUS_SET_ON 700 //The pet focus mode has been set to on. diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index dc2e29cad..d5346877d 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -4707,6 +4707,47 @@ bool ZoneDatabase::SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zone_id return (CorpseCount > 0); } +int ZoneDatabase::CountCharacterCorpses(uint32 char_id) { + std::string query = fmt::format( + SQL( + SELECT + COUNT(*) + FROM + character_corpses + WHERE + charid = '{}' + ), + char_id + ); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); + } + return 0; +} + +int ZoneDatabase::CountCharacterCorpsesByZoneID(uint32 char_id, uint32 zone_id) { + std::string query = fmt::format( + SQL( + SELECT + COUNT(*) + FROM + character_corpses + WHERE + charid = '{}' + AND + zone_id = '{}' + ), + char_id, + zone_id + ); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); + } + return 0; +} + bool ZoneDatabase::UnburyCharacterCorpse(uint32 db_id, uint32 new_zone_id, uint16 new_instance_id, const glm::vec4& position) { std::string query = StringFormat("UPDATE `character_corpses` " "SET `is_buried` = 0, `zone_id` = %u, `instance_id` = %u, " diff --git a/zone/zonedb.h b/zone/zonedb.h index e93a40713..f28ac5ec4 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -356,6 +356,8 @@ public: bool DeleteCharacterCorpse(uint32 dbid); bool SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, const glm::vec4& position); bool SummonAllGraveyardCorpses(uint32 cur_zoneid, uint32 dest_zoneid, uint16 dest_instanceid, const glm::vec4& position); + int CountCharacterCorpses(uint32 char_id); + int CountCharacterCorpsesByZoneID(uint32 char_id, uint32 zone_id); bool UnburyCharacterCorpse(uint32 dbid, uint32 new_zoneid, uint16 dest_instanceid, const glm::vec4& position); bool LoadCharacterCorpses(uint32 iZoneID, uint16 iInstanceID); bool DeleteGraveyard(uint32 zone_id, uint32 graveyard_id); From caceae1028492dc61b5eb81f6e61fad8a5e4da9f Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Mon, 20 Jan 2020 20:23:11 -0500 Subject: [PATCH 122/157] Implement Corpse counting methods for global/zone-specific counting. Global: - Perl: quest::getplayercorpsecount(uint32 char_id); - Lua: eq.get_player_corpse_count(uint32 char_id); Zone-specific: - Perl: quest::getplayercorpsecountbyzoneid(uint32 char_id, uint32 zone_id); - Lua: eq.get_player_corpse_count_by_zone_id(uint32 char_id, uint32 zone_id); --- zone/embparser_api.cpp | 39 +++++++++++++++++++++++++++++++++++++++ zone/lua_general.cpp | 10 ++++++++++ zone/questmgr.cpp | 15 +++++++++++++++ zone/questmgr.h | 2 ++ 4 files changed, 66 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index c29c28e20..871cc0ced 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1812,6 +1812,43 @@ XS(XS__summonallplayercorpses) { XSRETURN(1); } +XS(XS__getplayercorpsecount); +XS(XS__getplayercorpsecount) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getplayercorpsecount(uint32 char_id)"); + + uint32 RETVAL; + dXSTARG; + + uint32 char_id = (int) SvIV(ST(0)); + + RETVAL = quest_manager.getplayercorpsecount(char_id); + XSprePUSH; + PUSHu((IV) RETVAL); + + XSRETURN(1); +} + +XS(XS__getplayercorpsecountbyzoneid); +XS(XS__getplayercorpsecountbyzoneid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::getplayercorpsecountbyzoneid(uint32 char_id, uint32 zone_id)"); + + uint32 RETVAL; + dXSTARG; + + uint32 char_id = (int) SvIV(ST(0)); + uint32 zone_id = (int)SvIV(ST(1)); + + RETVAL = quest_manager.getplayercorpsecountbyzoneid(char_id, zone_id); + XSprePUSH; + PUSHu((IV) RETVAL); + + XSRETURN(1); +} + XS(XS__getplayerburiedcorpsecount); XS(XS__getplayerburiedcorpsecount) { dXSARGS; @@ -3907,6 +3944,8 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getlevel"), XS__getlevel, file); newXS(strcpy(buf, "getplayerburiedcorpsecount"), XS__getplayerburiedcorpsecount, file); + newXS(strcpy(buf, "getplayercorpsecount"), XS__getplayercorpsecount, file); + newXS(strcpy(buf, "getplayercorpsecountbyzoneid"), XS__getplayercorpsecountbyzoneid, file); newXS(strcpy(buf, "gettaskactivitydonecount"), XS__gettaskactivitydonecount, file); newXS(strcpy(buf, "givecash"), XS__givecash, file); newXS(strcpy(buf, "gmmove"), XS__gmmove, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index ff0c6b10a..0d1086ad1 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -549,6 +549,14 @@ void lua_summon_all_player_corpses(uint32 char_id, float x, float y, float z, fl quest_manager.summonallplayercorpses(char_id, glm::vec4(x, y, z, h)); } +int lua_get_player_corpse_count(uint32 char_id) { + return database.CountCharacterCorpses(char_id); +} + +int lua_get_player_corpse_count_by_zone_id(uint32 char_id, uint32 zone_id) { + return database.CountCharacterCorpsesByZoneID(char_id, zone_id); +} + int lua_get_player_buried_corpse_count(uint32 char_id) { return quest_manager.getplayerburiedcorpsecount(char_id); } @@ -1663,6 +1671,8 @@ luabind::scope lua_register_general() { luabind::def("toggle_spawn_event", &lua_toggle_spawn_event), luabind::def("summon_buried_player_corpse", &lua_summon_buried_player_corpse), luabind::def("summon_all_player_corpses", &lua_summon_all_player_corpses), + luabind::def("get_player_corpse_count", &lua_get_player_corpse_count), + luabind::def("get_player_corpse_count_by_zone_id", &lua_get_player_corpse_count_by_zone_id), luabind::def("get_player_buried_corpse_count", &lua_get_player_buried_corpse_count), luabind::def("bury_player_corpse", &lua_bury_player_corpse), luabind::def("task_selector", &lua_task_selector), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 69e55a57f..b48cd8993 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1910,6 +1910,21 @@ bool QuestManager::summonallplayercorpses(uint32 char_id, const glm::vec4& posit return true; } +int QuestManager::getplayercorpsecount(uint32 char_id) { + if (char_id > 0) + return database.CountCharacterCorpses(char_id); + + return 0; + +} + +int QuestManager::getplayercorpsecountbyzoneid(uint32 char_id, uint32 zone_id) { + if (char_id > 0 && zone_id > 0) + return database.CountCharacterCorpsesByZoneID(char_id, zone_id); + + return 0; +} + uint32 QuestManager::getplayerburiedcorpsecount(uint32 char_id) { uint32 Result = 0; diff --git a/zone/questmgr.h b/zone/questmgr.h index 1b9ea9c4d..5ad551c43 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -172,6 +172,8 @@ public: bool summonburiedplayercorpse(uint32 char_id, const glm::vec4& position); bool summonallplayercorpses(uint32 char_id, const glm::vec4& position); uint32 getplayerburiedcorpsecount(uint32 char_id); + int getplayercorpsecount(uint32 char_id); + int getplayercorpsecountbyzoneid(uint32 char_id, uint32 zone_id); bool buryplayercorpse(uint32 char_id); void forcedooropen(uint32 doorid, bool altmode); void forcedoorclose(uint32 doorid, bool altmode); From 8b37ef5e672d16d3a7baf41659baa4bc42abee66 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Mon, 20 Jan 2020 21:14:28 -0500 Subject: [PATCH 123/157] Formatting --- zone/questmgr.cpp | 8 ++++---- zone/spell_effects.cpp | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index b48cd8993..bea136e28 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1911,17 +1911,17 @@ bool QuestManager::summonallplayercorpses(uint32 char_id, const glm::vec4& posit } int QuestManager::getplayercorpsecount(uint32 char_id) { - if (char_id > 0) + if (char_id > 0) { return database.CountCharacterCorpses(char_id); - + } return 0; } int QuestManager::getplayercorpsecountbyzoneid(uint32 char_id, uint32 zone_id) { - if (char_id > 0 && zone_id > 0) + if (char_id > 0 && zone_id > 0) { return database.CountCharacterCorpsesByZoneID(char_id, zone_id); - + } return 0; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2a30cf924..693222e9f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1824,34 +1824,34 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove { if (IsClient()) { Client* client_target = this->CastToClient(); - if(client_target->IsGrouped()) { + if (client_target->IsGrouped()) { Group* group = client_target->GetGroup(); - if(!group->IsGroupMember(caster)) { + if (!group->IsGroupMember(caster)) { if (caster != this) { caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); break; } } } else if (caster) { - if(caster->IsRaidGrouped()) { + if (caster->IsRaidGrouped()) { Raid *raid = caster->GetRaid(); uint32 group_id = raid->GetGroup(caster->GetName()); - if(group_id > 0 && group_id < MAX_RAID_GROUPS) { - if(raid->GetGroup(client_target->GetName()) != group_id) { + if (group_id > 0 && group_id < MAX_RAID_GROUPS) { + if (raid->GetGroup(client_target->GetName()) != group_id) { caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); break; } } } else { - if(caster != this) { + if (caster != this) { caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); break; } } } - if(client_target) { - if(database.CountCharacterCorpses(client_target->CharacterID()) == 0) { + if (client_target) { + if (database.CountCharacterCorpses(client_target->CharacterID()) == 0) { if (caster == this) { Message(Chat::Yellow, "You have no corpses to summon."); } else { From c590cf7c35e118a9e876a0c533a67ba118dc6cc6 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 23 Jan 2020 23:36:13 -0500 Subject: [PATCH 124/157] Let's try updating travis to bionic --- .travis.yml | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index faed5b846..38db9d797 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,26 +1,18 @@ language: cpp compiler: gcc -dist: trusty +dist: bionic -before_install: - - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - sudo apt-get update -qq - - mkdir $HOME/usr - - export PATH="$HOME/usr/bin:$PATH" - - wget https://cmake.org/files/v3.11/cmake-3.11.2-Linux-x86_64.sh - - chmod +x cmake-3.11.2-Linux-x86_64.sh - - ./cmake-3.11.2-Linux-x86_64.sh --prefix=$HOME/usr --exclude-subdir --skip-license +addons: + apt: + packages: + - libmysqlclient-dev + - libperl-dev + - libboost-dev + - liblua5.1-0-dev + - zlib1g-dev + - uuid-dev + - libssl-dev -install: - - sudo apt-get install -qq g++-7 - - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 90 - - sudo apt-get install libmysqlclient-dev - - sudo apt-get install libperl-dev - - sudo apt-get install libboost-dev - - sudo apt-get install liblua5.1-0-dev - - sudo apt-get install zlib1g-dev - - sudo apt-get install uuid-dev - - sudo apt-get install libssl-dev script: - cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LOGIN=ON - make -j2 From c2b3e852724f67b2540ef9405bf1554138e8161b Mon Sep 17 00:00:00 2001 From: regneq Date: Fri, 24 Jan 2020 15:11:08 -0800 Subject: [PATCH 125/157] Added new pathgrid type 7 (GridCenterPoint). This grid causes a NPC to alternate between the first waypoint in their grid (Number 1 in the editor) and a random waypoint. (1 - 7 - 1 - 4 - 1 - 11 - 1 - 5 - 1, etc) Changed the wandertype IDs to an enum so we know what we're looking at. Added new pathgrid type 8 (GridRandomCenterPoint). (SQL required) This new type causes a NPC to alternate between a random waypoint in grid_entries and a random waypoint marked with the new centerpoint column set to true. If no waypoints are marked as a centerpoint, this wandertype will not work. There is no numbering requirement or limit for centerpoints. You can have as many as you need. New spawngroup field: wp_spawns (SQL required). Added a new spawngroup field, which is a boolean that if true changes the behavior of spawngroups this way: If the spawnpoint in the spawngroup has a grid, the NPC will spawn at a random waypoint location taken from its grid instead of the spawnpoint location. New randompath behavior: The randompath grid type will now use the closest waypoint as its current waypoint on spawning. This allows multiple spawn locations to use the same grid without having the undesirable behavior of walking to the first waypoint through walls and ignoring waypoint nodes. NPC::GetClosestWaypoint() was renamed to NPC::GetClosestWaypoints() as it was filling a list of multiple waypoints. a new method NPC::GetClosestWaypoint() returns a single waypoint in the form of an integer. --- utils/sql/db_update_manifest.txt | 1 + .../2020_01_24_grid_centerpoint_wp.sql | 2 + zone/common.h | 13 ++ zone/mob_ai.cpp | 30 +++- zone/npc.h | 5 +- zone/spawn2.cpp | 22 ++- zone/spawn2.h | 2 +- zone/spawngroup.cpp | 16 +- zone/spawngroup.h | 4 +- zone/waypoints.cpp | 165 +++++++++++++++--- zone/zonedb.h | 2 + 11 files changed, 219 insertions(+), 43 deletions(-) create mode 100644 utils/sql/git/required/2020_01_24_grid_centerpoint_wp.sql diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index aa7897e7f..53a71e109 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -400,6 +400,7 @@ 9144|2019_11_09_logsys_description_update.sql|SELECT * FROM db_version WHERE version >= 9143|empty| 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| # 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_24_grid_centerpoint_wp.sql b/utils/sql/git/required/2020_01_24_grid_centerpoint_wp.sql new file mode 100644 index 000000000..b4a118242 --- /dev/null +++ b/utils/sql/git/required/2020_01_24_grid_centerpoint_wp.sql @@ -0,0 +1,2 @@ +alter table grid_entries add column `centerpoint` tinyint(4) not null default 0; +alter table spawngroup add column `wp_spawns` tinyint(1) unsigned not null default 0; \ No newline at end of file diff --git a/zone/common.h b/zone/common.h index e06fa134a..2d97527fc 100644 --- a/zone/common.h +++ b/zone/common.h @@ -647,6 +647,19 @@ enum { SKILLUP_FAILURE = 2 }; +enum { + GridCircular, + GridRandom10, + GridRandom, + GridPatrol, + GridOneWayRepop, + GridRand5LoS, + GridOneWayDepop, + GridCenterPoint, + GridRandomCenterPoint, + GridRandomPath +}; + typedef enum { petFamiliar, //only listens to /pet get lost petAnimation, //does not listen to any commands diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 525948bb2..43d85c450 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1693,12 +1693,32 @@ void NPC::AI_DoMovement() { GetZ(), GetGrid()); + if (wandertype == GridRandomPath) + { + if (cur_wp == patrol) + { + // reached our randomly selected destination; force a pause + if (cur_wp_pause == 0) + { + if (Waypoints.size() > 0 && Waypoints[0].pause) + cur_wp_pause = Waypoints[0].pause; + else + cur_wp_pause = 38; + } + Log(Logs::Detail, Logs::AI, "NPC using wander type GridRandomPath on grid %d at waypoint %d has reached its random destination; pause time is %d", GetGrid(), cur_wp, cur_wp_pause); + } + else + cur_wp_pause = 0; // skipping pauses until destination + } + SetWaypointPause(); - SetAppearance(eaStanding, false); - if (cur_wp_pause > 0) { + if (GetAppearance() != eaStanding) { + SetAppearance(eaStanding, false); + } + if (cur_wp_pause > 0 && m_CurrentWayPoint.w >= 0.0) { RotateTo(m_CurrentWayPoint.w); } - + //kick off event_waypoint arrive char temp[16]; sprintf(temp, "%d", cur_wp); @@ -1789,12 +1809,12 @@ void NPC::AI_SetupNextWaypoint() { } } - if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { + if (wandertype == GridOneWayRepop && cur_wp == CastToNPC()->GetMaxWp()) { CastToNPC()->Depop(true); //depop and restart spawn timer if (found_spawn) found_spawn->SetNPCPointerNull(); } - else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { + else if (wandertype == GridOneWayDepop && cur_wp == CastToNPC()->GetMaxWp()) { CastToNPC()->Depop(false);//depop without spawn timer if (found_spawn) found_spawn->SetNPCPointerNull(); diff --git a/zone/npc.h b/zone/npc.h index 84ab8b4ef..d0ecdf1ca 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -303,7 +303,7 @@ public: int GetMaxWp() const { return max_wp; } void DisplayWaypointInfo(Client *to); void CalculateNewWaypoint(); - void AssignWaypoints(int32 grid); + void AssignWaypoints(int32 grid, int start_wp = 0); void SetWaypointPause(); void UpdateWaypoint(int wp_index); @@ -312,7 +312,8 @@ public: void ResumeWandering(); void PauseWandering(int pausetime); void MoveTo(const glm::vec4& position, bool saveguardspot); - void GetClosestWaypoint(std::list &wp_list, int count, const glm::vec3& location); + void GetClosestWaypoints(std::list &wp_list, int count, const glm::vec3& location); + int GetClosestWaypoint(const glm::vec3& location); uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; // returns item id int32 GetEquipmentMaterial(uint8 material_slot) const; diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index e6d265813..958500550 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -233,6 +233,20 @@ bool Spawn2::Process() { } currentnpcid = npcid; + + glm::vec4 loc(x, y, z, heading); + int starting_wp = 0; + if (spawn_group->wp_spawns && grid_ > 0) + { + glm::vec4 wploc; + starting_wp = database.GetRandomWaypointLocFromGrid(wploc, zone->GetZoneID(), grid_); + if (wploc.x != 0.0f || wploc.y != 0.0f || wploc.z != 0.0f) + { + loc = wploc; + Log(Logs::General, Logs::Spawns, "spawning at random waypoint #%i loc: (%.3f, %.3f, %.3f).", starting_wp , loc.x, loc.y, loc.z); + } + } + NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water); npc->mod_prespawn(this); @@ -275,7 +289,7 @@ bool Spawn2::Process() { z ); - LoadGrid(); + LoadGrid(starting_wp); } else { LogSpawns("Spawn2 [{}]: Group [{}] spawned [{}] ([{}]) at ([{}], [{}], [{}]). Grid loading delayed", @@ -302,7 +316,7 @@ void Spawn2::Disable() enabled = false; } -void Spawn2::LoadGrid() { +void Spawn2::LoadGrid(int start_wp) { if (!npcthis) return; if (grid_ < 1) @@ -311,8 +325,8 @@ void Spawn2::LoadGrid() { return; //dont set an NPC's grid until its loaded for them. npcthis->SetGrid(grid_); - npcthis->AssignWaypoints(grid_); - LogSpawns("Spawn2 [{}]: Loading grid [{}] for [{}]", spawn2_id, grid_, npcthis->GetName()); + npcthis->AssignWaypoints(grid_, start_wp); + LogSpawns("Spawn2 [{}]: Loading grid [{}] for [{}]; starting wp is [{}]", spawn2_id, grid_, npcthis->GetName(), start_wp); } /* diff --git a/zone/spawn2.h b/zone/spawn2.h index a626b7084..bf6530876 100644 --- a/zone/spawn2.h +++ b/zone/spawn2.h @@ -36,7 +36,7 @@ public: uint16 cond_id = SC_AlwaysEnabled, int16 min_value = 0, bool in_enabled = true, EmuAppearance anim = eaStanding); ~Spawn2(); - void LoadGrid(); + void LoadGrid(int start_wp = 0); void Enable() { enabled = true; } void Disable(); bool Enabled() { return enabled; } diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index cc3735386..6a7817bce 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -48,7 +48,8 @@ SpawnGroup::SpawnGroup( int delay_in, int despawn_in, uint32 despawn_timer_in, - int min_delay_in + int min_delay_in, + bool wp_spawns_in ) { id = in_id; @@ -63,6 +64,7 @@ SpawnGroup::SpawnGroup( delay = delay_in; despawn = despawn_in; despawn_timer = despawn_timer_in; + wp_spawns = wp_spawns_in; } uint32 SpawnGroup::GetNPCType(uint16 in_filter) @@ -198,7 +200,8 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, - spawngroup.mindelay + spawngroup.mindelay, + spawngroup.wp_spawns FROM spawn2, spawngroup @@ -229,7 +232,8 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG atoi(row[8]), atoi(row[9]), atoi(row[10]), - atoi(row[11]) + atoi(row[11]), + atoi(row[12]) ); spawn_group_list->AddSpawnGroup(new_spawn_group); @@ -305,7 +309,8 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, - spawngroup.mindelay + spawngroup.mindelay, + spawngroup.wp_spawns FROM spawngroup WHERE @@ -332,7 +337,8 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn atoi(row[8]), atoi(row[9]), atoi(row[10]), - atoi(row[11]) + atoi(row[11]), + atoi(row[12]) ); spawn_group_list->AddSpawnGroup(new_spawn_group); diff --git a/zone/spawngroup.h b/zone/spawngroup.h index 2a4c1af6c..a1068cea0 100644 --- a/zone/spawngroup.h +++ b/zone/spawngroup.h @@ -49,13 +49,15 @@ public: int delay_in, int despawn_in, uint32 despawn_timer_in, - int min_delay_in + int min_delay_in, + bool wp_spawns_in ); ~SpawnGroup(); uint32 GetNPCType(uint16 condition_value_filter=1); void AddSpawnEntry(SpawnEntry *newEntry); uint32 id; + bool wp_spawns; // if true, spawn NPCs at a random waypoint location (if spawnpoint has a grid) instead of the spawnpoint's loc float roamdist; float roambox[4]; int min_delay; diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index b6033744c..0bd18edbf 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -244,14 +244,14 @@ void NPC::CalculateNewWaypoint() int old_wp = cur_wp; bool reached_end = false; bool reached_beginning = false; - if (cur_wp == max_wp) + if (cur_wp == max_wp - 1) //cur_wp starts at 0, max_wp starts at 1. reached_end = true; if (cur_wp == 0) reached_beginning = true; switch (wandertype) { - case 0: //circle + case GridCircular: { if (reached_end) cur_wp = 0; @@ -259,10 +259,10 @@ void NPC::CalculateNewWaypoint() cur_wp = cur_wp + 1; break; } - case 1: //10 closest + case GridRandom10: { std::list closest; - GetClosestWaypoint(closest, 10, glm::vec3(GetPosition())); + GetClosestWaypoints(closest, 10, glm::vec3(GetPosition())); auto iter = closest.begin(); if (closest.size() != 0) { @@ -273,30 +273,64 @@ void NPC::CalculateNewWaypoint() break; } - case 2: //random + case GridRandom: + case GridCenterPoint: { - cur_wp = zone->random.Int(0, Waypoints.size() - 1); - if (cur_wp == old_wp) + if (wandertype == GridCenterPoint && !reached_beginning) { - if (cur_wp == (Waypoints.size() - 1)) + cur_wp = 0; + } + else + { + cur_wp = zone->random.Int(0, Waypoints.size() - 1); + if (cur_wp == old_wp || (wandertype == GridCenterPoint && cur_wp == 0)) { - if (cur_wp > 0) + if (cur_wp == (Waypoints.size() - 1)) { - cur_wp--; + if (cur_wp > 0) + { + cur_wp--; + } } - } - else if (cur_wp == 0) - { - if ((Waypoints.size() - 1) > 0) + else if (cur_wp == 0) { - cur_wp++; + if ((Waypoints.size() - 1) > 0) + { + cur_wp++; + } } } } break; } - case 3: //patrol + case GridRandomCenterPoint: + { + bool on_center = Waypoints[cur_wp].centerpoint; + std::vector random_waypoints; + for (auto &w : Waypoints) + { + wplist wpl = w; + if (wpl.index != cur_wp && + ((on_center && !wpl.centerpoint) || (!on_center && wpl.centerpoint))) + { + random_waypoints.push_back(w); + } + } + + if (random_waypoints.size() == 0) + { + cur_wp = 0; + } + else + { + int windex = zone->random.Roll0(random_waypoints.size()); + cur_wp = random_waypoints[windex].index; + } + + break; + } + case GridPatrol: { if (reached_end) patrol = 1; @@ -309,16 +343,16 @@ void NPC::CalculateNewWaypoint() break; } - case 4: //goto the end and depop with spawn timer - case 6: //goto the end and depop without spawn timer + case GridOneWayRepop: + case GridOneWayDepop: { cur_wp = cur_wp + 1; break; } - case 5: //pick random closest 5 and pick one that's in sight + case GridRand5LoS: { std::list closest; - GetClosestWaypoint(closest, 5, glm::vec3(GetPosition())); + GetClosestWaypoints(closest, 5, glm::vec3(GetPosition())); auto iter = closest.begin(); while (iter != closest.end()) @@ -341,6 +375,25 @@ void NPC::CalculateNewWaypoint() } break; } + case GridRandomPath: // randomly select a waypoint but follow path to it instead of walk directly to it ignoring walls + { + if (Waypoints.size() == 0) + { + cur_wp = 0; + } + else + { + if (cur_wp == patrol) // reutilizing patrol member instead of making new member for this wander type; here we use it to save a random waypoint + { + while (patrol == cur_wp) + patrol = zone->random.Int(0, Waypoints.size() - 1); + } + if (patrol > cur_wp) + cur_wp = cur_wp + 1; + else + cur_wp = cur_wp - 1; + } + } } // Preserve waypoint setting for quest controlled NPCs @@ -357,7 +410,30 @@ bool wp_distance_pred(const wp_distance& left, const wp_distance& right) return left.dist < right.dist; } -void NPC::GetClosestWaypoint(std::list &wp_list, int count, const glm::vec3& location) +int NPC::GetClosestWaypoint(const glm::vec3& location) +{ + if (Waypoints.size() <= 1) + return 0; + + int closest = 0; + float closestDist = 9999999.0f; + float dist; + + for (int i = 0; i < Waypoints.size(); ++i) + { + dist = DistanceSquared(location, glm::vec3(Waypoints[i].x, Waypoints[i].y, Waypoints[i].z)); + + if (dist < closestDist) + { + closestDist = dist; + closest = i; + } + } + return closest; +} + +// fills wp_list with the closest count number of waypoints +void NPC::GetClosestWaypoints(std::list &wp_list, int count, const glm::vec3& location) { wp_list.clear(); if (Waypoints.size() <= count) @@ -485,7 +561,7 @@ void Mob::StopNavigation() { mMovementManager->StopNavigation(this); } -void NPC::AssignWaypoints(int32 grid) +void NPC::AssignWaypoints(int32 grid, int start_wp) { if (grid == 0) return; // grid ID 0 not supported @@ -518,7 +594,7 @@ void NPC::AssignWaypoints(int32 grid) SetGrid(grid); // Assign grid number // Retrieve all waypoints for this grid - query = StringFormat("SELECT `x`,`y`,`z`,`pause`,`heading` " + query = StringFormat("SELECT `x`,`y`,`z`,`pause`,`heading`, `centerpoint` " "FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %i " "ORDER BY `number`", grid, zone->GetZoneID()); results = database.QueryDatabase(query); @@ -539,14 +615,22 @@ void NPC::AssignWaypoints(int32 grid) newwp.pause = atoi(row[3]); newwp.heading = atof(row[4]); + newwp.centerpoint = atobool(row[5]); Waypoints.push_back(newwp); } - UpdateWaypoint(0); + cur_wp = start_wp; + UpdateWaypoint(start_wp); SetWaypointPause(); - if (wandertype == 1 || wandertype == 2 || wandertype == 5) + if (wandertype == GridRandomPath) { + cur_wp = GetClosestWaypoint(glm::vec3(GetPosition())); + patrol = cur_wp; + } + + if (wandertype == GridRandom10 || wandertype == GridRandom || wandertype == GridRand5LoS) CalculateNewWaypoint(); + } void Mob::SendTo(float new_x, float new_y, float new_z) { @@ -1058,6 +1142,37 @@ int ZoneDatabase::GetHighestWaypoint(uint32 zoneid, uint32 gridid) { return atoi(row[0]); } +int ZoneDatabase::GetRandomWaypointLocFromGrid(glm::vec4 &loc, uint16 zoneid, int grid) +{ + loc.x = loc.y = loc.z = loc.w = 0.0f; + + std::string query = StringFormat("SELECT `x`,`y`,`z`,`heading` " + "FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %u ORDER BY `number`", grid, zone->GetZoneID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + Log(Logs::General, Logs::Error, "MySQL Error while trying get random waypoint loc from grid %i in zoneid %u; %s", grid, zoneid, results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() > 0) + { + int roll = zone->random.Int(0, results.RowCount() - 1); + int i = 0; + auto row = results.begin(); + while (i < roll) + { + row++; + i++; + } + loc.x = atof(row[0]); + loc.y = atof(row[1]); + loc.z = atof(row[2]); + loc.w = atof(row[3]); + return i; + } + return 0; +} + void NPC::SaveGuardSpotCharm() { m_GuardPointSaved = m_GuardPoint; diff --git a/zone/zonedb.h b/zone/zonedb.h index f28ac5ec4..d5c02c693 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -47,6 +47,7 @@ struct wplist { float z; int pause; float heading; + bool centerpoint; }; #pragma pack(1) @@ -434,6 +435,7 @@ public: void AssignGrid(Client *client, int grid, int spawn2id); int GetHighestGrid(uint32 zoneid); int GetHighestWaypoint(uint32 zoneid, uint32 gridid); + int GetRandomWaypointLocFromGrid(glm::vec4 &loc, uint16 zoneid, int grid); /* NPCs */ From ff897dc90ab302346a2ca6a50bf83242ca7ea8e4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 24 Jan 2020 20:36:33 -0500 Subject: [PATCH 126/157] Update CURRENT_BINARY_DATABASE_VERSION --- common/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/version.h b/common/version.h index 0f659d57b..5d00daaf4 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 9146 +#define CURRENT_BINARY_DATABASE_VERSION 9147 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026 From 2f49266d086e80e8b76150d85c6847bd1fb90caf Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 25 Jan 2020 18:26:59 -0600 Subject: [PATCH 127/157] Fix bots db updates when bins are in bin folder [skip ci] --- utils/scripts/eqemu_server.pl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 76084f23f..2c769e811 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -2216,11 +2216,18 @@ sub get_bots_db_version { } 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 db_version`); + @db_version = split(': ', `$world_path db_version`); } if ($OS eq "Linux") { - @db_version = split(': ', `./world db_version`); + @db_version = split(': ', `./$world_path db_version`); } #::: Main Binary Database version From 6514ccc41c80edf6ce3dd93156ac4169553018f3 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 25 Jan 2020 18:30:52 -0600 Subject: [PATCH 128/157] Bot updates are killing running regular updates afterwards [skip ci] --- utils/scripts/eqemu_server.pl | 3 --- 1 file changed, 3 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 2c769e811..40762a2bd 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -547,10 +547,7 @@ sub check_for_world_bootup_database_update { 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"; From c6ba29f2e5449920bd1c822efe7fb7267cd3d0ed Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 25 Jan 2020 18:41:15 -0600 Subject: [PATCH 129/157] Revert commit until further testing [skip ci] --- utils/scripts/eqemu_server.pl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 40762a2bd..2c769e811 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -547,7 +547,10 @@ sub check_for_world_bootup_database_update { 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"; From c82d08cf11adc85c0c3edc91852c98c6069bf92c Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 26 Jan 2020 16:31:15 -0600 Subject: [PATCH 130/157] Make sure character soft deletes do not reserve name once deleted, add optional retro script to run for servers who had soft deletes running prior to this commit --- common/database.cpp | 1 + .../git/optional/2020_01_26_soft_delete_retro.sql | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 utils/sql/git/optional/2020_01_26_soft_delete_retro.sql diff --git a/common/database.cpp b/common/database.cpp index f6adc3659..ac5ae16bf 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -371,6 +371,7 @@ bool Database::DeleteCharacter(char *character_name) { UPDATE character_data SET + name = SUBSTRING(CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), 1, 64), deleted_at = NOW() WHERE id = '{}' diff --git a/utils/sql/git/optional/2020_01_26_soft_delete_retro.sql b/utils/sql/git/optional/2020_01_26_soft_delete_retro.sql new file mode 100644 index 000000000..d3d0c7c4b --- /dev/null +++ b/utils/sql/git/optional/2020_01_26_soft_delete_retro.sql @@ -0,0 +1,12 @@ +-- Run this to un-reserve deleted characters +UPDATE + character_data +SET + name = SUBSTRING( + CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), + 1, + 64 + ) +WHERE + deleted_at IS NOT NULL + AND name NOT LIKE '%-deleted-%'; \ No newline at end of file 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 131/157] 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 43da07fb55231eb520113a5745c36aab91e5d2bf Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Tue, 28 Jan 2020 19:04:47 -0500 Subject: [PATCH 132/157] Fix zone crash when a group member raid invites own group leader --- zone/client_packet.cpp | 5 +++++ zone/string_ids.h | 1 + 2 files changed, 6 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ebc6bd38a..dfb7ab673 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11130,6 +11130,11 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) break; } + if (player_to_invite_group && player_to_invite_group->IsGroupMember(this)) { + MessageString(Chat::Red, ALREADY_IN_PARTY); + break; + } + if (player_to_invite_group && !player_to_invite_group->IsLeader(player_to_invite)) { Message(Chat::Red, "You can only invite an ungrouped player or group leader to join your raid."); break; diff --git a/zone/string_ids.h b/zone/string_ids.h index 0948d13a8..25321b3c9 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -417,6 +417,7 @@ #define TARGET_PLAYER_FOR_GUILD_STATUS 12260 #define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite to invite someone to your group. #define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself. +#define ALREADY_IN_PARTY 12272 //That person is already in your party. #define NO_LONGER_HIDDEN 12337 //You are no longer hidden. #define STOP_SNEAKING 12338 //You stop sneaking #define NOT_IN_CONTROL 12368 //You do not have control of yourself right now. 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 133/157] 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 92d32feb0dd98de4a8460292b9ff84ab11697382 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 28 Jan 2020 21:24:14 -0500 Subject: [PATCH 134/157] Fix for player hp updates not matching between client and server --- zone/client_mods.cpp | 4 ++++ zone/mob.cpp | 15 ++++++++++++++- zone/mob.h | 1 + zone/spell_effects.cpp | 3 +++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 2d61a59ec..255201090 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -336,6 +336,10 @@ int32 Client::CalcMaxHP() current_hp = curHP_cap; } } + + // hack fix for client health not reflecting server value + last_max_hp = 0; + return max_hp; } diff --git a/zone/mob.cpp b/zone/mob.cpp index 71e876938..a6de82eee 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -186,6 +186,7 @@ Mob::Mob( last_hp_percent = 0; last_hp = 0; + last_max_hp = 0; current_speed = base_runspeed; @@ -1334,6 +1335,16 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal * If our HP is different from last HP update call - let's update selves */ if (IsClient()) { + + // delay to allow the client to catch up on buff states + if (max_hp != last_max_hp) { + + last_max_hp = max_hp; + CastToClient()->hp_self_update_throttle_timer.Trigger(); + + return; + } + if (current_hp != last_hp || force_update_all) { /** @@ -1341,10 +1352,12 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal */ if (this->CastToClient()->hp_self_update_throttle_timer.Check() || force_update_all) { Log(Logs::General, Logs::HPUpdate, - "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i last: %i skip_self: %s", + "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i/%i last: %i/%i skip_self: %s", this->GetCleanName(), current_hp, + max_hp, last_hp, + last_max_hp, (skip_self ? "true" : "false") ); diff --git a/zone/mob.h b/zone/mob.h index d48f47e6d..7c095a001 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1526,6 +1526,7 @@ protected: int8 last_hp_percent; int32 last_hp; + int32 last_max_hp; int cur_wp; glm::vec4 m_CurrentWayPoint; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 693222e9f..63a4a113f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -286,6 +286,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove dmg = -dmg; } + // hack fix for client health not reflecting server value + last_hp = 0; + //do any AAs apply to these spells? if(dmg < 0) { if (!PassCastRestriction(false, spells[spell_id].base2[i], true)) 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 135/157] 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 712366293d661c3d37452f7cfb4ac80cfe84ce86 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 30 Jan 2020 11:10:57 -0500 Subject: [PATCH 136/157] Further refine monk weight checks for floating point --- zone/attack.cpp | 51 ++++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 911f22dfb..da8dd6e3d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -659,85 +659,88 @@ int Mob::GetClassRaceACBonus() auto level = GetLevel(); if (GetClass() == MONK) { int hardcap = 30; - int softcap = 14; + double softcap = 14.9; if (level > 99) { hardcap = 58; - softcap = 35; + softcap = 35.9; } else if (level > 94) { hardcap = 57; - softcap = 34; + softcap = 34.9; } else if (level > 89) { hardcap = 56; - softcap = 33; + softcap = 33.9; } else if (level > 84) { hardcap = 55; - softcap = 32; + softcap = 32.9; } else if (level > 79) { hardcap = 54; - softcap = 31; + softcap = 31.9; } else if (level > 74) { hardcap = 53; - softcap = 30; + softcap = 30.9; } else if (level > 69) { hardcap = 53; - softcap = 28; + softcap = 28.9; } else if (level > 64) { hardcap = 53; - softcap = 26; + softcap = 26.9; } else if (level > 63) { hardcap = 50; - softcap = 24; + softcap = 24.9; } else if (level > 61) { hardcap = 47; - softcap = 24; + softcap = 24.9; } else if (level > 59) { hardcap = 45; - softcap = 24; + softcap = 24.9; } else if (level > 54) { hardcap = 40; - softcap = 20; + softcap = 20.9; } else if (level > 50) { hardcap = 38; - softcap = 18; + softcap = 18.9; } else if (level > 44) { hardcap = 36; - softcap = 17; + softcap = 17.9; } else if (level > 29) { hardcap = 34; - softcap = 16; + softcap = 16.9; } else if (level > 14) { hardcap = 32; - softcap = 15; + softcap = 15.9; } - int weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10 : 0; + + double weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10.0 : 0; + if (weight < hardcap - 1) { - int temp = level + 5; + double temp = level + 5; if (weight > softcap) { double redux = (weight - softcap) * 6.66667; - redux = (100.0 - std::min(100.0, redux)) * 0.01; - temp = std::max(0, static_cast(temp * redux)); + redux = (100.0 - redux) * 0.01; + temp = temp * redux; } - ac_bonus = (4 * temp) / 3; + ac_bonus = (4.0 * temp) / 3; + //LogError("weight[{}] temp[{}] ac_bonus[{}]", weight, temp, ac_bonus); } else if (weight > hardcap + 1) { - int temp = level + 5; + double temp = level + 5; double multiplier = std::min(1.0, (weight - (hardcap - 10.0)) / 100.0); - temp = (4 * temp) / 3; + temp = (4.0 * temp) / 3; ac_bonus -= static_cast(temp * multiplier); } } From f968d0df4c9a0493ceb5535d31d764a74d2cfa7d Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 30 Jan 2020 15:04:06 -0500 Subject: [PATCH 137/157] Reduce changes to those suggested by @mackal --- zone/attack.cpp | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index da8dd6e3d..724a8575a 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -655,92 +655,92 @@ double Mob::GetSoftcapReturns() int Mob::GetClassRaceACBonus() { + int ac_bonus = 0; + auto level = GetLevel(); + if (GetClass() == MONK) { int ac_bonus = 0; auto level = GetLevel(); if (GetClass() == MONK) { int hardcap = 30; - double softcap = 14.9; + int softcap = 14; if (level > 99) { hardcap = 58; - softcap = 35.9; + softcap = 35; } else if (level > 94) { hardcap = 57; - softcap = 34.9; + softcap = 34; } else if (level > 89) { hardcap = 56; - softcap = 33.9; + softcap = 33; } else if (level > 84) { hardcap = 55; - softcap = 32.9; + softcap = 32; } else if (level > 79) { hardcap = 54; - softcap = 31.9; + softcap = 31; } else if (level > 74) { hardcap = 53; - softcap = 30.9; + softcap = 30; } else if (level > 69) { hardcap = 53; - softcap = 28.9; + softcap = 28; } else if (level > 64) { hardcap = 53; - softcap = 26.9; + softcap = 26; } else if (level > 63) { hardcap = 50; - softcap = 24.9; + softcap = 24; } else if (level > 61) { hardcap = 47; - softcap = 24.9; + softcap = 24; } else if (level > 59) { hardcap = 45; - softcap = 24.9; + softcap = 24; } else if (level > 54) { hardcap = 40; - softcap = 20.9; + softcap = 20; } else if (level > 50) { hardcap = 38; - softcap = 18.9; + softcap = 18; } else if (level > 44) { hardcap = 36; - softcap = 17.9; + softcap = 17; } else if (level > 29) { hardcap = 34; - softcap = 16.9; + softcap = 16; } else if (level > 14) { hardcap = 32; - softcap = 15.9; + softcap = 15; } - - double weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10.0 : 0; - + double weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10.0:0; if (weight < hardcap - 1) { double temp = level + 5; if (weight > softcap) { - double redux = (weight - softcap) * 6.66667; - redux = (100.0 - redux) * 0.01; - temp = temp * redux; + double redux = static_cast(weight - softcap) * 6.66667; + redux = (100.0 - std::min(100.0, redux)) * 0.01; + temp = std::max(0.0, temp * redux); } - ac_bonus = (4.0 * temp) / 3; - //LogError("weight[{}] temp[{}] ac_bonus[{}]", weight, temp, ac_bonus); + ac_bonus = static_cast((4.0 * temp) / 3.0); } else if (weight > hardcap + 1) { double temp = level + 5; - double multiplier = std::min(1.0, (weight - (hardcap - 10.0)) / 100.0); - temp = (4.0 * temp) / 3; + double multiplier = std::min(1.0, (weight - (static_cast(hardcap) - 10.0)) / 100.0); + temp = (4.0 * temp) / 3.0; ac_bonus -= static_cast(temp * multiplier); } } From bcb08f99f089329f768f0f60eb5ad477ca10ef3d Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 30 Jan 2020 15:05:41 -0500 Subject: [PATCH 138/157] Update attack.cpp --- zone/attack.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 724a8575a..f532fc04a 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -655,9 +655,6 @@ double Mob::GetSoftcapReturns() int Mob::GetClassRaceACBonus() { - int ac_bonus = 0; - auto level = GetLevel(); - if (GetClass() == MONK) { int ac_bonus = 0; auto level = GetLevel(); if (GetClass() == MONK) { From 83ad9c86dbed0545de661774baf03dd973e57f3f Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 30 Jan 2020 15:19:02 -0500 Subject: [PATCH 139/157] 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 f532fc04a..308592041 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -724,7 +724,7 @@ int Mob::GetClassRaceACBonus() hardcap = 32; softcap = 15; } - double weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10.0:0; + int weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10 : 0; if (weight < hardcap - 1) { double temp = level + 5; if (weight > softcap) { 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 140/157] 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 50a39057e4bfd36141d69aa16539b60bdbfa1dca Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 1 Feb 2020 19:27:15 -0500 Subject: [PATCH 141/157] Update QuestReward_Struct --- common/eq_packet_structs.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 53d7021ba..3fe2430db 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -35,6 +35,7 @@ static const uint32 MAX_MERC = 100; 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; /* @@ -2180,14 +2181,7 @@ struct QuestReward_Struct /*024*/ uint32 silver; // Gives silver to the client /*028*/ uint32 gold; // Gives gold to the client /*032*/ uint32 platinum; // Gives platinum to the client - /*036*/ uint32 item_id; - /*040*/ uint32 unknown040; - /*044*/ uint32 unknown044; - /*048*/ uint32 unknown048; - /*052*/ uint32 unknown052; - /*056*/ uint32 unknown056; - /*060*/ uint32 unknown060; - /*064*/ uint32 unknown064; + /*036*/ int32 item_id[QUESTREWARD_COUNT]; // -1 for nothing /*068*/ }; From b02e87cce72a9a413de3213aeaa91883e2ce26bc Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 1 Feb 2020 19:54:26 -0500 Subject: [PATCH 142/157] Fix Client::QuestReward for struct adjustment --- zone/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index 9ea27869a..eb157dc2f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8539,7 +8539,7 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, qr->silver = silver; qr->gold = gold; qr->platinum = platinum; - qr->item_id = itemid; + qr->item_id[0] = itemid; qr->exp_reward = exp; if (copper > 0 || silver > 0 || gold > 0 || platinum > 0) From ab3d65b2ea344fed0f6bf1fdcc5b92ce0ec2989c Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sat, 1 Feb 2020 20:29:48 -0500 Subject: [PATCH 143/157] Fix FixZ regarding the use of model in npc_types This fix only impacts those that use the model field in npc_types to override race on the client. GetModel() returns model if set, otherwise race. As a refresher, the model field is there so the server can still see a mob as its base race for things like bane, while the client can use a new model. FixZ needs to know about this. --- zone/waypoints.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 0bd18edbf..65f4fd099 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -771,7 +771,7 @@ void Mob::FixZ(int32 z_find_offset /*= 5*/, bool fix_client_z /*= false*/) { float Mob::GetZOffset() const { float offset = 3.125f; - switch (race) { + switch (GetModel()) { case RACE_BASILISK_436: offset = 0.577f; break; From 59903313e4c46ff8b7cae6b8c7eb7b6d46a35f82 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 1 Feb 2020 22:20:19 -0500 Subject: [PATCH 144/157] Expand Lua's Client QuestReward function You can now use it to summon up to 8 items ex: `e.other:QuestReward(e.self, {items = {28745, 28092}, exp = 250})` This expands the version that takes a table. The new item is a table (in the main table) called items, which needs to be auto keyed like the example above. If you also provide the old itemid key, it will be ignored if the items is there. --- zone/client.cpp | 36 ++++++++++++++++++++++++++++++ zone/client.h | 1 + zone/lua_client.cpp | 54 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 78 insertions(+), 13 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index eb157dc2f..acbb7cc74 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8566,6 +8566,42 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, safe_delete(outapp); } +void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction) +{ + auto outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct)); + memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct)); + QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer; + + memcpy(qr, &reward, sizeof(QuestReward_Struct)); + + // not set in caller because reasons + qr->mob_id = target->GetID(); // Entity ID for the from mob name + + if (reward.copper > 0 || reward.silver > 0 || reward.gold > 0 || reward.platinum > 0) + AddMoneyToPP(reward.copper, reward.silver, reward.gold, reward.platinum, false); + + for (int i = 0; i < QUESTREWARD_COUNT; ++i) + if (reward.item_id[i] > 0) + SummonItem(reward.item_id[i], 0, 0, 0, 0, 0, 0, false, EQEmu::invslot::slotCursor); + + if (faction) + { + if (target->IsNPC()) + { + int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); + SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); + qr->faction = target->CastToNPC()->GetPrimaryFaction(); + qr->faction_mod = 1; // Too lazy to get real value, not sure if this is even used by client anyhow. + } + } + + if (reward.exp_reward> 0) + AddEXP(reward.exp_reward); + + QueuePacket(outapp, true, Client::CLIENT_CONNECTED); + safe_delete(outapp); +} + void Client::SendHPUpdateMarquee(){ if (!this || !this->IsClient() || !this->current_hp || !this->max_hp) return; diff --git a/zone/client.h b/zone/client.h index 4ff431520..300073bb5 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1282,6 +1282,7 @@ public: int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false); void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false); + void QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction); // TODO: Fix faction processing void ResetHPUpdateTimer() { hpupdate_timer.Start(); } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 14c009b12..9d0873360 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1385,18 +1385,23 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { return; } - uint32 copper = 0; - uint32 silver = 0; - uint32 gold = 0; - uint32 platinum = 0; - uint32 itemid = 0; - uint32 exp = 0; + QuestReward_Struct quest_reward; + quest_reward.mob_id = 0; + quest_reward.target_id = self->GetID(); + quest_reward.copper = 0; + quest_reward.silver = 0; + quest_reward.gold = 0; + quest_reward.platinum = 0; + quest_reward.exp_reward = 0; + quest_reward.faction = 0; + quest_reward.faction_mod = 0; bool faction = false; + std::fill(std::begin(quest_reward.item_id), std::end(quest_reward.item_id), -1); auto cur = reward["copper"]; if (luabind::type(cur) != LUA_TNIL) { try { - copper = luabind::object_cast(cur); + quest_reward.copper = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1404,7 +1409,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["silver"]; if (luabind::type(cur) != LUA_TNIL) { try { - silver = luabind::object_cast(cur); + quest_reward.silver = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1412,7 +1417,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["gold"]; if (luabind::type(cur) != LUA_TNIL) { try { - gold = luabind::object_cast(cur); + quest_reward.gold = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1420,7 +1425,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["platinum"]; if (luabind::type(cur) != LUA_TNIL) { try { - platinum = luabind::object_cast(cur); + quest_reward.platinum = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1428,7 +1433,30 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["itemid"]; if (luabind::type(cur) != LUA_TNIL) { try { - itemid = luabind::object_cast(cur); + quest_reward.item_id[0] = luabind::object_cast(cur); + } catch (luabind::cast_failed &) { + } + } + + // if you define both an itemid and items table, the itemid is thrown away + // should we error? + cur = reward["items"]; + if (luabind::type(cur) == LUA_TTABLE) { + try { + // assume they defined a compatible table + for (int i = 1; i <= QUESTREWARD_COUNT; ++i) { + auto item = cur[i]; + int cur_value = -1; + if (luabind::type(item) != LUA_TNIL) { + try { + cur_value = luabind::object_cast(item); + } catch (luabind::cast_failed &) { + } + } else { + break; + } + quest_reward.item_id[i - 1] = cur_value; + } } catch (luabind::cast_failed &) { } } @@ -1436,7 +1464,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["exp"]; if (luabind::type(cur) != LUA_TNIL) { try { - exp = luabind::object_cast(cur); + quest_reward.exp_reward = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1449,7 +1477,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { } } - self->QuestReward(target, copper, silver, gold, platinum, itemid, exp, faction); + self->QuestReward(target, quest_reward, faction); } bool Lua_Client::IsDead() { From c2300d514cecc32547679b4fdcebac5d26d3d662 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 1 Feb 2020 20:49:04 -0800 Subject: [PATCH 145/157] 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 146/157] 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 424b669cbbe574764d121d65f97722e01a92c906 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 2 Feb 2020 16:39:46 -0500 Subject: [PATCH 147/157] Target not required for newer clients in QuestReward --- zone/client.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index acbb7cc74..6b366afba 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8533,7 +8533,7 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct)); QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer; - qr->mob_id = target->GetID(); // Entity ID for the from mob name + qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name qr->target_id = GetID(); // The Client ID (this) qr->copper = copper; qr->silver = silver; @@ -8550,7 +8550,7 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, if (faction) { - if (target->IsNPC()) + if (target && target->IsNPC()) { int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); @@ -8575,7 +8575,7 @@ void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool fac memcpy(qr, &reward, sizeof(QuestReward_Struct)); // not set in caller because reasons - qr->mob_id = target->GetID(); // Entity ID for the from mob name + qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name if (reward.copper > 0 || reward.silver > 0 || reward.gold > 0 || reward.platinum > 0) AddMoneyToPP(reward.copper, reward.silver, reward.gold, reward.platinum, false); @@ -8586,7 +8586,7 @@ void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool fac if (faction) { - if (target->IsNPC()) + if (target && target->IsNPC()) { int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); 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 148/157] 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 149/157] 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 5fefdfcc170a9f42beee3381c7bb000e4e9f36a0 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 3 Feb 2020 12:50:05 -0500 Subject: [PATCH 150/157] 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 151/157] 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 152/157] 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 153/157] 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 154/157] 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 861b879a948f281027c1b259c96982f446c19824 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 5 Feb 2020 01:34:29 -0600 Subject: [PATCH 155/157] 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 156/157] 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 157/157] 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);