aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Phasing
diff options
context:
space:
mode:
authorMatan Shukry <matanshukry@gmail.com>2021-12-28 14:24:10 +0200
committerGitHub <noreply@github.com>2021-12-28 13:24:10 +0100
commit8fabe5a3aacf7797f03d074ab8434f445be64955 (patch)
treedd3c977290be47d5a708947893c3544678045194 /src/server/game/Phasing
parent1aad7f8ddd486e60f76730d3baa36ec63711c7e8 (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.cpp203
-rw-r--r--src/server/game/Phasing/PersonalPhaseTracker.h85
-rw-r--r--src/server/game/Phasing/PhaseShift.cpp8
-rw-r--r--src/server/game/Phasing/PhaseShift.h5
-rw-r--r--src/server/game/Phasing/PhasingHandler.cpp28
-rw-r--r--src/server/game/Phasing/PhasingHandler.h5
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);