diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 2bef3eb52bc27a..9770e459e6a571 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -939,6 +939,13 @@ ClientCacheVersion = 0 SessionAddDelay = 10000 +# +# GridCleanUpDelay +# Description: Time (in milliseconds) grid clean up delay. +# Default: 300000 - (5 minutes) + +GridCleanUpDelay = 300000 + # # CloseIdleConnections # Description: Automatically close idle connections. @@ -1244,17 +1251,17 @@ Visibility.GroupMode = 1 # Visibility.Distance.Instances # Visibility.Distance.BGArenas # Description: Visibility distance to see other players or gameobjects. -# Visibility on continents on retail ~90 yards. In BG/Arenas ~180. -# For instances default ~120. -# Max limited by active player zone: ~ 333 +# Visibility on continents on retail ~100 yards. In BG/Arenas ~533. +# For instances default ~170. +# Max limited by grid size: 533.33333 # Min limit is max aggro radius (45) * Rate.Creature.Aggro -# Default: 90 - (Visibility.Distance.Continents) -# 120 - (Visibility.Distance.Instances) -# 180 - (Visibility.Distance.BGArenas) +# Default: 100 - (Visibility.Distance.Continents) +# 170 - (Visibility.Distance.Instances) +# 533 - (Visibility.Distance.BGArenas) -Visibility.Distance.Continents = 90 -Visibility.Distance.Instances = 120 -Visibility.Distance.BGArenas = 180 +Visibility.Distance.Continents = 100 +Visibility.Distance.Instances = 170 +Visibility.Distance.BGArenas = 533 # # Visibility.Notify.Period.OnContinents diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp index 4cc94a60dae84c..0360ea47b5ff00 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp @@ -551,6 +551,6 @@ void BattlegroundAB::ApplyPhaseMask() for (auto const& itr : bgPlayerMap) { itr.second->SetPhaseMask(phaseMask, false); - itr.second->UpdateObjectVisibility(true, false); + itr.second->UpdateObjectVisibility(true); } } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 8340fbc72625a4..f64bf3a3e55772 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -365,17 +365,15 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool skipVisibility) //SaveRespawnTime(); } - float x, y, z, o; - GetRespawnPosition(x, y, z, &o); - SetHomePosition(x, y, z, o); - SetPosition(x, y, z, o); - - // xinef: relocate notifier - m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - // pussywizard: if corpse was removed during falling then the falling will continue after respawn, so stop falling is such case if (IsFalling()) StopMoving(); + + float x, y, z, o; + GetRespawnPosition(x, y, z, &o); + UpdateAllowedPositionZ(x, y, z); + SetHomePosition(x, y, z, o); + GetMap()->CreatureRelocation(this, x, y, z, o); } /** @@ -2077,9 +2075,7 @@ void Creature::Respawn(bool force) m_respawnedTime = GameTime::GetGameTime().count(); } m_respawnedTime = GameTime::GetGameTime().count(); - // xinef: relocate notifier, fixes npc appearing in corpse position after forced respawn (instead of spawn) - m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - UpdateObjectVisibility(false); + UpdateObjectVisibility(); } void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds forceRespawnTimer) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index fef69d6ef3d544..c0584ef005a6e8 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -65,7 +65,7 @@ constexpr float VisibilityDistances[AsUnderlyingType(VisibilityDistanceType::Max VISIBILITY_DISTANCE_SMALL, VISIBILITY_DISTANCE_LARGE, VISIBILITY_DISTANCE_GIGANTIC, - VISIBILITY_DISTANCE_INFINITE + MAX_VISIBILITY_DISTANCE }; Object::Object() : m_PackGUID(sizeof(uint64) + 1) @@ -2922,7 +2922,7 @@ void WorldObject::DestroyForNearbyPlayers() } } -void WorldObject::UpdateObjectVisibility(bool /*forced*/, bool /*fromUpdate*/) +void WorldObject::UpdateObjectVisibility(bool /*forced*/) { //updates object's visibility for nearby players Acore::VisibleChangesNotifier notifier(*this); @@ -2931,28 +2931,7 @@ void WorldObject::UpdateObjectVisibility(bool /*forced*/, bool /*fromUpdate*/) void WorldObject::AddToNotify(uint16 f) { - if (!(m_notifyflags & f)) - if (Unit* u = ToUnit()) - { - if (f & NOTIFY_VISIBILITY_CHANGED) - { - uint32 EVENT_VISIBILITY_DELAY = u->FindMap() ? DynamicVisibilityMgr::GetVisibilityNotifyDelay(u->FindMap()->GetEntry()->map_type) : 1000; - - uint32 diff = getMSTimeDiff(u->m_last_notify_mstime, GameTime::GetGameTimeMS().count()); - if (diff >= EVENT_VISIBILITY_DELAY / 2) - EVENT_VISIBILITY_DELAY /= 2; - else - EVENT_VISIBILITY_DELAY -= diff; - u->m_delayed_unit_relocation_timer = EVENT_VISIBILITY_DELAY; - u->m_last_notify_mstime = GameTime::GetGameTimeMS().count() + EVENT_VISIBILITY_DELAY - 1; - } - else if (f & NOTIFY_AI_RELOCATION) - { - u->m_delayed_unit_ai_notify_timer = u->FindMap() ? DynamicVisibilityMgr::GetAINotifyDelay(u->FindMap()->GetEntry()->map_type) : 500; - } - - m_notifyflags |= f; - } + m_notifyflags |= f; } struct WorldObjectChangeAccumulator diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 8a75f3d87e88c9..adaf92899d8887 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -379,14 +379,23 @@ class MovableMapObject template friend class RandomMovementGenerator; protected: - MovableMapObject() = default; + MovableMapObject() : _moveState(MAP_OBJECT_CELL_MOVE_NONE) + { + _newPosition.Relocate(0.0f, 0.0f, 0.0f, 0.0f); + } private: + Cell _currentCell; [[nodiscard]] Cell const& GetCurrentCell() const { return _currentCell; } void SetCurrentCell(Cell const& cell) { _currentCell = cell; } - Cell _currentCell; - MapObjectCellMoveState _moveState{MAP_OBJECT_CELL_MOVE_NONE}; + MapObjectCellMoveState _moveState; + Position _newPosition; + void SetNewCellPosition(float x, float y, float z, float o) + { + _moveState = MAP_OBJECT_CELL_MOVE_ACTIVE; + _newPosition.Relocate(x, y, z, o); + } }; class WorldObject : public Object, public WorldLocation @@ -538,7 +547,7 @@ class WorldObject : public Object, public WorldLocation void GetDeadCreatureListInGrid(std::list& lList, float maxSearchRange, bool alive = false) const; void DestroyForNearbyPlayers(); - virtual void UpdateObjectVisibility(bool forced = true, bool fromUpdate = false); + virtual void UpdateObjectVisibility(bool forced = true); void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet& player_set) override; void GetCreaturesWithEntryInRange(std::list& creatureList, float radius, uint32 entry); diff --git a/src/server/game/Entities/Object/ObjectDefines.h b/src/server/game/Entities/Object/ObjectDefines.h index 55c4826339ab93..5d809c05f58b58 100644 --- a/src/server/game/Entities/Object/ObjectDefines.h +++ b/src/server/game/Entities/Object/ObjectDefines.h @@ -28,16 +28,15 @@ #define VISIBILITY_INC_FOR_GOBJECTS 30.0f // pussywizard #define SPELL_SEARCHER_COMPENSATION 30.0f // increase searchers size in case we have large npc near cell border #define TRADE_DISTANCE 11.11f -#define MAX_VISIBILITY_DISTANCE 250.0f // max distance for visible objects, experimental +#define MAX_VISIBILITY_DISTANCE SIZE_OF_GRIDS // max distance for visible objects, experimental #define SIGHT_RANGE_UNIT 50.0f #define MAX_SEARCHER_DISTANCE 150.0f // pussywizard: replace the use of MAX_VISIBILITY_DISTANCE in searchers, because MAX_VISIBILITY_DISTANCE is quite too big for this purpose -#define VISIBILITY_DISTANCE_INFINITE 533.0f #define VISIBILITY_DISTANCE_GIGANTIC 400.0f #define VISIBILITY_DISTANCE_LARGE 200.0f #define VISIBILITY_DISTANCE_NORMAL 100.0f #define VISIBILITY_DISTANCE_SMALL 50.0f #define VISIBILITY_DISTANCE_TINY 25.0f -#define DEFAULT_VISIBILITY_DISTANCE 100.0f // default visible distance, 100 yards on continents +#define DEFAULT_VISIBILITY_DISTANCE VISIBILITY_DISTANCE_NORMAL // default visible distance, 100 yards on continents #define DEFAULT_VISIBILITY_INSTANCE 170.0f // default visible distance in instances, 170 yards #define VISIBILITY_DIST_WINTERGRASP 175.0f #define DEFAULT_VISIBILITY_BGARENAS 533.0f // default visible distance in BG/Arenas, roughly 533 yards diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index cffcaf7dfb1330..49b85e860c0a16 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2349,7 +2349,7 @@ class Player : public Unit, public GridObject bool IsVisibleGloballyFor(Player const* player) const; void GetInitialVisiblePackets(Unit* target); - void UpdateObjectVisibility(bool forced = true, bool fromUpdate = false) override; + void UpdateObjectVisibility(bool forced = true) override; void UpdateVisibilityForPlayer(bool mapChange = false); void UpdateVisibilityOf(WorldObject* target); void UpdateTriggerVisibility(); diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 4e703b43392d63..17c1319f9a4090 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -412,14 +412,6 @@ void Player::Update(uint32 p_time) SetHasDelayedTeleport(false); TeleportTo(teleportStore_dest, teleportStore_options); } - - if (!IsBeingTeleported() && bRequestForcedVisibilityUpdate) - { - bRequestForcedVisibilityUpdate = false; - UpdateObjectVisibility(true, true); - m_delayed_unit_relocation_timer = 0; - RemoveFromNotify(NOTIFY_VISIBILITY_CHANGED); - } } void Player::UpdateMirrorTimers() @@ -1550,23 +1542,13 @@ void Player::UpdateVisibilityForPlayer(bool mapChange) m_seer = this; } - Acore::VisibleNotifier notifierNoLarge( - *this, mapChange, - false); // visit only objects which are not large; default distance - Cell::VisitAllObjects(m_seer, notifierNoLarge, - GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - notifierNoLarge.SendToSelf(); - - Acore::VisibleNotifier notifierLarge( - *this, mapChange, true); // visit only large objects; maximum distance - Cell::VisitAllObjects(m_seer, notifierLarge, GetSightRange()); - notifierLarge.SendToSelf(); - - if (mapChange) - m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); + // updates visibility of all objects around point of view for current player + Acore::VisibleNotifier notifier(*this, mapChange); + Cell::VisitAllObjects(m_seer, notifier, GetSightRange()); + notifier.SendToSelf(); // send gathered data } -void Player::UpdateObjectVisibility(bool forced, bool fromUpdate) +void Player::UpdateObjectVisibility(bool forced) { // Prevent updating visibility if player is not in world (example: LoadFromDB sets drunkstate which updates invisibility while player is not in map) if (!IsInWorld()) @@ -1576,11 +1558,6 @@ void Player::UpdateObjectVisibility(bool forced, bool fromUpdate) AddToNotify(NOTIFY_VISIBILITY_CHANGED); else if (!isBeingLoaded()) { - if (!fromUpdate) // pussywizard: - { - bRequestForcedVisibilityUpdate = true; - return; - } Unit::UpdateObjectVisibility(true); UpdateVisibilityForPlayer(); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index c7964a1ff91767..9c7d6030bbf9b5 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -317,12 +317,6 @@ Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject), m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE); - m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); - m_last_notify_mstime = 0; - m_delayed_unit_relocation_timer = 0; - m_delayed_unit_ai_notify_timer = 0; - bRequestForcedVisibilityUpdate = false; - m_applyResilience = false; _instantCast = false; @@ -411,32 +405,6 @@ void Unit::Update(uint32 p_time) if (!IsInWorld()) return; - // pussywizard: - if (GetTypeId() != TYPEID_PLAYER || (!ToPlayer()->IsBeingTeleported() && !bRequestForcedVisibilityUpdate)) - { - if (m_delayed_unit_relocation_timer) - { - if (m_delayed_unit_relocation_timer <= p_time) - { - m_delayed_unit_relocation_timer = 0; - //ExecuteDelayedUnitRelocationEvent(); - FindMap()->i_objectsForDelayedVisibility.insert(this); - } - else - m_delayed_unit_relocation_timer -= p_time; - } - if (m_delayed_unit_ai_notify_timer) - { - if (m_delayed_unit_ai_notify_timer <= p_time) - { - m_delayed_unit_ai_notify_timer = 0; - ExecuteDelayedUnitAINotifyEvent(); - } - else - m_delayed_unit_ai_notify_timer -= p_time; - } - } - _UpdateSpells( p_time ); if (CanHaveThreatList() && GetThreatMgr().isNeedUpdateToClient(p_time)) @@ -17524,13 +17492,17 @@ void Unit::SetContestedPvP(Player* attackedPlayer, bool lookForNearContestedGuar player->AddUnitState(UNIT_STATE_ATTACK_PLAYER); player->SetPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP); // call MoveInLineOfSight for nearby contested guards - AddToNotify(NOTIFY_AI_RELOCATION); + Acore::AIRelocationNotifier notifier(*this); + Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); } - if (!HasUnitState(UNIT_STATE_ATTACK_PLAYER)) + for (Unit* unit : m_Controlled) { - AddUnitState(UNIT_STATE_ATTACK_PLAYER); - // call MoveInLineOfSight for nearby contested guards - AddToNotify(NOTIFY_AI_RELOCATION); + if (!unit->HasUnitState(UNIT_STATE_ATTACK_PLAYER)) + { + unit->AddUnitState(UNIT_STATE_ATTACK_PLAYER); + Acore::AIRelocationNotifier notifier(*unit); + Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); + } } } @@ -19377,7 +19349,7 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) } } -void Unit::UpdateObjectVisibility(bool forced, bool /*fromUpdate*/) +void Unit::UpdateObjectVisibility(bool forced) { if (!forced) AddToNotify(NOTIFY_VISIBILITY_CHANGED); @@ -19385,8 +19357,7 @@ void Unit::UpdateObjectVisibility(bool forced, bool /*fromUpdate*/) { WorldObject::UpdateObjectVisibility(true); Acore::AIRelocationNotifier notifier(*this); - float radius = 60.0f; - Cell::VisitAllObjects(this, notifier, radius); + Cell::VisitAllObjects(this, notifier, GetVisibilityRange()); } } @@ -20743,124 +20714,6 @@ bool ConflagrateAuraStateDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time return true; } -void Unit::ExecuteDelayedUnitRelocationEvent() -{ - this->RemoveFromNotify(NOTIFY_VISIBILITY_CHANGED); - if (!this->IsInWorld() || this->IsDuringRemoveFromWorld()) - return; - - if (this->HasSharedVision()) - for (SharedVisionList::const_iterator itr = this->GetSharedVisionList().begin(); itr != this->GetSharedVisionList().end(); ++itr) - if (Player* player = (*itr)) - { - if (player->IsOnVehicle(this) || !player->IsInWorld() || player->IsDuringRemoveFromWorld()) // players on vehicles have their own event executed (due to passenger relocation) - continue; - WorldObject* viewPoint = player; - if (player->m_seer && player->m_seer->IsInWorld()) - viewPoint = player->m_seer; - if (!viewPoint->IsPositionValid() || !player->IsPositionValid()) - continue; - - if (Unit* active = viewPoint->ToUnit()) - { - //if (active->IsVehicle()) // always check original unit here, last notify position is not relocated - // active = player; - - float dx = active->m_last_notify_position.GetPositionX() - active->GetPositionX(); - float dy = active->m_last_notify_position.GetPositionY() - active->GetPositionY(); - float dz = active->m_last_notify_position.GetPositionZ() - active->GetPositionZ(); - float distsq = dx * dx + dy * dy + dz * dz; - float mindistsq = DynamicVisibilityMgr::GetReqMoveDistSq(active->FindMap()->GetEntry()->map_type); - if (distsq < mindistsq) - continue; - - // this will be relocated below sharedvision! - //active->m_last_notify_position.Relocate(active->GetPositionX(), active->GetPositionY(), active->GetPositionZ()); - } - - Acore::PlayerRelocationNotifier relocateNoLarge(*player, false); // visit only objects which are not large; default distance - Cell::VisitAllObjects(viewPoint, relocateNoLarge, player->GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - relocateNoLarge.SendToSelf(); - Acore::PlayerRelocationNotifier relocateLarge(*player, true); // visit only large objects; maximum distance - Cell::VisitAllObjects(viewPoint, relocateLarge, MAX_VISIBILITY_DISTANCE); - relocateLarge.SendToSelf(); - } - - if (Player* player = this->ToPlayer()) - { - WorldObject* viewPoint = player; - if (player->m_seer && player->m_seer->IsInWorld()) - viewPoint = player->m_seer; - - if (viewPoint->GetMapId() != player->GetMapId() || !viewPoint->IsPositionValid() || !player->IsPositionValid()) - return; - - if (Unit* active = viewPoint->ToUnit()) - { - if (active->IsVehicle()) - active = player; - - if (!player->GetFarSightDistance()) - { - float dx = active->m_last_notify_position.GetPositionX() - active->GetPositionX(); - float dy = active->m_last_notify_position.GetPositionY() - active->GetPositionY(); - float dz = active->m_last_notify_position.GetPositionZ() - active->GetPositionZ(); - float distsq = dx * dx + dy * dy + dz * dz; - - float mindistsq = DynamicVisibilityMgr::GetReqMoveDistSq(active->FindMap()->GetEntry()->map_type); - if (distsq < mindistsq) - return; - - active->m_last_notify_position.Relocate(active->GetPositionX(), active->GetPositionY(), active->GetPositionZ()); - } - } - - Acore::PlayerRelocationNotifier relocateNoLarge(*player, false); // visit only objects which are not large; default distance - Cell::VisitAllObjects(viewPoint, relocateNoLarge, player->GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - relocateNoLarge.SendToSelf(); - - if (!player->GetFarSightDistance()) - { - Acore::PlayerRelocationNotifier relocateLarge(*player, true); // visit only large objects; maximum distance - Cell::VisitAllObjects(viewPoint, relocateLarge, MAX_VISIBILITY_DISTANCE); - relocateLarge.SendToSelf(); - } - - this->AddToNotify(NOTIFY_AI_RELOCATION); - } - else if (Creature* unit = this->ToCreature()) - { - if (!unit->IsPositionValid()) - return; - - float dx = unit->m_last_notify_position.GetPositionX() - unit->GetPositionX(); - float dy = unit->m_last_notify_position.GetPositionY() - unit->GetPositionY(); - float dz = unit->m_last_notify_position.GetPositionZ() - unit->GetPositionZ(); - float distsq = dx * dx + dy * dy + dz * dz; - float mindistsq = DynamicVisibilityMgr::GetReqMoveDistSq(unit->FindMap()->GetEntry()->map_type); - if (distsq < mindistsq) - return; - - unit->m_last_notify_position.Relocate(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); - - Acore::CreatureRelocationNotifier relocate(*unit); - Cell::VisitAllObjects(unit, relocate, unit->GetVisibilityRange() + VISIBILITY_COMPENSATION); - - this->AddToNotify(NOTIFY_AI_RELOCATION); - } -} - -void Unit::ExecuteDelayedUnitAINotifyEvent() -{ - this->RemoveFromNotify(NOTIFY_AI_RELOCATION); - if (!this->IsInWorld() || this->IsDuringRemoveFromWorld()) - return; - - Acore::AIRelocationNotifier notifier(*this); - float radius = 60.0f; - Cell::VisitAllObjects(this, notifier, radius); -} - void Unit::SetInFront(WorldObject const* target) { if (!HasUnitState(UNIT_STATE_CANNOT_TURN)) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 87cfccca04d323..5fe385fb78858f 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -2143,7 +2143,7 @@ class Unit : public WorldObject // common function for visibility checks for player/creatures with detection code [[nodiscard]] uint32 GetPhaseByAuras() const; void SetPhaseMask(uint32 newPhaseMask, bool update) override;// overwrite WorldObject::SetPhaseMask - void UpdateObjectVisibility(bool forced = true, bool fromUpdate = false) override; + void UpdateObjectVisibility(bool forced = true) override; SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY]; uint32 m_lastSanctuaryTime; @@ -2417,14 +2417,6 @@ class Unit : public WorldObject void AddPointedBy(SafeUnitPointer* sup) { SafeUnitPointerSet.insert(sup); } void RemovePointedBy(SafeUnitPointer* sup) { SafeUnitPointerSet.erase(sup); } static void HandleSafeUnitPointersOnDelete(Unit* thisUnit); - // Relocation Nofier optimization - Position m_last_notify_position; - uint32 m_last_notify_mstime; - uint16 m_delayed_unit_relocation_timer; - uint16 m_delayed_unit_ai_notify_timer; - bool bRequestForcedVisibilityUpdate; - void ExecuteDelayedUnitRelocationEvent(); - void ExecuteDelayedUnitAINotifyEvent(); // cooldowns [[nodiscard]] virtual bool HasSpellCooldown(uint32 /*spell_id*/) const { return false; } diff --git a/src/server/game/Grids/Grid.h b/src/server/game/Grids/Grid.h index b9cafdbe0cd487..d67b9ca3180b38 100644 --- a/src/server/game/Grids/Grid.h +++ b/src/server/game/Grids/Grid.h @@ -96,6 +96,12 @@ class Grid visitor.Visit(i_objects); } + template + uint32 GetWorldObjectCountInGrid() const + { + return i_objects.template Count(); + } + /** Inserts a container type object into the grid. */ template void AddGridObject(SPECIFIC_OBJECT* obj) diff --git a/src/server/game/Grids/GridStates.cpp b/src/server/game/Grids/GridStates.cpp new file mode 100644 index 00000000000000..eb0183b78383ec --- /dev/null +++ b/src/server/game/Grids/GridStates.cpp @@ -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 . + */ + +#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() && !map.ActiveObjectsNearGrid(grid)) + { + ObjectGridStoper worker; + TypeContainerVisitor 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); + } + } +} diff --git a/src/server/game/Grids/GridStates.h b/src/server/game/Grids/GridStates.h new file mode 100644 index 00000000000000..5b0aec3d9e2f82 --- /dev/null +++ b/src/server/game/Grids/GridStates.h @@ -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 . + */ + +#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 diff --git a/src/server/game/Grids/NGrid.cpp b/src/server/game/Grids/NGrid.cpp new file mode 100644 index 00000000000000..c20bf555824898 --- /dev/null +++ b/src/server/game/Grids/NGrid.cpp @@ -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 . + */ + +#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) +{ +} diff --git a/src/server/game/Grids/NGrid.h b/src/server/game/Grids/NGrid.h index ff1f1ed813d020..a49a299194cd99 100644 --- a/src/server/game/Grids/NGrid.h +++ b/src/server/game/Grids/NGrid.h @@ -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 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 @@ class NGrid } [[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 @@ class NGrid [[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 void AddWorldObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj) { @@ -103,11 +152,23 @@ class NGrid GetGridType(x, y).Visit(visitor); } + template + 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(); + return count; + } + private: uint32 i_gridId; + GridInfo i_GridInfo; GridReference > i_Reference; int32 i_x; int32 i_y; + grid_state_t i_cellstate; GridType i_cells[N][N]; bool i_GridObjectDataLoaded; }; diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 7b25ab1bdd5f22..c9e9bd73522f3c 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -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::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 c2world_relocation(relocate); + TypeContainerVisitor 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 void ObjectUpdater::Visit(GridRefMgr& m) { - T* obj; - for (typename GridRefMgr::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::iterator iter = m.begin(); iter != m.end(); ++iter) + if (iter->GetSource()->IsInWorld()) + iter->GetSource()->Update(i_timeDiff); } bool AnyDeadUnitObjectInRangeCheck::operator()(Player* u) diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index e06b9091f31690..c634fe9e35f3f6 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -44,16 +44,14 @@ namespace Acore GuidUnorderedSet vis_guids; std::vector& 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 void Visit(GridRefMgr& 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 void Visit(GridRefMgr& 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 void Visit(GridRefMgr&) {} + 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 void Visit(GridRefMgr&) { } + 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 void updateObjects(GridRefMgr& m) + { + for (typename GridRefMgr::iterator iter = m.begin(); iter != m.end(); ++iter) + iter->GetSource()->Update(i_timeDiff); + } + + void Visit(PlayerMapType& m) { updateObjects(m); } + void Visit(CreatureMapType& m) { updateObjects(m); } + void Visit(GameObjectMapType& m) { updateObjects(m); } + void Visit(DynamicObjectMapType& m) { updateObjects(m); } + void Visit(CorpseMapType& m) { updateObjects(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 void Visit(GridRefMgr& m); void Visit(PlayerMapType&) {} void Visit(CorpseMapType&) {} diff --git a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h index 3cd1b77dcf3ad3..00d967880c94e0 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h +++ b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h @@ -38,9 +38,6 @@ inline void Acore::VisibleNotifier::Visit(GridRefMgr& m) for (typename GridRefMgr::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); } diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp index 628189ae4b43b0..c0d9e78c08e91d 100644 --- a/src/server/game/Grids/ObjectGridLoader.cpp +++ b/src/server/game/Grids/ObjectGridLoader.cpp @@ -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& 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 void ObjectGridCleaner::Visit(GridRefMgr& m) { diff --git a/src/server/game/Grids/ObjectGridLoader.h b/src/server/game/Grids/ObjectGridLoader.h index 863e5f15f5f8a9..d16c3c5bc4fbd5 100644 --- a/src/server/game/Grids/ObjectGridLoader.h +++ b/src/server/game/Grids/ObjectGridLoader.h @@ -53,6 +53,23 @@ class ObjectGridLoader uint32 i_corpses; }; +//Stop the creatures before unloading the NGrid +class AC_GAME_API ObjectGridStoper +{ +public: + void Visit(CreatureMapType& m); + template void Visit(GridRefMgr&) { } +}; + +//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 void Visit(GridRefMgr&) { } +}; + //Clean up and remove from world class ObjectGridCleaner { diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp index ec6854ede84bfa..66927f642062dd 100644 --- a/src/server/game/Instances/InstanceSaveMgr.cpp +++ b/src/server/game/Instances/InstanceSaveMgr.cpp @@ -20,6 +20,7 @@ #include "Config.h" #include "GameTime.h" #include "GridNotifiers.h" +#include "GridStates.h" #include "Group.h" #include "InstanceScript.h" #include "Log.h" diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 5f6a1418ed0e92..e66ef5c9b988d3 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -24,6 +24,8 @@ #include "GameTime.h" #include "Geometry.h" #include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "GridStates.h" #include "Group.h" #include "InstanceScript.h" #include "LFGMgr.h" @@ -56,6 +58,8 @@ u_map_magic MapLiquidMagic = { {'M', 'L', 'I', 'Q'} }; static uint16 const holetab_h[4] = { 0x1111, 0x2222, 0x4444, 0x8888 }; static uint16 const holetab_v[4] = { 0x000F, 0x00F0, 0x0F00, 0xF000 }; +GridState* si_GridStates[MAX_GRID_STATE]; + ZoneDynamicInfo::ZoneDynamicInfo() : MusicId(0), WeatherId(WEATHER_STATE_FINE), WeatherGrade(0.0f), OverrideLightId(0), LightFadeInTime(0) { } @@ -188,6 +192,7 @@ void Map::LoadMap(int gx, int gy, bool reload) // load grid map for base map m_parentMap->EnsureGridCreated(GridCoord(63 - gx, 63 - gy)); + ((MapInstanced*)(m_parentMap))->AddGridMapReference(GridCoord(gx, gy)); GridMaps[gx][gy] = m_parentMap->GridMaps[gx][gy]; return; } @@ -232,11 +237,14 @@ void Map::LoadMapAndVMap(int gx, int gy) } } -Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) : +Map::Map(uint32 id, std::chrono::seconds expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent) : + _creatureToMoveLock(false), _gameObjectsToMoveLock(false), _dynamicObjectsToMoveLock(false), i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId), - m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), - _instanceResetPeriod(0), m_activeNonPlayersIter(m_activeNonPlayers.end()), - _transportsUpdateIter(_transports.end()), i_scriptLock(false), _defaultLight(GetDefaultMapLight(id)) + m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), _instanceResetPeriod(0), + m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD), + m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()), + i_gridExpiry(expiry), + i_scriptLock(false), _defaultLight(GetDefaultMapLight(id)) { m_parentMap = (_parent ? _parent : this); for (unsigned int idx = 0; idx < MAX_NUMBER_OF_GRIDS; ++idx) @@ -259,6 +267,7 @@ void Map::InitVisibilityDistance() { //init visibility for continents m_VisibleDistance = World::GetMaxVisibleDistanceOnContinents(); + m_VisibilityNotifyPeriod = World::GetVisibilityNotifyPeriodOnContinents(); switch (GetId()) { @@ -431,8 +440,6 @@ void Map::DeleteFromWorld(Player* player) void Map::EnsureGridCreated(const GridCoord& p) { - if (getNGrid(p.x_coord, p.y_coord)) // pussywizard - return; std::lock_guard guard(GridLock); EnsureGridCreated_i(p); } @@ -443,11 +450,14 @@ void Map::EnsureGridCreated_i(const GridCoord& p) { if (!getNGrid(p.x_coord, p.y_coord)) { - // pussywizard: moved setNGrid to the end of the function - NGridType* ngt = new NGridType(p.x_coord * MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord); + LOG_DEBUG("maps", "Creating grid[{}, {}] for map {} instance {}", p.x_coord, p.y_coord, GetId(), i_InstanceId); + + setNGrid(new NGridType(p.x_coord * MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord, i_gridExpiry), p.x_coord, p.y_coord); // build a linkage between this map and NGridType - buildNGridLinkage(ngt); // pussywizard: getNGrid(x, y) changed to: ngt + buildNGridLinkage(getNGrid(p.x_coord, p.y_coord)); + + getNGrid(p.x_coord, p.y_coord)->SetGridState(GRID_STATE_IDLE); //z coord int gx = (MAX_NUMBER_OF_GRIDS - 1) - p.x_coord; @@ -457,9 +467,22 @@ void Map::EnsureGridCreated_i(const GridCoord& p) { LoadMapAndVMap(gx, gy); } + } +} - // pussywizard: moved here - setNGrid(ngt, p.x_coord, p.y_coord); +//Load NGrid and make it active +void Map::EnsureGridLoadedForActiveObject(const Cell& cell, WorldObject* object) +{ + EnsureGridLoaded(cell); + NGridType* grid = getNGrid(cell.GridX(), cell.GridY()); + ASSERT(grid != nullptr); + + // refresh grid state & timer + if (grid->GetGridState() != GRID_STATE_ACTIVE) + { + LOG_DEBUG("maps", "Active object {} triggers loading of grid [{}, {}] on map {}", object->GetGUID().ToString().c_str(), cell.GridX(), cell.GridY(), GetId()); + ResetGridExpiry(*grid, 0.1f); + grid->SetGridState(GRID_STATE_ACTIVE); } } @@ -489,6 +512,29 @@ bool Map::EnsureGridLoaded(const Cell& cell) return false; } +void Map::GridMarkNoUnload(uint32 x, uint32 y) +{ + // First make sure this grid is loaded + float gX = ((float(x) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2); + float gY = ((float(y) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2); + Cell cell = Cell(gX, gY); + EnsureGridLoaded(cell); + + // Mark as don't unload + NGridType* grid = getNGrid(x, y); + grid->setUnloadExplicitLock(true); +} + +void Map::GridUnmarkNoUnload(uint32 x, uint32 y) +{ + // If grid is loaded, clear unload lock + if (IsGridLoaded(GridCoord(x, y))) + { + NGridType* grid = getNGrid(x, y); + grid->setUnloadExplicitLock(false); + } +} + void Map::LoadGrid(float x, float y) { EnsureGridLoaded(Cell(x, y)); @@ -501,6 +547,22 @@ void Map::LoadAllCells() LoadGrid((cellX + 0.5f - CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL, (cellY + 0.5f - CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL); } +void Map::InitStateMachine() +{ + si_GridStates[GRID_STATE_INVALID] = new InvalidState(); + si_GridStates[GRID_STATE_ACTIVE] = new ActiveState(); + si_GridStates[GRID_STATE_IDLE] = new IdleState(); + si_GridStates[GRID_STATE_REMOVAL] = new RemovalState(); +} + +void Map::DeleteStateMachine() +{ + delete si_GridStates[GRID_STATE_INVALID]; + delete si_GridStates[GRID_STATE_ACTIVE]; + delete si_GridStates[GRID_STATE_IDLE]; + delete si_GridStates[GRID_STATE_REMOVAL]; +} + bool Map::AddPlayerToMap(Player* player) { CellCoord cellCoord = Acore::ComputeCellCoord(player->GetPositionX(), player->GetPositionY()); @@ -512,7 +574,7 @@ bool Map::AddPlayerToMap(Player* player) } Cell cell(cellCoord); - EnsureGridLoaded(cell); + EnsureGridLoadedForActiveObject(cell, player); AddToGrid(player, cell); // Check if we are adding to correct map @@ -540,15 +602,15 @@ void Map::InitializeObject(T* /*obj*/) } template<> -void Map::InitializeObject(Creature* /*obj*/) +void Map::InitializeObject(Creature* obj) { - //obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; + obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; } template<> -void Map::InitializeObject(GameObject* /*obj*/) +void Map::InitializeObject(GameObject* obj) { - //obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; + obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; } template @@ -576,7 +638,7 @@ bool Map::AddToMap(T* obj, bool checkTransport) Cell cell(cellCoord); if (obj->isActiveObject()) - EnsureGridLoaded(cell); + EnsureGridLoadedForActiveObject(cell, obj); else EnsureGridCreated(GridCoord(cell.GridX(), cell.GridY())); @@ -658,45 +720,8 @@ bool Map::IsGridLoaded(const GridCoord& p) const return (getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord)); } -void Map::VisitNearbyCellsOfPlayer(Player* player, TypeContainerVisitor& gridVisitor, - TypeContainerVisitor& worldVisitor, - TypeContainerVisitor& largeGridVisitor, - TypeContainerVisitor& largeWorldVisitor) -{ - // check for valid position - if (!player->IsPositionValid()) - return; - - // check normal grid activation range of the player - VisitNearbyCellsOf(player, gridVisitor, worldVisitor, largeGridVisitor, largeWorldVisitor); - - // check maximum visibility distance for large creatures - CellArea area = Cell::CalculateCellArea(player->GetPositionX(), player->GetPositionY(), MAX_VISIBILITY_DISTANCE); - - for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x) - { - for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y) - { - // marked cells are those that have been visited - // don't visit the same cell twice - uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x; - if (isCellMarkedLarge(cell_id)) - continue; - - markCellLarge(cell_id); - CellCoord pair(x, y); - Cell cell(pair); - - Visit(cell, largeGridVisitor); - Visit(cell, largeWorldVisitor); - } - } -} - void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor& gridVisitor, - TypeContainerVisitor& worldVisitor, - TypeContainerVisitor& largeGridVisitor, - TypeContainerVisitor& largeWorldVisitor) + TypeContainerVisitor& worldVisitor) { // Check for valid position if (!obj->IsPositionValid()) @@ -725,13 +750,6 @@ void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitorGetSource(); - - if (!player || !player->IsInWorld()) - continue; - - // update players at tick - player->Update(s_diff); - } - - HandleDelayedVisibility(); - return; - } - /// update active cells around players and active objects resetMarkedCells(); - resetMarkedCellsLarge(); - Acore::ObjectUpdater updater(t_diff, false); + Acore::ObjectUpdater updater(t_diff); // for creature TypeContainerVisitor grid_object_update(updater); // for pets TypeContainerVisitor world_object_update(updater); - // for large creatures - Acore::ObjectUpdater largeObjectUpdater(t_diff, true); - TypeContainerVisitor grid_large_object_update(largeObjectUpdater); - TypeContainerVisitor world_large_object_update(largeObjectUpdater); - // pussywizard: container for far creatures in combat with players std::vector updateList; updateList.reserve(10); @@ -800,7 +795,7 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) if (!obj || !obj->IsInWorld()) continue; - VisitNearbyCellsOf(obj, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); + VisitNearbyCellsOf(obj, grid_object_update, world_object_update); } // the player iterator is stored in the map object @@ -815,20 +810,11 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) // update players at tick player->Update(s_diff); - VisitNearbyCellsOfPlayer(player, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); + VisitNearbyCellsOf(player, grid_object_update, world_object_update); // If player is using far sight, visit that object too if (WorldObject* viewPoint = player->GetViewpoint()) - { - if (Creature* viewCreature = viewPoint->ToCreature()) - { - VisitNearbyCellsOf(viewCreature, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); - } - else if (DynamicObject* viewObject = viewPoint->ToDynObject()) - { - VisitNearbyCellsOf(viewObject, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); - } - } + VisitNearbyCellsOf(viewPoint, grid_object_update, world_object_update); // handle updates for creatures in combat with player and are more than X yards away if (player->IsInCombat()) @@ -846,7 +832,7 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) ref = ref->next(); } for (std::vector::const_iterator itr = updateList.begin(); itr != updateList.end(); ++itr) - VisitNearbyCellsOf(*itr, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); + VisitNearbyCellsOf(*itr, grid_object_update, world_object_update); } } @@ -875,7 +861,8 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) MoveAllGameObjectsInMoveList(); MoveAllDynamicObjectsInMoveList(); - HandleDelayedVisibility(); + if (!m_mapRefMgr.IsEmpty() || !m_activeNonPlayers.empty()) + ProcessRelocationNotifies(t_diff); sScriptMgr->OnMapUpdate(this, t_diff); @@ -888,15 +875,6 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) METRIC_TAG("map_instanceid", std::to_string(GetInstanceId()))); } -void Map::HandleDelayedVisibility() -{ - if (i_objectsForDelayedVisibility.empty()) - return; - for (std::unordered_set::iterator itr = i_objectsForDelayedVisibility.begin(); itr != i_objectsForDelayedVisibility.end(); ++itr) - (*itr)->ExecuteDelayedUnitRelocationEvent(); - i_objectsForDelayedVisibility.clear(); -} - struct ResetNotifier { templateinline void resetNotify(GridRefMgr& m) @@ -909,6 +887,83 @@ struct ResetNotifier void Visit(PlayerMapType& m) { resetNotify(m);} }; +void Map::ProcessRelocationNotifies(uint32 diff) +{ + for (GridRefMgr::iterator i = GridRefMgr::begin(); i != GridRefMgr::end(); ++i) + { + NGridType* grid = i->GetSource(); + + if (grid->GetGridState() != GRID_STATE_ACTIVE) + continue; + + grid->getGridInfoRef()->getRelocationTimer().TUpdate(diff); + if (!grid->getGridInfoRef()->getRelocationTimer().TPassed()) + continue; + + uint32 gx = grid->getX(), gy = grid->getY(); + + CellCoord cell_min(gx * MAX_NUMBER_OF_CELLS, gy * MAX_NUMBER_OF_CELLS); + CellCoord cell_max(cell_min.x_coord + MAX_NUMBER_OF_CELLS, cell_min.y_coord + MAX_NUMBER_OF_CELLS); + + for (uint32 x = cell_min.x_coord; x < cell_max.x_coord; ++x) + { + for (uint32 y = cell_min.y_coord; y < cell_max.y_coord; ++y) + { + uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x; + if (!isCellMarked(cell_id)) + continue; + + CellCoord pair(x, y); + Cell cell(pair); + cell.SetNoCreate(); + + Acore::DelayedUnitRelocation cell_relocation(cell, pair, *this, MAX_VISIBILITY_DISTANCE); + TypeContainerVisitor grid_object_relocation(cell_relocation); + TypeContainerVisitor world_object_relocation(cell_relocation); + Visit(cell, grid_object_relocation); + Visit(cell, world_object_relocation); + } + } + } + + ResetNotifier reset; + TypeContainerVisitor grid_notifier(reset); + TypeContainerVisitor world_notifier(reset); + for (GridRefMgr::iterator i = GridRefMgr::begin(); i != GridRefMgr::end(); ++i) + { + NGridType* grid = i->GetSource(); + + if (grid->GetGridState() != GRID_STATE_ACTIVE) + continue; + + if (!grid->getGridInfoRef()->getRelocationTimer().TPassed()) + continue; + + grid->getGridInfoRef()->getRelocationTimer().TReset(diff, m_VisibilityNotifyPeriod); + + uint32 gx = grid->getX(), gy = grid->getY(); + + CellCoord cell_min(gx * MAX_NUMBER_OF_CELLS, gy * MAX_NUMBER_OF_CELLS); + CellCoord cell_max(cell_min.x_coord + MAX_NUMBER_OF_CELLS, cell_min.y_coord + MAX_NUMBER_OF_CELLS); + + for (uint32 x = cell_min.x_coord; x < cell_max.x_coord; ++x) + { + for (uint32 y = cell_min.y_coord; y < cell_max.y_coord; ++y) + { + uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x; + if (!isCellMarked(cell_id)) + continue; + + CellCoord pair(x, y); + Cell cell(pair); + cell.SetNoCreate(); + Visit(cell, grid_notifier); + Visit(cell, world_notifier); + } + } + } +} + void Map::RemovePlayerFromMap(Player* player, bool remove) { player->getHostileRefMgr().deleteReferences(true); // pussywizard: multithreading crashfix @@ -1000,15 +1055,19 @@ void Map::RemoveFromMap(MotionTransport* obj, bool remove) void Map::PlayerRelocation(Player* player, float x, float y, float z, float o) { + ASSERT(player); + Cell old_cell(player->GetPositionX(), player->GetPositionY()); Cell new_cell(x, y); if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell)) { + LOG_DEBUG("maps", "Player {} relocation grid[{}, {}]cell[{}, {}]->grid[{}, {}]cell[{}, {}]", player->GetName().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + player->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) - EnsureGridLoaded(new_cell); + EnsureGridLoadedForActiveObject(new_cell, player); AddToGrid(player, new_cell); } @@ -1030,10 +1089,15 @@ void Map::CreatureRelocation(Creature* creature, float x, float y, float z, floa if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); - AddCreatureToMoveList(creature); + #ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Creature {} added to moving list from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", creature->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + #endif + AddCreatureToMoveList(creature, x, y, z, o); } else + { RemoveCreatureFromMoveList(creature); + } creature->Relocate(x, y, z, o); if (creature->IsVehicle()) @@ -1052,10 +1116,15 @@ void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); - AddGameObjectToMoveList(go); + #ifdef ACORE_DEBUG + LOG_DEBUG("maps", "GameObject {} added to moving list from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + #endif + AddGameObjectToMoveList(go, x, y, z, o); } else + { RemoveGameObjectFromMoveList(go); + } go->Relocate(x, y, z, o); go->UpdateModelPosition(); @@ -1073,61 +1142,85 @@ void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); - AddDynamicObjectToMoveList(dynObj); + #ifdef ACORE_DEBUG + LOG_DEBUG("maps", "GameObject {} added to moving list from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", dynObj->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + #endif + AddDynamicObjectToMoveList(dynObj, x, y, z, o); } else + { RemoveDynamicObjectFromMoveList(dynObj); + } dynObj->Relocate(x, y, z, o); dynObj->SetPositionDataUpdate(); dynObj->UpdateObjectVisibility(false); } -void Map::AddCreatureToMoveList(Creature* c) +void Map::AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang) { + if (_creatureToMoveLock) //can this happen? + return; + if (c->_moveState == MAP_OBJECT_CELL_MOVE_NONE) _creaturesToMove.push_back(c); - c->_moveState = MAP_OBJECT_CELL_MOVE_ACTIVE; + c->SetNewCellPosition(x, y, z, ang); } void Map::RemoveCreatureFromMoveList(Creature* c) { + if (_creatureToMoveLock) //can this happen? + return; + if (c->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE) c->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; } -void Map::AddGameObjectToMoveList(GameObject* go) +void Map::AddGameObjectToMoveList(GameObject* go, float x, float y, float z, float ang) { + if (_gameObjectsToMoveLock) //can this happen? + return; + if (go->_moveState == MAP_OBJECT_CELL_MOVE_NONE) _gameObjectsToMove.push_back(go); - go->_moveState = MAP_OBJECT_CELL_MOVE_ACTIVE; + go->SetNewCellPosition(x, y, z, ang); } void Map::RemoveGameObjectFromMoveList(GameObject* go) { + if (_gameObjectsToMoveLock) //can this happen? + return; + if (go->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE) go->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; } -void Map::AddDynamicObjectToMoveList(DynamicObject* dynObj) +void Map::AddDynamicObjectToMoveList(DynamicObject* dynObj, float x, float y, float z, float ang) { + if (_dynamicObjectsToMoveLock) //can this happen? + return; + if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_NONE) _dynamicObjectsToMove.push_back(dynObj); - dynObj->_moveState = MAP_OBJECT_CELL_MOVE_ACTIVE; + dynObj->SetNewCellPosition(x, y, z, ang); } void Map::RemoveDynamicObjectFromMoveList(DynamicObject* dynObj) { + if (_dynamicObjectsToMoveLock) //can this happen? + return; + if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE) dynObj->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; } void Map::MoveAllCreaturesInMoveList() { + _creatureToMoveLock = true; for (std::vector::iterator itr = _creaturesToMove.begin(); itr != _creaturesToMove.end(); ++itr) { Creature* c = *itr; - if (c->FindMap() != this) + if (c->FindMap() != this) //pet is teleported to another map continue; if (c->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE) @@ -1140,23 +1233,51 @@ void Map::MoveAllCreaturesInMoveList() if (!c->IsInWorld()) continue; - Cell const& old_cell = c->GetCurrentCell(); - Cell new_cell(c->GetPositionX(), c->GetPositionY()); - - c->RemoveFromGrid(); - if (old_cell.DiffGrid(new_cell)) - EnsureGridLoaded(new_cell); - AddToGrid(c, new_cell); + // do move or do move to respawn or remove creature if previous all fail + if (CreatureCellRelocation(c, Cell(c->_newPosition.m_positionX, c->_newPosition.m_positionY))) + { + // update pos + c->Relocate(c->_newPosition); + if (c->IsVehicle()) + c->GetVehicleKit()->RelocatePassengers(); + //CreatureRelocationNotify(c, new_cell, new_cell.cellCoord()); + c->UpdatePositionData(); + c->UpdateObjectVisibility(false); + } + else + { + // if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid + // creature coordinates will be updated and notifiers send + if (!CreatureRespawnRelocation(c, false)) + { + // ... or unload (if respawn grid also not loaded) +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Creature {} cannot be move to unloaded respawn grid.", c->GetGUID().ToString().c_str()); +#endif + //AddObjectToRemoveList(Pet*) should only be called in Pet::Remove + //This may happen when a player just logs in and a pet moves to a nearby unloaded cell + //To avoid this, we can load nearby cells when player log in + //But this check is always needed to ensure safety + /// @todo pets will disappear if this is outside CreatureRespawnRelocation + //need to check why pet is frequently relocated to an unloaded cell + if (c->IsPet()) + ((Pet*)c)->Remove(PET_SAVE_NOT_IN_SLOT, true); + else + AddObjectToRemoveList(c); + } + } } _creaturesToMove.clear(); + _creatureToMoveLock = false; } void Map::MoveAllGameObjectsInMoveList() { + _gameObjectsToMoveLock = true; for (std::vector::iterator itr = _gameObjectsToMove.begin(); itr != _gameObjectsToMove.end(); ++itr) { GameObject* go = *itr; - if (go->FindMap() != this) + if (go->FindMap() != this) //transport is teleported to another map continue; if (go->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE) @@ -1169,23 +1290,40 @@ void Map::MoveAllGameObjectsInMoveList() if (!go->IsInWorld()) continue; - Cell const& old_cell = go->GetCurrentCell(); - Cell new_cell(go->GetPositionX(), go->GetPositionY()); - - go->RemoveFromGrid(); - if (old_cell.DiffGrid(new_cell)) - EnsureGridLoaded(new_cell); - AddToGrid(go, new_cell); + // do move or do move to respawn or remove creature if previous all fail + if (GameObjectCellRelocation(go, Cell(go->_newPosition.m_positionX, go->_newPosition.m_positionY))) + { + // update pos + go->Relocate(go->_newPosition); + go->UpdateModelPosition(); + go->UpdatePositionData(); + go->UpdateObjectVisibility(false); + } + else + { + // if GameObject can't be move in new cell/grid (not loaded) move it to repawn cell/grid + // GameObject coordinates will be updated and notifiers send + if (!GameObjectRespawnRelocation(go, false)) + { + // ... or unload (if respawn grid also not loaded) +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "GameObject {} cannot be move to unloaded respawn grid.", go->GetGUID().ToString().c_str()); +#endif + AddObjectToRemoveList(go); + } + } } _gameObjectsToMove.clear(); + _gameObjectsToMoveLock = false; } void Map::MoveAllDynamicObjectsInMoveList() { + _dynamicObjectsToMoveLock = true; for (std::vector::iterator itr = _dynamicObjectsToMove.begin(); itr != _dynamicObjectsToMove.end(); ++itr) { DynamicObject* dynObj = *itr; - if (dynObj->FindMap() != this) + if (dynObj->FindMap() != this) //transport is teleported to another map continue; if (dynObj->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE) @@ -1198,60 +1336,344 @@ void Map::MoveAllDynamicObjectsInMoveList() if (!dynObj->IsInWorld()) continue; - Cell const& old_cell = dynObj->GetCurrentCell(); - Cell new_cell(dynObj->GetPositionX(), dynObj->GetPositionY()); - - dynObj->RemoveFromGrid(); - if (old_cell.DiffGrid(new_cell)) - EnsureGridLoaded(new_cell); - AddToGrid(dynObj, new_cell); + // do move or do move to respawn or remove creature if previous all fail + if (DynamicObjectCellRelocation(dynObj, Cell(dynObj->_newPosition.m_positionX, dynObj->_newPosition.m_positionY))) + { + // update pos + dynObj->Relocate(dynObj->_newPosition); + dynObj->UpdatePositionData(); + dynObj->UpdateObjectVisibility(false); + } + else + { +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "DynamicObject {} cannot be moved to unloaded grid.", dynObj->GetGUID().ToString().c_str()); +#endif + } } + _dynamicObjectsToMove.clear(); + _dynamicObjectsToMoveLock = false; } -bool Map::UnloadGrid(NGridType& ngrid) +bool Map::CreatureCellRelocation(Creature* c, Cell new_cell) { - // pussywizard: UnloadGrid only done when whole map is unloaded, no need to worry about moving npcs between grids, etc. + Cell const& old_cell = c->GetCurrentCell(); + if (!old_cell.DiffGrid(new_cell)) // in same grid + { + // if in same cell then none do + if (old_cell.DiffCell(new_cell)) + { +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Creature {} moved in grid[{}, {}] from cell[{}, {}] to cell[{}, {}].", c->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY()); +#endif - const uint32 x = ngrid.getX(); - const uint32 y = ngrid.getY(); + c->RemoveFromGrid(); + AddToGrid(c, new_cell); + } + else + { +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Creature {} moved in same grid[{}, {}]cell[{}, {}].", c->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY()); +#endif + } + return true; + } + + // in diff. grids but active creature + if (c->isActiveObject()) { - ObjectGridCleaner worker; - TypeContainerVisitor visitor(worker); - ngrid.VisitAllGrids(visitor); + EnsureGridLoadedForActiveObject(new_cell, c); + +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Active creature {} moved from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", c->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + + c->RemoveFromGrid(); + AddToGrid(c, new_cell); + + return true; } - RemoveAllObjectsInRemoveList(); + if (c->GetCharmerOrOwnerGUID().IsPlayer()) + EnsureGridLoaded(new_cell); + // in diff. loaded grid normal creature + if (IsGridLoaded(GridCoord(new_cell.GridX(), new_cell.GridY()))) { - ObjectGridUnloader worker; - TypeContainerVisitor visitor(worker); - ngrid.VisitAllGrids(visitor); +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Creature {} moved from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", c->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + + c->RemoveFromGrid(); + EnsureGridCreated(GridCoord(new_cell.GridX(), new_cell.GridY())); + AddToGrid(c, new_cell); + + return true; } - ASSERT(i_objectsToRemove.empty()); + // fail to move: normal creature attempt move to unloaded grid +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Creature {} attempted to move from grid[{}, {}]cell[{}, {}] to unloaded grid[{}, {}]cell[{}, {}].", c->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + return false; +} - delete &ngrid; - setNGrid(nullptr, x, y); +bool Map::GameObjectCellRelocation(GameObject* go, Cell new_cell) +{ + Cell const& old_cell = go->GetCurrentCell(); + if (!old_cell.DiffGrid(new_cell)) // in same grid + { + // if in same cell then none do + if (old_cell.DiffCell(new_cell)) + { +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "GameObject {} moved in grid[{}, {}] from cell[{}, {}] to cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY()); +#endif - int gx = (MAX_NUMBER_OF_GRIDS - 1) - x; - int gy = (MAX_NUMBER_OF_GRIDS - 1) - y; + go->RemoveFromGrid(); + AddToGrid(go, new_cell); + } + else + { +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "GameObject {} moved in same grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY()); +#endif + } - if (i_InstanceId == 0) + return true; + } + + // in diff. grids but active GameObject + if (go->isActiveObject()) { - if (GridMaps[gx][gy]) + EnsureGridLoadedForActiveObject(new_cell, go); + +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Active GameObject {} moved from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + + go->RemoveFromGrid(); + AddToGrid(go, new_cell); + + return true; + } + + // in diff. loaded grid normal GameObject + if (IsGridLoaded(GridCoord(new_cell.GridX(), new_cell.GridY()))) + { +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "GameObject {} moved from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + + go->RemoveFromGrid(); + EnsureGridCreated(GridCoord(new_cell.GridX(), new_cell.GridY())); + AddToGrid(go, new_cell); + + return true; + } + + // fail to move: normal GameObject attempt move to unloaded grid +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "GameObject {} attempted to move from grid[{}, {}]cell[{}, {}] to unloaded grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + return false; +} + +bool Map::DynamicObjectCellRelocation(DynamicObject* go, Cell new_cell) +{ + Cell const& old_cell = go->GetCurrentCell(); + if (!old_cell.DiffGrid(new_cell)) // in same grid + { + // if in same cell then none do + if (old_cell.DiffCell(new_cell)) + { +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "DynamicObject {} moved in grid[{}, {}] from cell[{}, {}] to cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY()); +#endif + + go->RemoveFromGrid(); + AddToGrid(go, new_cell); + } + else { - GridMaps[gx][gy]->unloadData(); - delete GridMaps[gx][gy]; +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "DynamicObject {} moved in same grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY()); +#endif } - // x and y are swapped - VMAP::VMapFactory::createOrGetVMapMgr()->unloadMap(GetId(), gx, gy); - MMAP::MMapFactory::createOrGetMMapMgr()->unloadMap(GetId(), gx, gy); + + return true; } - GridMaps[gx][gy] = nullptr; + // in diff. grids but active GameObject + if (go->isActiveObject()) + { + EnsureGridLoadedForActiveObject(new_cell, go); + +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Active DynamicObject {} moved from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + + go->RemoveFromGrid(); + AddToGrid(go, new_cell); + + return true; + } + + // in diff. loaded grid normal GameObject + if (IsGridLoaded(GridCoord(new_cell.GridX(), new_cell.GridY()))) + { +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "DynamicObject {} moved from grid[{}, {}]cell[{}, {}] to grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + + go->RemoveFromGrid(); + EnsureGridCreated(GridCoord(new_cell.GridX(), new_cell.GridY())); + AddToGrid(go, new_cell); + + return true; + } + + // fail to move: normal GameObject attempt move to unloaded grid +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "DynamicObject {} attempted to move from grid[{}, {}]cell[{}, {}] to unloaded grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + return false; +} + +bool Map::CreatureRespawnRelocation(Creature* c, bool diffGridOnly) +{ + float resp_x, resp_y, resp_z, resp_o; + c->GetRespawnPosition(resp_x, resp_y, resp_z, &resp_o); + Cell resp_cell(resp_x, resp_y); + + //creature will be unloaded with grid + if (diffGridOnly && !c->GetCurrentCell().DiffGrid(resp_cell)) + return true; + + c->CombatStop(); + c->GetMotionMaster()->Clear(); + +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "Creature {} moved from grid[{}, {}]cell[{}, {}] to respawn grid[{}, {}]cell[{}, {}].", c->GetGUID().ToString().c_str(), c->GetCurrentCell().GridX(), c->GetCurrentCell().GridY(), c->GetCurrentCell().CellX(), c->GetCurrentCell().CellY(), resp_cell.GridX(), resp_cell.GridY(), resp_cell.CellX(), resp_cell.CellY()); +#endif + + // teleport it to respawn point (like normal respawn if player see) + if (CreatureCellRelocation(c, resp_cell)) + { + c->Relocate(resp_x, resp_y, resp_z, resp_o); + c->GetMotionMaster()->Initialize(); // prevent possible problems with default move generators + //CreatureRelocationNotify(c, resp_cell, resp_cell.GetCellCoord()); + c->UpdatePositionData(); + c->UpdateObjectVisibility(false); + return true; + } + return false; +} + +bool Map::GameObjectRespawnRelocation(GameObject* go, bool diffGridOnly) +{ + float resp_x, resp_y, resp_z, resp_o; + go->GetRespawnPosition(resp_x, resp_y, resp_z, &resp_o); + Cell resp_cell(resp_x, resp_y); + + //GameObject will be unloaded with grid + if (diffGridOnly && !go->GetCurrentCell().DiffGrid(resp_cell)) + return true; + +#ifdef ACORE_DEBUG + LOG_DEBUG("maps", "GameObject {} moved from grid[{}, {}]cell[{}, {}] to respawn grid[{}, {}]cell[{}, {}].", go->GetGUID().ToString().c_str(), go->GetCurrentCell().GridX(), go->GetCurrentCell().GridY(), go->GetCurrentCell().CellX(), go->GetCurrentCell().CellY(), resp_cell.GridX(), resp_cell.GridY(), resp_cell.CellX(), resp_cell.CellY()); +#endif + + // teleport it to respawn point (like normal respawn if player see) + if (GameObjectCellRelocation(go, resp_cell)) + { + go->Relocate(resp_x, resp_y, resp_z, resp_o); + go->UpdatePositionData(); + go->UpdateObjectVisibility(false); + return true; + } + + return false; +} + +bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll) +{ + const uint32 x = ngrid.getX(); + const uint32 y = ngrid.getY(); + + { + if (!unloadAll) + { + //pets, possessed creatures (must be active), transport passengers + if (ngrid.GetWorldObjectCountInNGrid()) + return false; + + if (ActiveObjectsNearGrid(ngrid)) + return false; + } + + LOG_DEBUG("maps", "Unloading grid[{}, {}] for map {}", x, y, GetId()); + + if (!unloadAll) + { + // Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids + // Must know real mob position before move + MoveAllCreaturesInMoveList(); + MoveAllGameObjectsInMoveList(); + + // move creatures to respawn grids if this is diff.grid or to remove list + ObjectGridEvacuator worker; + TypeContainerVisitor visitor(worker); + ngrid.VisitAllGrids(visitor); + + // Finish creature moves, remove and delete all creatures with delayed remove before unload + MoveAllCreaturesInMoveList(); + MoveAllGameObjectsInMoveList(); + } + + { + ObjectGridCleaner worker; + TypeContainerVisitor visitor(worker); + ngrid.VisitAllGrids(visitor); + } + + RemoveAllObjectsInRemoveList(); + + { + ObjectGridUnloader worker; + TypeContainerVisitor visitor(worker); + ngrid.VisitAllGrids(visitor); + } + + ASSERT(i_objectsToRemove.empty()); + + delete& ngrid; + setNGrid(nullptr, x, y); + } + int gx = (MAX_NUMBER_OF_GRIDS - 1) - x; + int gy = (MAX_NUMBER_OF_GRIDS - 1) - y; + + // delete grid map, but don't delete if it is from parent map (and thus only reference) + //+++if (GridMaps[gx][gy]) don't check for GridMaps[gx][gy], we might have to unload vmaps + { + if (i_InstanceId == 0) + { + if (GridMaps[gx][gy]) + { + GridMaps[gx][gy]->unloadData(); + delete GridMaps[gx][gy]; + } + VMAP::VMapFactory::createOrGetVMapMgr()->unloadMap(GetId(), gx, gy); + MMAP::MMapFactory::createOrGetMMapMgr()->unloadMap(GetId(), gx, gy); + } + else + ((MapInstanced*)m_parentMap)->RemoveGridMapReference(GridCoord(gx, gy)); + + GridMaps[gx][gy] = nullptr; + } LOG_DEBUG("maps", "Unloading grid[{}, {}] for map {} finished", x, y, GetId()); return true; } @@ -1283,7 +1705,7 @@ void Map::UnloadAll() { NGridType& grid(*i->GetSource()); ++i; - UnloadGrid(grid); // deletes the grid and removes it from the GridRefMgr + UnloadGrid(grid, true); // deletes the grid and removes it from the GridRefMgr } // pussywizard: crashfix, some npc can be left on transport (not a default passenger) @@ -2639,6 +3061,20 @@ void Map::DelayedUpdate(const uint32 t_diff) } RemoveAllObjectsInRemoveList(); + + // Don't unload grids if it's battleground, since we may have manually added GOs, creatures, those doesn't load from DB at grid re-load ! + // This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended + if (!IsBattlegroundOrArena()) + { + for (GridRefMgr::iterator i = GridRefMgr::begin(); i != GridRefMgr::end();) + { + NGridType* grid = i->GetSource(); + GridInfo* info = i->GetSource()->getGridInfoRef(); + ++i; // The update might delete the map and we need the next map before the iterator gets invalid + ASSERT(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE); + si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, t_diff); + } + } } void Map::AddObjectToRemoveList(WorldObject* obj) @@ -2698,7 +3134,6 @@ void Map::RemoveAllObjectsInRemoveList() { std::unordered_set::iterator itr = i_objectsToRemove.begin(); WorldObject* obj = *itr; - i_objectsToRemove.erase(itr); switch (obj->GetTypeId()) { @@ -2730,6 +3165,8 @@ void Map::RemoveAllObjectsInRemoveList() LOG_ERROR("maps", "Non-grid object (TypeId: {}) is in grid object remove list, ignored.", obj->GetTypeId()); break; } + + i_objectsToRemove.erase(itr); } //LOG_DEBUG("maps", "Object remover 2 check."); @@ -2750,6 +3187,43 @@ void Map::SendToPlayers(WorldPacket const* data) const itr->GetSource()->GetSession()->SendPacket(data); } +bool Map::ActiveObjectsNearGrid(NGridType const& ngrid) const +{ + CellCoord cell_min(ngrid.getX() * MAX_NUMBER_OF_CELLS, ngrid.getY() * MAX_NUMBER_OF_CELLS); + CellCoord cell_max(cell_min.x_coord + MAX_NUMBER_OF_CELLS, cell_min.y_coord + MAX_NUMBER_OF_CELLS); + + //we must find visible range in cells so we unload only non-visible cells... + float viewDist = GetVisibilityRange(); + int cell_range = (int)ceilf(viewDist / SIZE_OF_GRID_CELL) + 1; + + cell_min.dec_x(cell_range); + cell_min.dec_y(cell_range); + cell_max.inc_x(cell_range); + cell_max.inc_y(cell_range); + + for (MapRefMgr::const_iterator iter = m_mapRefMgr.begin(); iter != m_mapRefMgr.end(); ++iter) + { + Player* player = iter->GetSource(); + + CellCoord p = Acore::ComputeCellCoord(player->GetPositionX(), player->GetPositionY()); + if ((cell_min.x_coord <= p.x_coord && p.x_coord <= cell_max.x_coord) && + (cell_min.y_coord <= p.y_coord && p.y_coord <= cell_max.y_coord)) + return true; + } + + for (ActiveNonPlayers::const_iterator iter = m_activeNonPlayers.begin(); iter != m_activeNonPlayers.end(); ++iter) + { + WorldObject* obj = *iter; + + CellCoord p = Acore::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); + if ((cell_min.x_coord <= p.x_coord && p.x_coord <= cell_max.x_coord) && + (cell_min.y_coord <= p.y_coord && p.y_coord <= cell_max.y_coord)) + return true; + } + + return false; +} + template void Map::AddToActive(T* obj) { @@ -2760,6 +3234,22 @@ template <> void Map::AddToActive(Creature* c) { AddToActiveHelper(c); + + // also not allow unloading spawn grid to prevent creating creature clone at load + if (!c->IsPet() && c->GetSpawnId()) + { + float x, y, z; + c->GetRespawnPosition(x, y, z); + GridCoord p = Acore::ComputeGridCoord(x, y); + if (getNGrid(p.x_coord, p.y_coord)) + getNGrid(p.x_coord, p.y_coord)->incUnloadActiveLock(); + else + { + GridCoord p2 = Acore::ComputeGridCoord(c->GetPositionX(), c->GetPositionY()); + LOG_ERROR("maps", "Active creature {} added to grid[{}, {}] but spawn grid[{}, {}] was not loaded.", + c->GetGUID().ToString().c_str(), p.x_coord, p.y_coord, p2.x_coord, p2.y_coord); + } + } } template<> @@ -2784,6 +3274,22 @@ template <> void Map::RemoveFromActive(Creature* c) { RemoveFromActiveHelper(c); + + // also allow unloading spawn grid + if (!c->IsPet() && c->GetSpawnId()) + { + float x, y, z; + c->GetRespawnPosition(x, y, z); + GridCoord p = Acore::ComputeGridCoord(x, y); + if (getNGrid(p.x_coord, p.y_coord)) + getNGrid(p.x_coord, p.y_coord)->decUnloadActiveLock(); + else + { + GridCoord p2 = Acore::ComputeGridCoord(c->GetPositionX(), c->GetPositionY()); + LOG_ERROR("maps", "Active creature {} removed from grid[{}, {}] but spawn grid[{}, {}] was not loaded.", + c->GetGUID().ToString().c_str(), p.x_coord, p.y_coord, p2.x_coord, p2.y_coord); + } + } } template<> @@ -2810,8 +3316,8 @@ template void Map::RemoveFromMap(DynamicObject*, bool); /* ******* Dungeon Instance Maps ******* */ -InstanceMap::InstanceMap(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) - : Map(id, InstanceId, SpawnMode, _parent), +InstanceMap::InstanceMap(uint32 id, std::chrono::seconds expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent) + : Map(id, expiry, InstanceId, SpawnMode, _parent), m_resetAfterUnload(false), m_unloadWhenEmpty(false), instance_data(nullptr), i_script_id(0) { @@ -2840,6 +3346,7 @@ void InstanceMap::InitVisibilityDistance() { //init visibility distance for instances m_VisibleDistance = World::GetMaxVisibleDistanceInInstances(); + m_VisibilityNotifyPeriod = World::GetVisibilityNotifyPeriodInInstances(); // pussywizard: this CAN NOT exceed MAX_VISIBILITY_DISTANCE switch (GetId()) @@ -3219,8 +3726,8 @@ uint32 InstanceMap::GetMaxResetDelay() const /* ******* Battleground Instance Maps ******* */ -BattlegroundMap::BattlegroundMap(uint32 id, uint32 InstanceId, Map* _parent, uint8 spawnMode) - : Map(id, InstanceId, spawnMode, _parent), m_bg(nullptr) +BattlegroundMap::BattlegroundMap(uint32 id, std::chrono::seconds expiry, uint32 InstanceId, Map* _parent, uint8 spawnMode) + : Map(id, expiry, InstanceId, spawnMode, _parent), m_bg(nullptr) { //lets initialize visibility distance for BG/Arenas BattlegroundMap::InitVisibilityDistance(); @@ -3240,6 +3747,7 @@ void BattlegroundMap::InitVisibilityDistance() { //init visibility distance for BG/Arenas m_VisibleDistance = World::GetMaxVisibleDistanceInBGArenas(); + m_VisibilityNotifyPeriod = World::GetVisibilityNotifyPeriodInBGArenas(); if (IsBattleArena()) // pussywizard: start with 30yd visibility range on arenas to ensure players can't get informations about the opponents in any way m_VisibleDistance = 30.0f; diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 0bf6b9c184d71f..9b607a1afbc90f 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -71,7 +71,6 @@ namespace VMAP namespace Acore { struct ObjectUpdater; - struct LargeObjectUpdater; } struct ScriptAction @@ -312,7 +311,7 @@ class Map : public GridRefMgr { friend class MapReference; public: - Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent = nullptr); + Map(uint32 id, std::chrono::seconds, uint32 InstanceId, uint8 SpawnMode, Map* _parent = nullptr); ~Map() override; [[nodiscard]] MapEntry const* GetEntry() const { return i_mapEntry; } @@ -337,13 +336,7 @@ class Map : public GridRefMgr template void RemoveFromMap(T*, bool); void VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor& gridVisitor, - TypeContainerVisitor& worldVisitor, - TypeContainerVisitor& largeGridVisitor, - TypeContainerVisitor& largeWorldVisitor); - void VisitNearbyCellsOfPlayer(Player* player, TypeContainerVisitor& gridVisitor, - TypeContainerVisitor& worldVisitor, - TypeContainerVisitor& largeGridVisitor, - TypeContainerVisitor& largeWorldVisitor); + TypeContainerVisitor& worldVisitor); virtual void Update(const uint32, const uint32, bool thread = true); @@ -362,7 +355,7 @@ class Map : public GridRefMgr [[nodiscard]] bool IsRemovalGrid(float x, float y) const { GridCoord p = Acore::ComputeGridCoord(x, y); - return !getNGrid(p.x_coord, p.y_coord); + return !getNGrid(p.x_coord, p.y_coord) || getNGrid(p.x_coord, p.y_coord)->GetGridState() == GRID_STATE_REMOVAL; } [[nodiscard]] bool IsGridLoaded(float x, float y) const @@ -370,13 +363,26 @@ class Map : public GridRefMgr return IsGridLoaded(Acore::ComputeGridCoord(x, y)); } + bool GetUnloadLock(GridCoord const& p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadLock(); } + void SetUnloadLock(GridCoord const& p, bool on) { getNGrid(p.x_coord, p.y_coord)->setUnloadExplicitLock(on); } void LoadGrid(float x, float y); void LoadAllCells(); - bool UnloadGrid(NGridType& ngrid); + bool UnloadGrid(NGridType& ngrid, bool pForce); + void GridMarkNoUnload(uint32 x, uint32 y); + void GridUnmarkNoUnload(uint32 x, uint32 y); virtual void UnloadAll(); + void ResetGridExpiry(NGridType& grid, float factor = 1) const + { + grid.ResetTimeTracker(std::chrono::duration_cast(i_gridExpiry * factor)); + } + + [[nodiscard]] std::chrono::seconds GetGridExpiry(void) const { return i_gridExpiry; } [[nodiscard]] uint32 GetId() const { return i_mapEntry->MapID; } + static void InitStateMachine(); + static void DeleteStateMachine(); + static bool ExistMap(uint32 mapid, int gx, int gy); static bool ExistVMap(uint32 mapid, int gx, int gy); @@ -415,6 +421,10 @@ class Map : public GridRefMgr void RemoveAllObjectsInRemoveList(); virtual void RemoveAllPlayers(); + // used only in MoveAllCreaturesInMoveList and ObjectGridUnloader + bool CreatureRespawnRelocation(Creature* c, bool diffGridOnly); + bool GameObjectRespawnRelocation(GameObject* go, bool diffGridOnly); + [[nodiscard]] uint32 GetInstanceId() const { return i_InstanceId; } [[nodiscard]] uint8 GetSpawnMode() const { return (i_spawnMode); } @@ -468,12 +478,10 @@ class Map : public GridRefMgr void resetMarkedCells() { marked_cells.reset(); } bool isCellMarked(uint32 pCellId) { return marked_cells.test(pCellId); } void markCell(uint32 pCellId) { marked_cells.set(pCellId); } - void resetMarkedCellsLarge() { marked_cells_large.reset(); } - bool isCellMarkedLarge(uint32 pCellId) { return marked_cells_large.test(pCellId); } - void markCellLarge(uint32 pCellId) { marked_cells_large.set(pCellId); } [[nodiscard]] bool HavePlayers() const { return !m_mapRefMgr.IsEmpty(); } [[nodiscard]] uint32 GetPlayersCountExceptGMs() const; + [[nodiscard]] bool ActiveObjectsNearGrid(NGridType const& ngrid) const; void AddWorldObject(WorldObject* obj) { i_worldObjects.insert(obj); } void RemoveWorldObject(WorldObject* obj) { i_worldObjects.erase(obj); } @@ -660,20 +668,30 @@ class Map : public GridRefMgr // Load MMap Data void LoadMMap(int gx, int gy); + bool CreatureCellRelocation(Creature* creature, Cell new_cell); + bool GameObjectCellRelocation(GameObject* go, Cell new_cell); + bool DynamicObjectCellRelocation(DynamicObject* go, Cell new_cell); + template void InitializeObject(T* obj); - void AddCreatureToMoveList(Creature* c); + void AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang); void RemoveCreatureFromMoveList(Creature* c); - void AddGameObjectToMoveList(GameObject* go); + void AddGameObjectToMoveList(GameObject* go, float x, float y, float z, float ang); void RemoveGameObjectFromMoveList(GameObject* go); - void AddDynamicObjectToMoveList(DynamicObject* go); + void AddDynamicObjectToMoveList(DynamicObject* go, float x, float y, float z, float ang); void RemoveDynamicObjectFromMoveList(DynamicObject* go); + bool _creatureToMoveLock; std::vector _creaturesToMove; + + bool _gameObjectsToMoveLock; std::vector _gameObjectsToMove; + + bool _dynamicObjectsToMoveLock; std::vector _dynamicObjectsToMove; [[nodiscard]] bool IsGridLoaded(const GridCoord&) const; void EnsureGridCreated_i(const GridCoord&); + void EnsureGridLoadedForActiveObject(Cell const&, WorldObject* object); void buildNGridLinkage(NGridType* pNGridType) { pNGridType->link(this); } @@ -695,6 +713,8 @@ class Map : public GridRefMgr void SendObjectUpdates(); protected: + void SetUnloadReferenceLock(GridCoord const& p, bool on) { getNGrid(p.x_coord, p.y_coord)->setUnloadReferenceLock(on); } + std::mutex Lock; std::mutex GridLock; std::shared_mutex MMapLock; @@ -710,6 +730,8 @@ class Map : public GridRefMgr MapRefMgr m_mapRefMgr; MapRefMgr::iterator m_mapRefIter; + int32 m_VisibilityNotifyPeriod; + typedef std::set ActiveNonPlayers; ActiveNonPlayers m_activeNonPlayers; ActiveNonPlayers::iterator m_activeNonPlayersIter; @@ -728,6 +750,8 @@ class Map : public GridRefMgr void _ScriptProcessDoor(Object* source, Object* target, const ScriptInfo* scriptInfo) const; GameObject* _FindGameObject(WorldObject* pWorldObject, ObjectGuid::LowType guid) const; + std::chrono::seconds i_gridExpiry; + //used for fast base_map (e.g. MapInstanced class object) search for //InstanceMaps and BattlegroundMaps... Map* m_parentMap; @@ -735,7 +759,10 @@ class Map : public GridRefMgr NGridType* i_grids[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; GridMap* GridMaps[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; std::bitset marked_cells; - std::bitset marked_cells_large; + + //these functions used to process player/mob aggro reactions and + //visibility calculations. Highly optimized for massive calculations + void ProcessRelocationNotifies(uint32 diff); bool i_scriptLock; std::unordered_set i_objectsToRemove; @@ -812,7 +839,7 @@ enum InstanceResetMethod class InstanceMap : public Map { public: - InstanceMap(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent); + InstanceMap(uint32 id, std::chrono::seconds, uint32 InstanceId, uint8 SpawnMode, Map* _parent); ~InstanceMap() override; bool AddPlayerToMap(Player*) override; void RemovePlayerFromMap(Player*, bool) override; @@ -846,7 +873,7 @@ class InstanceMap : public Map class BattlegroundMap : public Map { public: - BattlegroundMap(uint32 id, uint32 InstanceId, Map* _parent, uint8 spawnMode); + BattlegroundMap(uint32 id, std::chrono::seconds, uint32 InstanceId, Map* _parent, uint8 spawnMode); ~BattlegroundMap() override; bool AddPlayerToMap(Player*) override; diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp index 12c6acf6b5e660..2ce5bbfbc108e4 100644 --- a/src/server/game/Maps/MapInstanced.cpp +++ b/src/server/game/Maps/MapInstanced.cpp @@ -26,10 +26,10 @@ #include "ScriptMgr.h" #include "VMapFactory.h" -MapInstanced::MapInstanced(uint32 id) : Map(id, 0, DUNGEON_DIFFICULTY_NORMAL) +MapInstanced::MapInstanced(uint32 id, std::chrono::seconds expiry) : Map(id, expiry, 0, DUNGEON_DIFFICULTY_NORMAL) { - // initialize instanced maps list - m_InstancedMaps.clear(); + // fill with zero + memset(&GridMapReference, 0, MAX_NUMBER_OF_GRIDS * MAX_NUMBER_OF_GRIDS * sizeof(uint16)); } void MapInstanced::InitVisibilityDistance() @@ -203,7 +203,7 @@ InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave* save, LOG_DEBUG("maps", "MapInstanced::CreateInstance: {} map instance {} for {} created with difficulty {}", save ? "" : "new ", InstanceId, GetId(), difficulty ? "heroic" : "normal"); - InstanceMap* map = new InstanceMap(GetId(), InstanceId, difficulty, this); + InstanceMap* map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty, this); ASSERT(map->IsDungeon()); map->LoadRespawnTimes(); @@ -237,7 +237,7 @@ BattlegroundMap* MapInstanced::CreateBattleground(uint32 InstanceId, Battlegroun else spawnMode = REGULAR_DIFFICULTY; - BattlegroundMap* map = new BattlegroundMap(GetId(), InstanceId, this, spawnMode); + BattlegroundMap* map = new BattlegroundMap(GetId(), GetGridExpiry(), InstanceId, this, spawnMode); ASSERT(map->IsBattlegroundOrArena()); map->SetBG(bg); bg->SetBgMap(map); diff --git a/src/server/game/Maps/MapInstanced.h b/src/server/game/Maps/MapInstanced.h index f08c5687acbd4e..fd86b8fa612973 100644 --- a/src/server/game/Maps/MapInstanced.h +++ b/src/server/game/Maps/MapInstanced.h @@ -28,7 +28,7 @@ class MapInstanced : public Map public: using InstancedMaps = std::unordered_map; - MapInstanced(uint32 id); + MapInstanced(uint32 id, std::chrono::seconds expiry); ~MapInstanced() override {} // functions overwrite Map versions @@ -46,6 +46,19 @@ class MapInstanced : public Map } bool DestroyInstance(InstancedMaps::iterator& itr); + void AddGridMapReference(GridCoord const& p) + { + ++GridMapReference[p.x_coord][p.y_coord]; + SetUnloadReferenceLock(GridCoord((MAX_NUMBER_OF_GRIDS - 1) - p.x_coord, (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord), true); + } + + void RemoveGridMapReference(GridCoord const& p) + { + --GridMapReference[p.x_coord][p.y_coord]; + if (!GridMapReference[p.x_coord][p.y_coord]) + SetUnloadReferenceLock(GridCoord((MAX_NUMBER_OF_GRIDS - 1) - p.x_coord, (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord), false); + } + InstancedMaps& GetInstancedMaps() { return m_InstancedMaps; } void InitVisibilityDistance() override; @@ -54,5 +67,7 @@ class MapInstanced : public Map BattlegroundMap* CreateBattleground(uint32 InstanceId, Battleground* bg); InstancedMaps m_InstancedMaps; + + uint16 GridMapReference[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; }; #endif diff --git a/src/server/game/Maps/MapMgr.cpp b/src/server/game/Maps/MapMgr.cpp index 9e89d64e219daa..980b7898fd9737 100644 --- a/src/server/game/Maps/MapMgr.cpp +++ b/src/server/game/Maps/MapMgr.cpp @@ -36,6 +36,7 @@ MapMgr::MapMgr() { + i_gridCleanUpDelay = sWorld->getIntConfig(CONFIG_INTERVAL_GRIDCLEAN); i_timer[3].SetInterval(sWorld->getIntConfig(CONFIG_INTERVAL_MAPUPDATE)); mapUpdateStep = 0; _nextInstanceId = 0; @@ -53,6 +54,8 @@ MapMgr* MapMgr::instance() void MapMgr::Initialize() { + Map::InitStateMachine(); + int num_threads(sWorld->getIntConfig(CONFIG_NUMTHREADS)); // Start mtmaps if needed @@ -81,10 +84,10 @@ Map* MapMgr::CreateBaseMap(uint32 id) ASSERT(entry); if (entry->Instanceable()) - map = new MapInstanced(id); + map = new MapInstanced(id, std::chrono::seconds(i_gridCleanUpDelay)); else { - map = new Map(id, 0, REGULAR_DIFFICULTY); + map = new Map(id, std::chrono::seconds(i_gridCleanUpDelay), 0, REGULAR_DIFFICULTY); map->LoadRespawnTimes(); map->LoadCorpseData(); } @@ -333,6 +336,8 @@ void MapMgr::UnloadAll() if (m_updater.activated()) m_updater.deactivate(); + + Map::DeleteStateMachine(); } void MapMgr::GetNumInstances(uint32& dungeons, uint32& battlegrounds, uint32& arenas) diff --git a/src/server/game/Maps/MapMgr.h b/src/server/game/Maps/MapMgr.h index 828fe548eb5f0f..f8f86e78e0b942 100644 --- a/src/server/game/Maps/MapMgr.h +++ b/src/server/game/Maps/MapMgr.h @@ -24,6 +24,7 @@ #include "MapInstanced.h" #include "MapUpdater.h" #include "Object.h" +#include "GridStates.h" #include @@ -73,6 +74,14 @@ class MapMgr void Initialize(void); void Update(uint32); + void SetGridCleanUpDelay(uint32 t) + { + if (t < MIN_GRID_DELAY) + i_gridCleanUpDelay = MIN_GRID_DELAY; + else + i_gridCleanUpDelay = t; + } + void SetMapUpdateInterval(uint32 t) { if (t < MIN_MAP_UPDATE_DELAY) @@ -170,6 +179,7 @@ class MapMgr MapMgr& operator=(const MapMgr&); std::mutex Lock; + uint32 i_gridCleanUpDelay; MapMapType i_maps; IntervalTimer i_timer[4]; // continents, bgs/arenas, instances, total from the beginning uint8 mapUpdateStep; diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 27a3b8971f82a6..ef96f53f430691 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1602,8 +1602,8 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); } - target->UpdateObjectVisibility(target->GetTypeId() == TYPEID_PLAYER || target->GetOwnerGUID().IsPlayer() || target->GetMap()->Instanceable(), true); - target->bRequestForcedVisibilityUpdate = false; + if (target->IsInWorld()) + target->UpdateObjectVisibility(); } void AuraEffect::HandleModStealthDetect(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -1676,8 +1676,8 @@ void AuraEffect::HandleModStealth(AuraApplication const* aurApp, uint8 mode, boo target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); } - target->UpdateObjectVisibility(target->GetTypeId() == TYPEID_PLAYER || target->GetOwnerGUID().IsPlayer() || target->GetMap()->Instanceable(), true); - target->bRequestForcedVisibilityUpdate = false; + if (target->IsInWorld()) + target->UpdateObjectVisibility(); } void AuraEffect::HandleModStealthLevel(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -1841,7 +1841,6 @@ void AuraEffect::HandlePhase(AuraApplication const* aurApp, uint8 mode, bool app if (!target->GetMap()->Instanceable()) { target->UpdateObjectVisibility(false); - target->m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f); } else target->UpdateObjectVisibility(); diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index f035470e1b12ee..addd4069f0c6ce 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -208,6 +208,7 @@ enum WorldFloatConfigs enum WorldIntConfigs { CONFIG_COMPRESSION = 0, + CONFIG_INTERVAL_GRIDCLEAN, CONFIG_INTERVAL_MAPUPDATE, CONFIG_INTERVAL_CHANGEWEATHER, CONFIG_INTERVAL_DISCONNECT_TOLERANCE, diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index eb4785b61b6bcb..951d327254d26e 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -109,6 +109,10 @@ float World::_maxVisibleDistanceOnContinents = DEFAULT_VISIBILITY_DISTANCE; float World::_maxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE; float World::_maxVisibleDistanceInBGArenas = DEFAULT_VISIBILITY_BGARENAS; +int32 World::m_visibility_notify_periodOnContinents = DEFAULT_VISIBILITY_NOTIFY_PERIOD; +int32 World::m_visibility_notify_periodInInstances = DEFAULT_VISIBILITY_NOTIFY_PERIOD; +int32 World::m_visibility_notify_periodInBGArenas = DEFAULT_VISIBILITY_NOTIFY_PERIOD; + Realm realm; /// World constructor @@ -666,6 +670,14 @@ void World::LoadConfigSettings(bool reload) LOG_ERROR("server.loading", "PlayerSave.Stats.MinLevel ({}) must be in range 0..80. Using default, do not save character stats (0).", _int_configs[CONFIG_MIN_LEVEL_STAT_SAVE]); _int_configs[CONFIG_MIN_LEVEL_STAT_SAVE] = 0; } + _int_configs[CONFIG_INTERVAL_GRIDCLEAN] = sConfigMgr->GetOption("GridCleanUpDelay", 5 * MINUTE * IN_MILLISECONDS); + if (_int_configs[CONFIG_INTERVAL_GRIDCLEAN] < MIN_GRID_DELAY) + { + LOG_ERROR("server.loading", "GridCleanUpDelay (%i) must be greater %u. Use this minimal value.", _int_configs[CONFIG_INTERVAL_GRIDCLEAN], MIN_GRID_DELAY); + _int_configs[CONFIG_INTERVAL_GRIDCLEAN] = MIN_GRID_DELAY; + } + if (reload) + sMapMgr->SetGridCleanUpDelay(_int_configs[CONFIG_INTERVAL_GRIDCLEAN]); _int_configs[CONFIG_INTERVAL_MAPUPDATE] = sConfigMgr->GetOption("MapUpdateInterval", 10); if (_int_configs[CONFIG_INTERVAL_MAPUPDATE] < MIN_MAP_UPDATE_DELAY) @@ -1260,6 +1272,10 @@ void World::LoadConfigSettings(bool reload) _maxVisibleDistanceInBGArenas = MAX_VISIBILITY_DISTANCE; } + m_visibility_notify_periodOnContinents = sConfigMgr->GetOption("Visibility.Notify.Period.OnContinents", DEFAULT_VISIBILITY_NOTIFY_PERIOD); + m_visibility_notify_periodInInstances = sConfigMgr->GetOption("Visibility.Notify.Period.InInstances", DEFAULT_VISIBILITY_NOTIFY_PERIOD); + m_visibility_notify_periodInBGArenas = sConfigMgr->GetOption("Visibility.Notify.Period.InBGArenas", DEFAULT_VISIBILITY_NOTIFY_PERIOD); + ///- Load the CharDelete related config options _int_configs[CONFIG_CHARDELETE_METHOD] = sConfigMgr->GetOption("CharDelete.Method", 0); _int_configs[CONFIG_CHARDELETE_MIN_LEVEL] = sConfigMgr->GetOption("CharDelete.MinLevel", 0); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index de373358a63d3f..68a349969db9f9 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -322,6 +322,10 @@ class World: public IWorld static float GetMaxVisibleDistanceInInstances() { return _maxVisibleDistanceInInstances; } static float GetMaxVisibleDistanceInBGArenas() { return _maxVisibleDistanceInBGArenas; } + static int32 GetVisibilityNotifyPeriodOnContinents() { return m_visibility_notify_periodOnContinents; } + static int32 GetVisibilityNotifyPeriodInInstances() { return m_visibility_notify_periodInInstances; } + static int32 GetVisibilityNotifyPeriodInBGArenas() { return m_visibility_notify_periodInBGArenas; } + // our: needed for arena spectator subscriptions uint32 GetNextWhoListUpdateDelaySecs() override; @@ -409,6 +413,10 @@ class World: public IWorld static float _maxVisibleDistanceInInstances; static float _maxVisibleDistanceInBGArenas; + static int32 m_visibility_notify_periodOnContinents; + static int32 m_visibility_notify_periodInInstances; + static int32 m_visibility_notify_periodInBGArenas; + std::string _realmName; // CLI command holder to be thread safe diff --git a/src/server/scripts/Events/midsummer.cpp b/src/server/scripts/Events/midsummer.cpp index 8c9b251bb1d6bf..1a26028846b9eb 100644 --- a/src/server/scripts/Events/midsummer.cpp +++ b/src/server/scripts/Events/midsummer.cpp @@ -145,8 +145,6 @@ struct npc_midsummer_torch_target : public ScriptedAI int8 num = urand(0, posVec.size() - 1); Position pos; pos.Relocate(posVec.at(num)); - me->m_last_notify_position.Relocate(0.0f, 0.0f, 0.0f); - me->m_last_notify_mstime = GameTime::GetGameTimeMS().count() + 10000; me->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); } diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp index 726e2523bfadb1..4817b8d86171cc 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp @@ -443,8 +443,6 @@ class spell_alar_ember_blast_death : public SpellScriptLoader GetUnitOwner()->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); GetUnitOwner()->SetStandState(UNIT_STAND_STATE_DEAD); - GetUnitOwner()->m_last_notify_position.Relocate(0.0f, 0.0f, 0.0f); - GetUnitOwner()->m_delayed_unit_relocation_timer = 1000; } void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)