diff options
| author | Matan Shukry <matanshukry@gmail.com> | 2021-12-28 14:24:10 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-12-28 13:24:10 +0100 |
| commit | 8fabe5a3aacf7797f03d074ab8434f445be64955 (patch) | |
| tree | dd3c977290be47d5a708947893c3544678045194 /src/server/game/Phasing | |
| parent | 1aad7f8ddd486e60f76730d3baa36ec63711c7e8 (diff) | |
Core/Phasing: Implemented db spawns in personal phases (#26345)
Co-authored-by: Shauren <shauren.trinity@gmail.com>
Diffstat (limited to 'src/server/game/Phasing')
| -rw-r--r-- | src/server/game/Phasing/PersonalPhaseTracker.cpp | 203 | ||||
| -rw-r--r-- | src/server/game/Phasing/PersonalPhaseTracker.h | 85 | ||||
| -rw-r--r-- | src/server/game/Phasing/PhaseShift.cpp | 8 | ||||
| -rw-r--r-- | src/server/game/Phasing/PhaseShift.h | 5 | ||||
| -rw-r--r-- | src/server/game/Phasing/PhasingHandler.cpp | 28 | ||||
| -rw-r--r-- | src/server/game/Phasing/PhasingHandler.h | 5 |
6 files changed, 331 insertions, 3 deletions
diff --git a/src/server/game/Phasing/PersonalPhaseTracker.cpp b/src/server/game/Phasing/PersonalPhaseTracker.cpp new file mode 100644 index 00000000000..db73485b352 --- /dev/null +++ b/src/server/game/Phasing/PersonalPhaseTracker.cpp @@ -0,0 +1,203 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "PersonalPhaseTracker.h" +#include "Containers.h" +#include "Log.h" +#include "Map.h" +#include "Object.h" +#include "ObjectGridLoader.h" +#include "ObjectMgr.h" +#include "PhasingHandler.h" + + /*********************************************************/ + /*** PlayerPersonalPhasesTracker ***/ + /*********************************************************/ + +void PlayerPersonalPhasesTracker::RegisterTrackedObject(uint32 phaseId, WorldObject* object) +{ + _spawns[phaseId].Objects.insert(object); +} + +void PlayerPersonalPhasesTracker::UnregisterTrackedObject(WorldObject* object) +{ + for (auto& [_, spawns] : _spawns) + spawns.Objects.erase(object); +} + +void PlayerPersonalPhasesTracker::OnOwnerPhasesChanged(WorldObject const* owner) +{ + PhaseShift const& phaseShift = owner->GetPhaseShift(); + + // Loop over all our tracked phases. If any don't exist - delete them + for (auto& [phaseId, spawns] : _spawns) + if (!spawns.DurationRemaining && !phaseShift.HasPhase(phaseId)) + spawns.DurationRemaining = PersonalPhaseSpawns::DELETE_TIME_DEFAULT; + + // loop over all owner phases. If any exist and marked for deletion - reset delete + for (PhaseShift::PhaseRef const& phaseRef : phaseShift.GetPhases()) + if (PersonalPhaseSpawns* spawns = Trinity::Containers::MapGetValuePtr(_spawns, phaseRef.Id)) + spawns->DurationRemaining.reset(); +} + +void PlayerPersonalPhasesTracker::MarkAllPhasesForDeletion() +{ + for (auto& [_, spawns] : _spawns) + spawns.DurationRemaining = PersonalPhaseSpawns::DELETE_TIME_DEFAULT; +} + +void PlayerPersonalPhasesTracker::Update(Map* map, uint32 diff) +{ + for (auto itr = _spawns.begin(); itr != _spawns.end(); ) + { + if (itr->second.DurationRemaining) + { + itr->second.DurationRemaining = *itr->second.DurationRemaining - Milliseconds(diff); + if (itr->second.DurationRemaining <= Milliseconds::zero()) + { + DespawnPhase(map, itr->second); + itr = _spawns.erase(itr); + continue; + } + } + ++itr; + } +} + +bool PlayerPersonalPhasesTracker::IsGridLoadedForPhase(uint32 gridId, uint32 phaseId) const +{ + if (PersonalPhaseSpawns const* spawns = Trinity::Containers::MapGetValuePtr(_spawns, phaseId)) + return spawns->Grids.find(gridId) != spawns->Grids.cend(); + + return false; +} + +void PlayerPersonalPhasesTracker::SetGridLoadedForPhase(uint32 gridId, uint32 phaseId) +{ + PersonalPhaseSpawns& group = _spawns[phaseId]; + group.Grids.insert(gridId); +} + +void PlayerPersonalPhasesTracker::SetGridUnloaded(uint32 gridId) +{ + for (auto itr = _spawns.begin(); itr != _spawns.end(); ) + { + itr->second.Grids.erase(gridId); + if (itr->second.IsEmpty()) + itr = _spawns.erase(itr); + else + ++itr; + } +} + +void PlayerPersonalPhasesTracker::DespawnPhase(Map* map, PersonalPhaseSpawns& spawns) +{ + for (WorldObject* obj : spawns.Objects) + map->AddObjectToRemoveList(obj); + + spawns.Objects.clear(); + spawns.Grids.clear(); +} + +/*********************************************************/ +/*** MultiPersonalPhaseTracker ***/ +/*********************************************************/ + +void MultiPersonalPhaseTracker::LoadGrid(PhaseShift const& phaseShift, NGridType& grid, Map* map, Cell const& cell) +{ + if (!phaseShift.HasPersonalPhase()) + return; + + PersonalPhaseGridLoader loader(grid, map, cell, phaseShift.GetPersonalGuid()); + PlayerPersonalPhasesTracker& playerTracker = _playerData[phaseShift.GetPersonalGuid()]; + + for (PhaseShift::PhaseRef const& phaseRef : phaseShift.GetPhases()) + { + if (!phaseRef.IsPersonal()) + continue; + + if (!sObjectMgr->HasPersonalSpawns(map->GetId(), map->GetDifficultyID(), phaseRef.Id)) + continue; + + if (playerTracker.IsGridLoadedForPhase(grid.GetGridId(), phaseRef.Id)) + continue; + + TC_LOG_DEBUG("maps", "Loading personal phase objects (phase %u) in grid[%u, %u] for map %u instance %u", + phaseRef.Id, cell.GridX(), cell.GridY(), map->GetId(), map->GetInstanceId()); + + loader.Load(phaseRef.Id); + + playerTracker.SetGridLoadedForPhase(grid.GetGridId(), phaseRef.Id); + } + + if (loader.GetLoadedGameObjects()) + map->Balance(); +} + +void MultiPersonalPhaseTracker::UnloadGrid(NGridType& grid) +{ + for (auto itr = _playerData.begin(); itr != _playerData.end(); ) + { + itr->second.SetGridUnloaded(grid.GetGridId()); + if (itr->second.IsEmpty()) + itr = _playerData.erase(itr); + else + ++itr; + } +} + +void MultiPersonalPhaseTracker::RegisterTrackedObject(uint32 phaseId, ObjectGuid const& phaseOwner, WorldObject* object) +{ + ASSERT(phaseId); + ASSERT(!phaseOwner.IsEmpty()); + ASSERT(object); + + _playerData[phaseOwner].RegisterTrackedObject(phaseId, object); +} + +void MultiPersonalPhaseTracker::UnregisterTrackedObject(WorldObject* object) +{ + if (PlayerPersonalPhasesTracker* playerTracker = Trinity::Containers::MapGetValuePtr(_playerData, object->GetPhaseShift().GetPersonalGuid())) + playerTracker->UnregisterTrackedObject(object); +} + +void MultiPersonalPhaseTracker::OnOwnerPhaseChanged(WorldObject const* phaseOwner, NGridType* grid, Map* map, Cell const& cell) +{ + if (PlayerPersonalPhasesTracker* playerTracker = Trinity::Containers::MapGetValuePtr(_playerData, phaseOwner->GetGUID())) + playerTracker->OnOwnerPhasesChanged(phaseOwner); + + if (grid) + LoadGrid(phaseOwner->GetPhaseShift(), *grid, map, cell); +} + +void MultiPersonalPhaseTracker::MarkAllPhasesForDeletion(ObjectGuid const& phaseOwner) +{ + if (PlayerPersonalPhasesTracker* playerTracker = Trinity::Containers::MapGetValuePtr(_playerData, phaseOwner)) + playerTracker->MarkAllPhasesForDeletion(); +} + +void MultiPersonalPhaseTracker::Update(Map* map, uint32 diff) +{ + for (auto itr = _playerData.begin(); itr != _playerData.end(); ) + { + itr->second.Update(map, diff); + if (itr->second.IsEmpty()) + itr = _playerData.erase(itr); + else + ++itr; + } +} diff --git a/src/server/game/Phasing/PersonalPhaseTracker.h b/src/server/game/Phasing/PersonalPhaseTracker.h new file mode 100644 index 00000000000..641cbece9fa --- /dev/null +++ b/src/server/game/Phasing/PersonalPhaseTracker.h @@ -0,0 +1,85 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef PersonalPhaseTracker_h__ +#define PersonalPhaseTracker_h__ + +#include "Common.h" +#include "Duration.h" +#include "GridDefines.h" +#include "ObjectGuid.h" +#include "Optional.h" + +class Map; +class PhaseShift; +class WorldObject; +struct Cell; + +struct PersonalPhaseSpawns +{ + static constexpr Milliseconds DELETE_TIME_DEFAULT = 1min; + + std::unordered_set<WorldObject*> Objects; + std::unordered_set<uint16> Grids; + Optional<Milliseconds> DurationRemaining; + + bool IsEmpty() const { return Objects.empty() && Grids.empty(); } +}; + +/* Tracks personal phases for a single owner */ +struct PlayerPersonalPhasesTracker +{ +public: + void RegisterTrackedObject(uint32 phaseId, WorldObject* object); + void UnregisterTrackedObject(WorldObject* object); + + void OnOwnerPhasesChanged(WorldObject const* owner); + void MarkAllPhasesForDeletion(); + + void Update(Map* map, uint32 diff); + + bool IsGridLoadedForPhase(uint32 gridId, uint32 phaseId) const; + void SetGridLoadedForPhase(uint32 gridId, uint32 phaseId); + void SetGridUnloaded(uint32 gridId); + + bool IsEmpty() const { return _spawns.empty(); } + +private: + void DespawnPhase(Map* map, PersonalPhaseSpawns& spawns); + + std::unordered_map<uint32 /*phaseId*/, PersonalPhaseSpawns> _spawns; +}; + +/* Handles personal phase trackers for all owners */ +struct MultiPersonalPhaseTracker +{ + void LoadGrid(PhaseShift const& phaseShift, NGridType& grid, Map* map, Cell const& cell); + void UnloadGrid(NGridType& grid); + + void RegisterTrackedObject(uint32 phaseId, ObjectGuid const& phaseOwner, WorldObject* object); + void UnregisterTrackedObject(WorldObject* object); + + void OnOwnerPhaseChanged(WorldObject const* phaseOwner, NGridType* grid, Map* map, Cell const& cell); + void MarkAllPhasesForDeletion(ObjectGuid const& phaseOwner); + + void Update(Map* map, uint32 diff); + +private: + std::unordered_map<ObjectGuid /*owner*/, PlayerPersonalPhasesTracker> _playerData; +}; + +#endif // PersonalPhaseTracker_h__ diff --git a/src/server/game/Phasing/PhaseShift.cpp b/src/server/game/Phasing/PhaseShift.cpp index f9753226918..f0655bf01fa 100644 --- a/src/server/game/Phasing/PhaseShift.cpp +++ b/src/server/game/Phasing/PhaseShift.cpp @@ -186,3 +186,11 @@ void PhaseShift::UpdatePersonalGuid() if (!PersonalReferences) PersonalGuid.Clear(); } + +bool PhaseShift::HasPersonalPhase() const +{ + for (PhaseRef const& phaseRef : GetPhases()) + if (phaseRef.IsPersonal()) + return true; + return false; +} diff --git a/src/server/game/Phasing/PhaseShift.h b/src/server/game/Phasing/PhaseShift.h index 133f58056a2..32fc4455877 100644 --- a/src/server/game/Phasing/PhaseShift.h +++ b/src/server/game/Phasing/PhaseShift.h @@ -65,6 +65,7 @@ public: std::vector<Condition*> const* AreaConditions; bool operator<(PhaseRef const& right) const { return Id < right.Id; } bool operator==(PhaseRef const& right) const { return Id == right.Id; } + bool IsPersonal() const { return Flags.HasFlag(PhaseFlags::Personal); } }; struct VisibleMapIdRef { @@ -85,6 +86,8 @@ public: using VisibleMapIdContainer = std::map<uint32, VisibleMapIdRef>; using UiMapPhaseIdContainer = std::map<uint32, UiMapPhaseIdRef>; + ObjectGuid GetPersonalGuid() const { return PersonalGuid; } + bool AddPhase(uint32 phaseId, PhaseFlags flags, std::vector<Condition*> const* areaConditions, int32 references = 1); EraseResult<PhaseContainer> RemovePhase(uint32 phaseId); bool HasPhase(uint32 phaseId) const { return Phases.find(PhaseRef(phaseId, PhaseFlags::None, nullptr)) != Phases.end(); } @@ -105,6 +108,8 @@ public: bool CanSee(PhaseShift const& other) const; + bool HasPersonalPhase() const; + protected: friend class PhasingHandler; diff --git a/src/server/game/Phasing/PhasingHandler.cpp b/src/server/game/Phasing/PhasingHandler.cpp index e1432b49351..31a28010c71 100644 --- a/src/server/game/Phasing/PhasingHandler.cpp +++ b/src/server/game/Phasing/PhasingHandler.cpp @@ -496,6 +496,13 @@ void PhasingHandler::InitDbPhaseShift(PhaseShift& phaseShift, uint8 phaseUseFlag phaseShift.Flags = flags; } +void PhasingHandler::InitDbPersonalOwnership(PhaseShift& phaseShift, ObjectGuid const& personalGuid) +{ + ASSERT(phaseShift.IsDbPhaseShift); + ASSERT(phaseShift.HasPersonalPhase()); + phaseShift.PersonalGuid = personalGuid; +} + void PhasingHandler::InitDbVisibleMapId(PhaseShift& phaseShift, int32 visibleMapId) { phaseShift.VisibleMapIds.clear(); @@ -551,9 +558,18 @@ void PhasingHandler::SetInversed(WorldObject* object, bool apply, bool updateVis UpdateVisibilityIfNeeded(object, updateVisibility, true); } -void PhasingHandler::PrintToChat(ChatHandler* chat, PhaseShift const& phaseShift) +void PhasingHandler::PrintToChat(ChatHandler* chat, WorldObject const* target) { - chat->PSendSysMessage(LANG_PHASESHIFT_STATUS, phaseShift.Flags.AsUnderlyingType(), phaseShift.PersonalGuid.ToString().c_str()); + PhaseShift const& phaseShift = target->GetPhaseShift(); + + std::string phaseOwnerName = "N/A"; + if (phaseShift.HasPersonalPhase()) + if (WorldObject* personalGuid = ObjectAccessor::GetWorldObject(*target, phaseShift.PersonalGuid)) + phaseOwnerName = personalGuid->GetName(); + + chat->PSendSysMessage(LANG_PHASESHIFT_STATUS, phaseShift.Flags.AsUnderlyingType(), + phaseShift.PersonalGuid.ToString().c_str(), phaseOwnerName.c_str()); + if (!phaseShift.Phases.empty()) { std::ostringstream phases; @@ -601,6 +617,14 @@ std::string PhasingHandler::FormatPhases(PhaseShift const& phaseShift) return phases.str(); } +bool PhasingHandler::IsPersonalPhase(uint32 phaseId) +{ + if (PhaseEntry const* phase = sPhaseStore.LookupEntry(phaseId)) + return phase->GetFlags().HasFlag(PhaseEntryFlags::Personal); + + return false; +} + void PhasingHandler::UpdateVisibilityIfNeeded(WorldObject* object, bool updateVisibility, bool changed) { if (changed && object->IsInWorld()) diff --git a/src/server/game/Phasing/PhasingHandler.h b/src/server/game/Phasing/PhasingHandler.h index 73003029cfc..17d40977e56 100644 --- a/src/server/game/Phasing/PhasingHandler.h +++ b/src/server/game/Phasing/PhasingHandler.h @@ -59,6 +59,7 @@ public: static PhaseShift const& GetEmptyPhaseShift(); static void InitDbPhaseShift(PhaseShift& phaseShift, uint8 phaseUseFlags, uint16 phaseId, uint32 phaseGroupId); + static void InitDbPersonalOwnership(PhaseShift& phaseShift, ObjectGuid const& personalGuid); static void InitDbVisibleMapId(PhaseShift& phaseShift, int32 visibleMapId); static bool InDbPhaseShift(WorldObject const* object, uint8 phaseUseFlags, uint16 phaseId, uint32 phaseGroupId); @@ -67,9 +68,11 @@ public: static void SetAlwaysVisible(WorldObject* object, bool apply, bool updateVisibility); static void SetInversed(WorldObject* object, bool apply, bool updateVisibility); - static void PrintToChat(ChatHandler* chat, PhaseShift const& phaseShift); + static void PrintToChat(ChatHandler* chat, WorldObject const* target); static std::string FormatPhases(PhaseShift const& phaseShift); + static bool IsPersonalPhase(uint32 phaseId); + private: static void AddPhase(WorldObject* object, uint32 phaseId, ObjectGuid const& personalGuid, bool updateVisibility); static void AddPhaseGroup(WorldObject* object, uint32 phaseGroupId, ObjectGuid const& personalGuid, bool updateVisibility); |
