feat(Core/Grids): Implement visibility notifier (#15919)

* Cherry-picked from TrinityCore (unable to find author)
This commit is contained in:
AG
2023-09-28 22:28:28 +02:00
committed by GitHub
parent 29af6cc886
commit 2779833768
33 changed files with 1254 additions and 510 deletions

View File

@@ -96,6 +96,12 @@ public:
visitor.Visit(i_objects);
}
template<class T>
uint32 GetWorldObjectCountInGrid() const
{
return i_objects.template Count<T>();
}
/** Inserts a container type object into the grid.
*/
template<class SPECIFIC_OBJECT> void AddGridObject(SPECIFIC_OBJECT* obj)

View File

@@ -0,0 +1,65 @@
/*
* This file is part of the AzerothCore 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 Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 "GridStates.h"
#include "GridNotifiers.h"
#include "Log.h"
#include "Map.h"
#include "ObjectGridLoader.h"
void InvalidState::Update(Map&, NGridType&, GridInfo&, uint32) const
{
}
void ActiveState::Update(Map& map, NGridType& grid, GridInfo& info, uint32 diff) const
{
// Only check grid activity every (grid_expiry/10) ms, because it's really useless to do it every cycle
info.UpdateTimeTracker(diff);
if (info.getTimeTracker().Passed())
{
if (!grid.GetWorldObjectCountInNGrid<Player>() && !map.ActiveObjectsNearGrid(grid))
{
ObjectGridStoper worker;
TypeContainerVisitor<ObjectGridStoper, GridTypeMapContainer> visitor(worker);
grid.VisitAllGrids(visitor);
grid.SetGridState(GRID_STATE_IDLE);
LOG_DEBUG("maps", "Grid[{}, {}] on map {} moved to IDLE state", grid.getX(), grid.getY(), map.GetId());
}
else
map.ResetGridExpiry(grid, 0.1f);
}
}
void IdleState::Update(Map& map, NGridType& grid, GridInfo&, uint32) const
{
map.ResetGridExpiry(grid);
grid.SetGridState(GRID_STATE_REMOVAL);
LOG_DEBUG("maps", "Grid[{}, {}] on map {} moved to REMOVAL state", grid.getX(), grid.getY(), map.GetId());
}
void RemovalState::Update(Map& map, NGridType& grid, GridInfo& info, uint32 diff) const
{
if (!info.getUnloadLock())
{
info.UpdateTimeTracker(diff);
if (info.getTimeTracker().Passed() && !map.UnloadGrid(grid, false))
{
LOG_DEBUG("maps", "Grid[{}, {}] for map {} differed unloading due to players or active objects nearby", grid.getX(), grid.getY(), map.GetId());
map.ResetGridExpiry(grid);
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* This file is part of the AzerothCore 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 Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 ACORE_GRIDSTATES_H
#define ACORE_GRIDSTATES_H
#include "GridDefines.h"
#include "NGrid.h"
class Map;
class AC_GAME_API GridState
{
public:
virtual ~GridState() { };
virtual void Update(Map &, NGridType&, GridInfo &, uint32 t_diff) const = 0;
};
class AC_GAME_API InvalidState : public GridState
{
public:
void Update(Map &, NGridType &, GridInfo &, uint32 t_diff) const override;
};
class AC_GAME_API ActiveState : public GridState
{
public:
void Update(Map &, NGridType &, GridInfo &, uint32 t_diff) const override;
};
class AC_GAME_API IdleState : public GridState
{
public:
void Update(Map &, NGridType &, GridInfo &, uint32 t_diff) const override;
};
class AC_GAME_API RemovalState : public GridState
{
public:
void Update(Map &, NGridType &, GridInfo &, uint32 t_diff) const override;
};
#endif

View File

@@ -0,0 +1,29 @@
/*
* This file is part of the AzerothCore 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 Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 "NGrid.h"
#include "Random.h"
GridInfo::GridInfo() : i_timer(0), vis_Update(0, irand(0, DEFAULT_VISIBILITY_NOTIFY_PERIOD)),
i_unloadActiveLockCount(0), i_unloadExplicitLock(false), i_unloadReferenceLock(false)
{
}
GridInfo::GridInfo(std::chrono::seconds expiry, bool unload /*= true */) : i_timer(expiry.count()), vis_Update(0, irand(0, DEFAULT_VISIBILITY_NOTIFY_PERIOD)),
i_unloadActiveLockCount(0), i_unloadExplicitLock(!unload), i_unloadReferenceLock(false)
{
}

View File

@@ -26,6 +26,42 @@
#include "Timer.h"
#include "Util.h"
#define DEFAULT_VISIBILITY_NOTIFY_PERIOD 1000
class AC_GAME_API GridInfo
{
public:
GridInfo();
GridInfo(std::chrono::seconds expiry, bool unload = true);
TimeTracker const& getTimeTracker() const { return i_timer; }
bool getUnloadLock() const { return i_unloadActiveLockCount || i_unloadExplicitLock || i_unloadReferenceLock; }
void setUnloadExplicitLock(bool on) { i_unloadExplicitLock = on; }
void setUnloadReferenceLock(bool on) { i_unloadReferenceLock = on; }
void incUnloadActiveLock() { ++i_unloadActiveLockCount; }
void decUnloadActiveLock() { if (i_unloadActiveLockCount) --i_unloadActiveLockCount; }
void setTimer(TimeTracker const& pTimer) { i_timer = pTimer; }
void ResetTimeTracker(time_t interval) { i_timer.Reset(interval); }
void UpdateTimeTracker(time_t diff) { i_timer.Update(diff); }
PeriodicTimer& getRelocationTimer() { return vis_Update; }
private:
TimeTracker i_timer;
PeriodicTimer vis_Update;
uint16 i_unloadActiveLockCount : 16; // lock from active object spawn points (prevent clone loading)
bool i_unloadExplicitLock : 1; // explicit manual lock or config setting
bool i_unloadReferenceLock : 1; // lock from instance map copy
};
typedef enum
{
GRID_STATE_INVALID = 0,
GRID_STATE_ACTIVE = 1,
GRID_STATE_IDLE = 2,
GRID_STATE_REMOVAL = 3,
MAX_GRID_STATE = 4
} grid_state_t;
template
<
uint32 N,
@@ -37,10 +73,10 @@ class NGrid
{
public:
typedef Grid<ACTIVE_OBJECT, WORLD_OBJECT_TYPES, GRID_OBJECT_TYPES> GridType;
NGrid(uint32 id, int32 x, int32 y)
: i_gridId(id), i_x(x), i_y(y), i_GridObjectDataLoaded(false)
{
}
NGrid(uint32 id, int32 x, int32 y, std::chrono::seconds expiry, bool unload = true) :
i_gridId(id), i_GridInfo(GridInfo(expiry, unload)), i_x(x), i_y(y),
i_cellstate(GRID_STATE_INVALID), i_GridObjectDataLoaded(false)
{ }
GridType& GetGridType(const uint32 x, const uint32 y)
{
@@ -55,6 +91,9 @@ public:
}
[[nodiscard]] uint32 GetGridId() const { return i_gridId; }
void SetGridId(uint32 id) { i_gridId = id; }
[[nodiscard]] grid_state_t GetGridState() const { return i_cellstate; }
void SetGridState(grid_state_t s) { i_cellstate = s; }
[[nodiscard]] int32 getX() const { return i_x; }
[[nodiscard]] int32 getY() const { return i_y; }
@@ -65,6 +104,16 @@ public:
[[nodiscard]] bool isGridObjectDataLoaded() const { return i_GridObjectDataLoaded; }
void setGridObjectDataLoaded(bool pLoaded) { i_GridObjectDataLoaded = pLoaded; }
GridInfo* getGridInfoRef() { return &i_GridInfo; }
TimeTracker const& getTimeTracker() const { return i_GridInfo.getTimeTracker(); }
bool getUnloadLock() const { return i_GridInfo.getUnloadLock(); }
void setUnloadExplicitLock(bool on) { i_GridInfo.setUnloadExplicitLock(on); }
void setUnloadReferenceLock(bool on) { i_GridInfo.setUnloadReferenceLock(on); }
void incUnloadActiveLock() { i_GridInfo.incUnloadActiveLock(); }
void decUnloadActiveLock() { i_GridInfo.decUnloadActiveLock(); }
void ResetTimeTracker(std::chrono::seconds interval) { i_GridInfo.ResetTimeTracker(interval.count()); }
void UpdateTimeTracker(std::chrono::seconds diff) { i_GridInfo.UpdateTimeTracker(diff.count()); }
/*
template<class SPECIFIC_OBJECT> void AddWorldObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj)
{
@@ -103,11 +152,23 @@ public:
GetGridType(x, y).Visit(visitor);
}
template<class T>
uint32 GetWorldObjectCountInNGrid() const
{
uint32 count = 0;
for (uint32 x = 0; x < N; ++x)
for (uint32 y = 0; y < N; ++y)
count += i_cells[x][y].template GetWorldObjectCountInGrid<T>();
return count;
}
private:
uint32 i_gridId;
GridInfo i_GridInfo;
GridReference<NGrid<N, ACTIVE_OBJECT, WORLD_OBJECT_TYPES, GRID_OBJECT_TYPES> > i_Reference;
int32 i_x;
int32 i_y;
grid_state_t i_cellstate;
GridType i_cells[N][N];
bool i_GridObjectDataLoaded;
};

View File

@@ -16,6 +16,7 @@
*/
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Map.h"
#include "ObjectAccessor.h"
#include "SpellInfo.h"
@@ -23,22 +24,10 @@
#include "Transport.h"
#include "UpdateData.h"
#include "WorldPacket.h"
#include "CellImpl.h"
using namespace Acore;
void VisibleNotifier::Visit(GameObjectMapType& m)
{
for (GameObjectMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
GameObject* go = iter->GetSource();
if (i_largeOnly != go->IsVisibilityOverridden())
continue;
vis_guids.erase(go->GetGUID());
i_player.UpdateVisibilityOf(go, i_data, i_visibleNow);
}
}
void VisibleNotifier::SendToSelf()
{
// at this moment i_clientGUIDs have guids that not iterate at grid level checks
@@ -46,9 +35,6 @@ void VisibleNotifier::SendToSelf()
if (Transport* transport = i_player.GetTransport())
for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr)
{
if (i_largeOnly != (*itr)->IsVisibilityOverridden())
continue;
if (vis_guids.find((*itr)->GetGUID()) != vis_guids.end())
{
vis_guids.erase((*itr)->GetGUID());
@@ -65,6 +51,9 @@ void VisibleNotifier::SendToSelf()
case TYPEID_UNIT:
i_player.UpdateVisibilityOf((*itr)->ToCreature(), i_data, i_visibleNow);
break;
case TYPEID_DYNAMICOBJECT:
i_player.UpdateVisibilityOf((*itr)->ToDynObject(), i_data, i_visibleNow);
break;
default:
break;
}
@@ -73,12 +62,6 @@ void VisibleNotifier::SendToSelf()
for (GuidUnorderedSet::const_iterator it = vis_guids.begin(); it != vis_guids.end(); ++it)
{
if (WorldObject* obj = ObjectAccessor::GetWorldObject(i_player, *it))
{
if (i_largeOnly != obj->IsVisibilityOverridden())
continue;
}
// pussywizard: static transports are removed only in RemovePlayerFromMap and here if can no longer detect (eg. phase changed)
if ((*it).IsTransport())
if (GameObject* staticTrans = i_player.GetMap()->GetGameObject(*it))
@@ -105,9 +88,6 @@ void VisibleNotifier::SendToSelf()
for (std::vector<Unit*>::const_iterator it = i_visibleNow.begin(); it != i_visibleNow.end(); ++it)
{
if (i_largeOnly != (*it)->IsVisibilityOverridden())
continue;
i_player.GetInitialVisiblePackets(*it);
}
}
@@ -178,6 +158,23 @@ void PlayerRelocationNotifier::Visit(PlayerMapType& m)
}
}
void PlayerRelocationNotifier::Visit(CreatureMapType& m)
{
bool relocated_for_ai = (&i_player == i_player.m_seer);
for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
Creature* c = iter->GetSource();
vis_guids.erase(c->GetGUID());
i_player.UpdateVisibilityOf(c, i_data, i_visibleNow);
if (relocated_for_ai && !c->isNeedNotify(NOTIFY_VISIBILITY_CHANGED))
CreatureUnitRelocationWorker(c, &i_player);
}
}
void CreatureRelocationNotifier::Visit(PlayerMapType& m)
{
for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
@@ -194,6 +191,58 @@ void CreatureRelocationNotifier::Visit(PlayerMapType& m)
}
}
void CreatureRelocationNotifier::Visit(CreatureMapType& m)
{
if (!i_creature.IsAlive())
return;
for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
Creature* c = iter->GetSource();
CreatureUnitRelocationWorker(&i_creature, c);
if (!c->isNeedNotify(NOTIFY_VISIBILITY_CHANGED))
CreatureUnitRelocationWorker(c, &i_creature);
}
}
void DelayedUnitRelocation::Visit(CreatureMapType& m)
{
for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
Creature* unit = iter->GetSource();
if (!unit->isNeedNotify(NOTIFY_VISIBILITY_CHANGED))
continue;
CreatureRelocationNotifier relocate(*unit);
TypeContainerVisitor<CreatureRelocationNotifier, WorldTypeMapContainer > c2world_relocation(relocate);
TypeContainerVisitor<CreatureRelocationNotifier, GridTypeMapContainer > c2grid_relocation(relocate);
cell.Visit(p, c2world_relocation, i_map, *unit, i_radius);
cell.Visit(p, c2grid_relocation, i_map, *unit, i_radius);
}
}
void DelayedUnitRelocation::Visit(PlayerMapType& m)
{
for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
Player* player = iter->GetSource();
WorldObject const* viewPoint = player->m_seer;
if (!viewPoint->isNeedNotify(NOTIFY_VISIBILITY_CHANGED))
continue;
if (player != viewPoint && !viewPoint->IsPositionValid())
continue;
PlayerRelocationNotifier relocate(*player);
Cell::VisitAllObjects(viewPoint, relocate, i_radius, false);
relocate.SendToSelf();
}
}
void AIRelocationNotifier::Visit(CreatureMapType& m)
{
bool self = isCreature && !((Creature*)(&i_unit))->IsMoveInLineOfSightStrictlyDisabled();
@@ -341,14 +390,9 @@ void MessageDistDelivererToHostile::Visit(DynamicObjectMapType& m)
template<class T>
void ObjectUpdater::Visit(GridRefMgr<T>& m)
{
T* obj;
for (typename GridRefMgr<T>::iterator iter = m.begin(); iter != m.end(); )
{
obj = iter->GetSource();
++iter;
if (obj->IsInWorld() && (i_largeOnly == obj->IsVisibilityOverridden()))
obj->Update(i_timeDiff);
}
for (typename GridRefMgr<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
if (iter->GetSource()->IsInWorld())
iter->GetSource()->Update(i_timeDiff);
}
bool AnyDeadUnitObjectInRangeCheck::operator()(Player* u)

View File

@@ -44,16 +44,14 @@ namespace Acore
GuidUnorderedSet vis_guids;
std::vector<Unit*>& i_visibleNow;
bool i_gobjOnly;
bool i_largeOnly;
UpdateData i_data;
VisibleNotifier(Player& player, bool gobjOnly, bool largeOnly) :
i_player(player), vis_guids(player.m_clientGUIDs), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly), i_largeOnly(largeOnly)
VisibleNotifier(Player& player, bool gobjOnly) :
i_player(player), vis_guids(player.m_clientGUIDs), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly)
{
i_visibleNow.clear();
}
void Visit(GameObjectMapType&);
template<class T> void Visit(GridRefMgr<T>& m);
void SendToSelf(void);
};
@@ -71,9 +69,10 @@ namespace Acore
struct PlayerRelocationNotifier : public VisibleNotifier
{
PlayerRelocationNotifier(Player& player, bool largeOnly): VisibleNotifier(player, false, largeOnly) { }
PlayerRelocationNotifier(Player& player) : VisibleNotifier(player, false) { }
template<class T> void Visit(GridRefMgr<T>& m) { VisibleNotifier::Visit(m); }
void Visit(CreatureMapType&);
void Visit(PlayerMapType&);
};
@@ -82,6 +81,20 @@ namespace Acore
Creature& i_creature;
CreatureRelocationNotifier(Creature& c) : i_creature(c) {}
template<class T> void Visit(GridRefMgr<T>&) {}
void Visit(CreatureMapType&);
void Visit(PlayerMapType&);
};
struct DelayedUnitRelocation
{
Map& i_map;
Cell& cell;
CellCoord& p;
const float i_radius;
DelayedUnitRelocation(Cell& c, CellCoord& pair, Map& map, float radius) :
i_map(map), cell(c), p(pair), i_radius(radius) { }
template<class T> void Visit(GridRefMgr<T>&) { }
void Visit(CreatureMapType&);
void Visit(PlayerMapType&);
};
@@ -94,6 +107,25 @@ namespace Acore
void Visit(CreatureMapType&);
};
struct GridUpdater
{
GridType& i_grid;
uint32 i_timeDiff;
GridUpdater(GridType& grid, uint32 diff) : i_grid(grid), i_timeDiff(diff) { }
template<class T> void updateObjects(GridRefMgr<T>& m)
{
for (typename GridRefMgr<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
iter->GetSource()->Update(i_timeDiff);
}
void Visit(PlayerMapType& m) { updateObjects<Player>(m); }
void Visit(CreatureMapType& m) { updateObjects<Creature>(m); }
void Visit(GameObjectMapType& m) { updateObjects<GameObject>(m); }
void Visit(DynamicObjectMapType& m) { updateObjects<DynamicObject>(m); }
void Visit(CorpseMapType& m) { updateObjects<Corpse>(m); }
};
struct MessageDistDeliverer
{
WorldObject const* i_source;
@@ -154,8 +186,7 @@ namespace Acore
struct ObjectUpdater
{
uint32 i_timeDiff;
bool i_largeOnly;
explicit ObjectUpdater(const uint32 diff, bool largeOnly) : i_timeDiff(diff), i_largeOnly(largeOnly) {}
explicit ObjectUpdater(const uint32 diff) : i_timeDiff(diff) {}
template<class T> void Visit(GridRefMgr<T>& m);
void Visit(PlayerMapType&) {}
void Visit(CorpseMapType&) {}

View File

@@ -38,9 +38,6 @@ inline void Acore::VisibleNotifier::Visit(GridRefMgr<T>& m)
for (typename GridRefMgr<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
{
if (i_largeOnly != iter->GetSource()->IsVisibilityOverridden())
continue;
vis_guids.erase(iter->GetSource()->GetGUID());
i_player.UpdateVisibilityOf(iter->GetSource(), i_data, i_visibleNow);
}

View File

@@ -26,6 +26,35 @@
#include "Transport.h"
#include "Vehicle.h"
void ObjectGridEvacuator::Visit(CreatureMapType& m)
{
// creature in unloading grid can have respawn point in another grid
// if it will be unloaded then it will not respawn in original grid until unload/load original grid
// move to respawn point to prevent this case. For player view in respawn grid this will be normal respawn.
for (CreatureMapType::iterator iter = m.begin(); iter != m.end();)
{
Creature* c = iter->GetSource();
++iter;
ASSERT(!c->IsPet() && "ObjectGridRespawnMover must not be called for pets");
c->GetMap()->CreatureRespawnRelocation(c, true);
}
}
void ObjectGridEvacuator::Visit(GameObjectMapType& m)
{
// gameobject in unloading grid can have respawn point in another grid
// if it will be unloaded then it will not respawn in original grid until unload/load original grid
// move to respawn point to prevent this case. For player view in respawn grid this will be normal respawn.
for (GameObjectMapType::iterator iter = m.begin(); iter != m.end();)
{
GameObject* go = iter->GetSource();
++iter;
go->GetMap()->GameObjectRespawnRelocation(go, true);
}
}
// for loading world object at grid loading (Corpses)
//TODO: to implement npc on transport, also need to load npcs at grid loading
class ObjectWorldLoader
@@ -229,6 +258,17 @@ void ObjectGridUnloader::Visit(GridRefMgr<T>& m)
}
}
void ObjectGridStoper::Visit(CreatureMapType& m)
{
// stop any fights at grid de-activation and remove dynobjects created at cast by creatures
for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
iter->GetSource()->RemoveAllDynObjects();
if (iter->GetSource()->IsInCombat())
iter->GetSource()->CombatStop();
}
}
template<class T>
void ObjectGridCleaner::Visit(GridRefMgr<T>& m)
{

View File

@@ -53,6 +53,23 @@ private:
uint32 i_corpses;
};
//Stop the creatures before unloading the NGrid
class AC_GAME_API ObjectGridStoper
{
public:
void Visit(CreatureMapType& m);
template<class T> void Visit(GridRefMgr<T>&) { }
};
//Move the foreign creatures back to respawn positions before unloading the NGrid
class AC_GAME_API ObjectGridEvacuator
{
public:
void Visit(CreatureMapType& m);
void Visit(GameObjectMapType& m);
template<class T> void Visit(GridRefMgr<T>&) { }
};
//Clean up and remove from world
class ObjectGridCleaner
{