From adde43e965c41c13e8d74c75ca885a263a4a864f Mon Sep 17 00:00:00 2001 From: Giacomo Pozzoni Date: Mon, 27 Dec 2021 13:33:37 +0100 Subject: [PATCH] Core/AI: Fix crashes caused by charmed Creatures having null AI for 1 map update tick (#27434) Implement using ScheduledChangeAI instead of nullptr to signal a required AI change # Conflicts: # src/server/game/Entities/Unit/Unit.cpp # src/server/game/Entities/Unit/Unit.h --- .../game/AI/CoreAI/ScheduledChangeAI.cpp | 21 ++++++++++ src/server/game/AI/CoreAI/ScheduledChangeAI.h | 40 +++++++++++++++++++ src/server/game/AI/CreatureAIRegistry.cpp | 2 + src/server/game/Entities/Unit/Unit.cpp | 25 ++++++++++-- src/server/game/Entities/Unit/Unit.h | 2 + 5 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/server/game/AI/CoreAI/ScheduledChangeAI.cpp create mode 100644 src/server/game/AI/CoreAI/ScheduledChangeAI.h diff --git a/src/server/game/AI/CoreAI/ScheduledChangeAI.cpp b/src/server/game/AI/CoreAI/ScheduledChangeAI.cpp new file mode 100644 index 0000000000..c4f513fcfa --- /dev/null +++ b/src/server/game/AI/CoreAI/ScheduledChangeAI.cpp @@ -0,0 +1,21 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; 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, see . + */ + +#include "ScheduledChangeAI.h" + +ScheduledChangeAI::ScheduledChangeAI(Creature* creature): CreatureAI(creature) +{ } diff --git a/src/server/game/AI/CoreAI/ScheduledChangeAI.h b/src/server/game/AI/CoreAI/ScheduledChangeAI.h new file mode 100644 index 0000000000..79b8344eab --- /dev/null +++ b/src/server/game/AI/CoreAI/ScheduledChangeAI.h @@ -0,0 +1,40 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; 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, see . + */ + +#ifndef TRINITY_SCHEDULEDCHANGEAI_H +#define TRINITY_SCHEDULEDCHANGEAI_H + +#include "CreatureAI.h" + +class TC_GAME_API ScheduledChangeAI final : public CreatureAI +{ + public: + explicit ScheduledChangeAI(Creature* creature); + + void MoveInLineOfSight(Unit*) override { } + void AttackStart(Unit*) override { } + void JustStartedThreateningMe(Unit*) override { } + void JustEnteredCombat(Unit*) override { } + void UpdateAI(uint32) override { } + void JustAppeared() override { } + void EnterEvadeMode(EvadeReason /*why*/) override { } + void OnCharmed(bool /*isNew*/) override { } + + static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; } +}; + +#endif diff --git a/src/server/game/AI/CreatureAIRegistry.cpp b/src/server/game/AI/CreatureAIRegistry.cpp index daf98803a9..e1987b6ced 100644 --- a/src/server/game/AI/CreatureAIRegistry.cpp +++ b/src/server/game/AI/CreatureAIRegistry.cpp @@ -23,6 +23,7 @@ #include "PassiveAI.h" #include "PetAI.h" #include "ReactorAI.h" +#include "ScheduledChangeAI.h" #include "SmartAI.h" #include "TotemAI.h" @@ -49,6 +50,7 @@ namespace AIRegistry (new CreatureAIFactory("TurretAI"))->RegisterSelf(); (new CreatureAIFactory("VehicleAI"))->RegisterSelf(); (new CreatureAIFactory("SmartAI"))->RegisterSelf(); + (new CreatureAIFactory("ScheduledChangeAI"))->RegisterSelf(); (new GameObjectAIFactory("NullGameObjectAI"))->RegisterSelf(); (new GameObjectAIFactory("GameObjectAI"))->RegisterSelf(); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index bc5fac38e1..1274430e19 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -65,6 +65,7 @@ #include "PlayerAI.h" #include "QuestDef.h" #include "ReputationMgr.h" +#include "ScheduledChangeAI.h" #include "Spell.h" #include "SpellAuraEffects.h" #include "SpellAuras.h" @@ -543,7 +544,7 @@ void Unit::Update(uint32 p_time) _healthRegenerationTimer += UNIT_HEALTH_REGENERATION_INTERVAL; } - if (!GetAI() && (GetTypeId() != TYPEID_PLAYER || IsCharmed())) + if (HasScheduledAIChange() && (GetTypeId() != TYPEID_PLAYER || (IsCharmed() && GetCharmerGUID().IsCreature()))) UpdateCharmAI(); RefreshAI(); } @@ -10354,11 +10355,11 @@ void Unit::ScheduleAIChange() bool const charmed = IsCharmed(); if (charmed) - PushAI(nullptr); + PushAI(GetScheduledChangeAI()); else { RestoreDisabledAI(); - PushAI(nullptr); //This could actually be PopAI() to get the previous AI but it's required atm to trigger UpdateCharmAI() + PushAI(GetScheduledChangeAI()); //This could actually be PopAI() to get the previous AI but it's required atm to trigger UpdateCharmAI() } } @@ -10366,10 +10367,26 @@ void Unit::RestoreDisabledAI() { // Keep popping the stack until we either reach the bottom or find a valid AI while (PopAI()) - if (GetTopAI()) + if (GetTopAI() && dynamic_cast(GetTopAI()) == nullptr) return; } +UnitAI* Unit::GetScheduledChangeAI() +{ + if (Creature* creature = ToCreature()) + return new ScheduledChangeAI(creature); + else + return nullptr; +} + +bool Unit::HasScheduledAIChange() const +{ + if (UnitAI* ai = GetAI()) + return dynamic_cast(ai) != nullptr; + else + return true; +} + void Unit::AddToWorld() { WorldObject::AddToWorld(); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index ebe7a5e81b..67f215a3d1 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -840,6 +840,8 @@ class TC_GAME_API Unit : public WorldObject void SetAI(UnitAI* newAI); UnitAI* GetTopAI() const { return i_AIs.empty() ? nullptr : i_AIs.top().get(); } void RefreshAI(); + UnitAI* GetScheduledChangeAI(); + bool HasScheduledAIChange() const; public: void AddToWorld() override;