diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 7a7025296..c3f2d97a5 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -371,7 +371,7 @@ void Creature::RemoveFromWorld() void Creature::DisappearAndDie() { - DestroyForNearbyPlayers(); + DestroyForVisiblePlayers(); //SetVisibility(VISIBILITY_OFF); //ObjectAccessor::UpdateObjectVisibility(this); if (IsAlive()) @@ -408,7 +408,7 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool skipVisibility) setDeathState(DeathState::Dead); RemoveAllAuras(); if (!skipVisibility) // pussywizard - DestroyForNearbyPlayers(); // pussywizard: previous UpdateObjectVisibility() + DestroyForVisiblePlayers(); // pussywizard: previous UpdateObjectVisibility() loot.clear(); uint32 respawnDelay = m_respawnDelay; if (IsAIEnabled) diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 70544335d..ad0231a2b 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -893,7 +893,7 @@ void GameObject::Update(uint32 diff) if (!m_spawnedByDefault) { m_respawnTime = 0; - DestroyForNearbyPlayers(); // xinef: old UpdateObjectVisibility(); + DestroyForVisiblePlayers(); // xinef: old UpdateObjectVisibility(); return; } @@ -904,7 +904,7 @@ void GameObject::Update(uint32 diff) if (GetMap()->IsDungeon()) SaveRespawnTime(); - DestroyForNearbyPlayers(); // xinef: old UpdateObjectVisibility(); + DestroyForVisiblePlayers(); // xinef: old UpdateObjectVisibility(); break; } } diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 370ff04a4..9b58746e9 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -1150,7 +1150,7 @@ bool Item::IsBindedNotWith(Player const* player) const return true; } -void Item::BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&) +void Item::BuildUpdate(UpdateDataMapType& data_map) { if (Player* owner = GetOwner()) BuildFieldsUpdate(owner, data_map); diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 6fdc2f509..a4ce99fdd 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -357,7 +357,7 @@ public: void ClearSoulboundTradeable(Player* currentOwner); bool CheckSoulboundTradeExpire(); - void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&) override; + void BuildUpdate(UpdateDataMapType& data_map) override; void AddToObjectUpdate() override; void RemoveFromObjectUpdate() override; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index c87395169..91889565f 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1050,7 +1050,8 @@ void MovementInfo::OutDebug() WorldObject::WorldObject(bool isWorldObject) : WorldLocation(), LastUsedScriptID(0), m_name(""), m_isActive(false), m_visibilityDistanceOverride(), m_isWorldObject(isWorldObject), m_zoneScript(nullptr), _zoneId(0), _areaId(0), _floorZ(INVALID_HEIGHT), _outdoors(false), _liquidData(), _updatePositionData(false), m_transport(nullptr), - m_currMap(nullptr), _heartbeatTimer(HEARTBEAT_INTERVAL), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), m_useCombinedPhases(true), m_notifyflags(0), m_executed_notifies(0) + m_currMap(nullptr), _heartbeatTimer(HEARTBEAT_INTERVAL), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), m_useCombinedPhases(true), + m_notifyflags(0), m_executed_notifies(0), _objectVisibilityContainer(this) { m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST); m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE); @@ -1177,7 +1178,9 @@ void WorldObject::RemoveFromWorld() if (!IsInWorld()) return; - DestroyForNearbyPlayers(); + DestroyForVisiblePlayers(); + + GetObjectVisibilityContainer().CleanVisibilityReferences(); Object::RemoveFromWorld(); } @@ -2073,19 +2076,19 @@ void Unit::BuildHeartBeatMsg(WorldPacket* data) const void WorldObject::SendMessageToSet(WorldPacket const* data, bool self) const { if (IsInWorld()) - SendMessageToSetInRange(data, GetVisibilityRange(), self); + SendMessageToSetInRange(data, 0.0f, self); } void WorldObject::SendMessageToSetInRange(WorldPacket const* data, float dist, bool /*self*/) const { Acore::MessageDistDeliverer notifier(this, data, dist); - Cell::VisitWorldObjects(this, notifier, dist); + notifier.Visit(GetObjectVisibilityContainer().GetVisiblePlayersMap()); } void WorldObject::SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const { - Acore::MessageDistDeliverer notifier(this, data, GetVisibilityRange(), false, skipped_rcvr); - Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); + Acore::MessageDistDeliverer notifier(this, data, 0.0f, false, skipped_rcvr); + notifier.Visit(GetObjectVisibilityContainer().GetVisiblePlayersMap()); } void WorldObject::SendObjectDeSpawnAnim(ObjectGuid guid) @@ -2906,72 +2909,49 @@ void WorldObject::PlayDirectSound(uint32 sound_id, Player* target /*= nullptr*/) void WorldObject::PlayRadiusSound(uint32 sound_id, float radius) { - std::list targets; + std::vector targets; Acore::AnyPlayerInObjectRangeCheck check(this, radius, false); Acore::PlayerListSearcher searcher(this, targets, check); Cell::VisitWorldObjects(this, searcher, radius); for (Player* player : targets) - { - if (player) - { - player->SendDirectMessage(WorldPackets::Misc::Playsound(sound_id).Write()); - } - } + player->SendDirectMessage(WorldPackets::Misc::Playsound(sound_id).Write()); } void WorldObject::PlayDirectMusic(uint32 music_id, Player* target /*= nullptr*/) { if (target) - { target->SendDirectMessage(WorldPackets::Misc::PlayMusic(music_id).Write()); - } else - { SendMessageToSet(WorldPackets::Misc::PlayMusic(music_id).Write(), true); - } } void WorldObject::PlayRadiusMusic(uint32 music_id, float radius) { - std::list targets; + std::vector targets; Acore::AnyPlayerInObjectRangeCheck check(this, radius, false); Acore::PlayerListSearcher searcher(this, targets, check); Cell::VisitWorldObjects(this, searcher, radius); for (Player* player : targets) - { - if (player) - { - player->SendDirectMessage(WorldPackets::Misc::PlayMusic(music_id).Write()); - } - } + player->SendDirectMessage(WorldPackets::Misc::PlayMusic(music_id).Write()); } -void WorldObject::DestroyForNearbyPlayers() +// Removes us from visibility for all players who are currently able to see us +void WorldObject::DestroyForVisiblePlayers() { if (!IsInWorld()) return; - std::list targets; - Acore::AnyPlayerInObjectRangeCheck check(this, GetVisibilityRange() + VISIBILITY_COMPENSATION, false); - Acore::PlayerListSearcherWithSharedVision searcher(this, targets, check); - Cell::VisitWorldObjects(this, searcher, GetVisibilityRange()); - for (std::list::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) + VisiblePlayersMap& visiblePlayerMap = GetObjectVisibilityContainer().GetVisiblePlayersMap(); + for (VisiblePlayersMap::iterator itr = visiblePlayerMap.begin(); itr != visiblePlayerMap.end();) { - Player* player = (*iter); - - if (player == this) - continue; - - if (!player->HaveAtClient(this)) - continue; - - if (IsUnit() && ((Unit*)this)->GetCharmerGUID() == player->GetGUID()) /// @todo: this is for puppet - continue; + Player* player = itr->second; DestroyForPlayer(player); - player->m_clientGUIDs.erase(GetGUID()); + + // Clean up visibility references now + itr = GetObjectVisibilityContainer().UnlinkVisibilityFromWorldObject(player, itr); } } @@ -3008,84 +2988,17 @@ void WorldObject::AddToNotify(uint16 f) } } -struct WorldObjectChangeAccumulator +void WorldObject::BuildUpdate(UpdateDataMapType& data_map) { - UpdateDataMapType& i_updateDatas; - UpdatePlayerSet& i_playerSet; - WorldObject& i_object; - WorldObjectChangeAccumulator(WorldObject& obj, UpdateDataMapType& d, UpdatePlayerSet& p) : i_updateDatas(d), i_playerSet(p), i_object(obj) + // Build update for self + if (IsPlayer()) + BuildFieldsUpdate(ToPlayer(), data_map); + + // Build update for visible players + DoForAllVisiblePlayers([this, &data_map](Player* player) { - i_playerSet.clear(); - } - void Visit(PlayerMapType& m) - { - Player* source = nullptr; - for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter) - { - source = iter->GetSource(); - - BuildPacket(source); - - if (source->HasSharedVision()) - { - SharedVisionList::const_iterator it = source->GetSharedVisionList().begin(); - for (; it != source->GetSharedVisionList().end(); ++it) - BuildPacket(*it); - } - } - } - - void Visit(CreatureMapType& m) - { - Creature* source = nullptr; - for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter) - { - source = iter->GetSource(); - if (source->HasSharedVision()) - { - SharedVisionList::const_iterator it = source->GetSharedVisionList().begin(); - for (; it != source->GetSharedVisionList().end(); ++it) - BuildPacket(*it); - } - } - } - - void Visit(DynamicObjectMapType& m) - { - DynamicObject* source = nullptr; - for (DynamicObjectMapType::iterator iter = m.begin(); iter != m.end(); ++iter) - { - source = iter->GetSource(); - ObjectGuid guid = source->GetCasterGUID(); - - if (guid) - { - //Caster may be nullptr if DynObj is in removelist - if (Player* caster = ObjectAccessor::FindPlayer(guid)) - if (caster->GetGuidValue(PLAYER_FARSIGHT) == source->GetGUID()) - BuildPacket(caster); - } - } - } - - void BuildPacket(Player* player) - { - // Only send update once to a player - if (i_playerSet.find(player->GetGUID()) == i_playerSet.end() && player->HaveAtClient(&i_object)) - { - i_object.BuildFieldsUpdate(player, i_updateDatas); - i_playerSet.insert(player->GetGUID()); - } - } - - template void Visit(GridRefMgr&) {} -}; - -void WorldObject::BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet& player_set) -{ - WorldObjectChangeAccumulator notifier(*this, data_map, player_set); - //we must build packets for all visible players - Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); + BuildFieldsUpdate(player, data_map); + }); ClearUpdateMask(false); } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index c432d8575..3441a50a2 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -33,6 +33,7 @@ #include "Position.h" #include "UpdateData.h" #include "UpdateMask.h" +#include "ObjectVisibilityContainer.h" #include #include #include @@ -96,7 +97,6 @@ class MotionTransport; struct PositionFullTerrainStatus; typedef std::unordered_map UpdateDataMapType; -typedef GuidUnorderedSet UpdatePlayerSet; static constexpr Milliseconds HEARTBEAT_INTERVAL = 5s + 200ms; @@ -189,7 +189,7 @@ public: [[nodiscard]] virtual bool hasQuest(uint32 /* quest_id */) const { return false; } [[nodiscard]] virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; } - virtual void BuildUpdate(UpdateDataMapType&, UpdatePlayerSet&) {} + virtual void BuildUpdate(UpdateDataMapType&) {} void BuildFieldsUpdate(Player*, UpdateDataMapType&); void SetFieldNotifyFlag(uint16 flag) { _fieldNotifyFlags |= flag; } @@ -559,6 +559,29 @@ public: void PlayDirectMusic(uint32 music_id, Player* target = nullptr); void PlayRadiusMusic(uint32 music_id, float radius); + // Warning: Possible iterator invalidation in uses that may modify visibility map + template + void DoForAllVisiblePlayers(Worker&& worker) + { + for (auto const& kvPair : GetObjectVisibilityContainer().GetVisiblePlayersMap()) + worker(kvPair.second); + } + + // Warning: Possible iterator invalidation in uses that may modify visibility map + template + void DoForAllVisibleWorldObjects(Worker&& worker) + { + // Not a player, no access to this map + VisibleWorldObjectsMap const* visibleWorldObjectsMap = GetObjectVisibilityContainer().GetVisibleWorldObjectsMap(); + if (!visibleWorldObjectsMap) + return; + + for (auto const& kvPair : *visibleWorldObjectsMap) + worker(kvPair.second); + } + + void DestroyForVisiblePlayers(); + void SendObjectDeSpawnAnim(ObjectGuid guid); virtual void SaveRespawnTime() {} @@ -609,10 +632,9 @@ public: void GetCreatureListWithEntryInGrid(std::list& creatureList, std::vector const& entries, float maxSearchRange) const; void GetDeadCreatureListInGrid(std::list& lList, float maxSearchRange, bool alive = false) const; - void DestroyForNearbyPlayers(); virtual void UpdateObjectVisibility(bool forced = true, bool fromUpdate = false); virtual void UpdateObjectVisibilityOnCreate() { UpdateObjectVisibility(true); } - void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet& player_set) override; + void BuildUpdate(UpdateDataMapType& data_map) override; void GetCreaturesWithEntryInRange(std::list& creatureList, float radius, uint32 entry); void SetPositionDataUpdate(); @@ -688,6 +710,9 @@ public: std::string GetDebugInfo() const override; + ObjectVisibilityContainer& GetObjectVisibilityContainer() { return _objectVisibilityContainer; } + ObjectVisibilityContainer const& GetObjectVisibilityContainer() const { return _objectVisibilityContainer; } + // Event handler ElunaEventProcessor* elunaEvents; EventProcessor m_Events; @@ -745,6 +770,8 @@ private: bool CanDetectStealthOf(WorldObject const* obj, bool checkAlert = false) const; GuidUnorderedSet _allowedLooters; + + ObjectVisibilityContainer _objectVisibilityContainer; }; namespace Acore diff --git a/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp b/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp new file mode 100644 index 000000000..9831adf5a --- /dev/null +++ b/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp @@ -0,0 +1,114 @@ +/* + * 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 "ObjectVisibilityContainer.h" +#include "Object.h" +#include "Player.h" + +/* +* Some implementation notes: +* Non-player worldobjects do not have any concept of 'visibility', thus, +* the most important and mainly used map is 'VisibleWorldObjectsMap' +* which is only accessible for player objects. The 'VisiblePlayersMap' +* map is simply for managing the references so we can use direct pointers. +*/ + +ObjectVisibilityContainer::ObjectVisibilityContainer(WorldObject* selfObject) : + _selfObject(selfObject) +{ +} + +ObjectVisibilityContainer::~ObjectVisibilityContainer() +{ + ASSERT(_visiblePlayersMap.empty()); + if (_visibleWorldObjectsMap) + ASSERT((*_visibleWorldObjectsMap).empty()); +} + +void ObjectVisibilityContainer::InitForPlayer() +{ + _visibleWorldObjectsMap = std::make_unique(); +} + +void ObjectVisibilityContainer::CleanVisibilityReferences() +{ + for (auto const& kvPair : _visiblePlayersMap) + kvPair.second->GetObjectVisibilityContainer().DirectRemoveVisibilityReference(_selfObject->GetGUID()); + + if (_visibleWorldObjectsMap) + { + for (auto const& kvPair : *_visibleWorldObjectsMap) + kvPair.second->GetObjectVisibilityContainer().DirectRemoveVisiblePlayerReference(_selfObject->GetGUID()); + + (*_visibleWorldObjectsMap).clear(); + } + + _visiblePlayersMap.clear(); +} + +void ObjectVisibilityContainer::LinkWorldObjectVisibility(WorldObject* worldObject) +{ + // Do not link self + if (worldObject == _selfObject) + return; + + // Only players can link visibility + if (!_visibleWorldObjectsMap) + return; + + (*_visibleWorldObjectsMap).insert(std::make_pair(worldObject->GetGUID(), worldObject)); + worldObject->GetObjectVisibilityContainer().DirectInsertVisiblePlayerReference(_selfObject->ToPlayer()); +} + +void ObjectVisibilityContainer::UnlinkWorldObjectVisibility(WorldObject* worldObject) +{ + // Only players can unlink visibility + if (!_visibleWorldObjectsMap) + return; + + worldObject->GetObjectVisibilityContainer().DirectRemoveVisiblePlayerReference(_selfObject->GetGUID()); + (*_visibleWorldObjectsMap).erase(worldObject->GetGUID()); +} + +VisibleWorldObjectsMap::iterator ObjectVisibilityContainer::UnlinkVisibilityFromPlayer(WorldObject* worldObject, VisibleWorldObjectsMap::iterator itr) +{ + ASSERT(_visibleWorldObjectsMap); // Ensure we aren't for some reason calling this as a non-player object + worldObject->GetObjectVisibilityContainer().DirectRemoveVisiblePlayerReference(_selfObject->GetGUID()); + return (*_visibleWorldObjectsMap).erase(itr); +} + +VisiblePlayersMap::iterator ObjectVisibilityContainer::UnlinkVisibilityFromWorldObject(Player* player, VisiblePlayersMap::iterator itr) +{ + player->GetObjectVisibilityContainer().DirectRemoveVisibilityReference(_selfObject->GetGUID()); + return _visiblePlayersMap.erase(itr); +} + +void ObjectVisibilityContainer::DirectRemoveVisibilityReference(ObjectGuid guid) +{ + ASSERT(_visibleWorldObjectsMap); + (*_visibleWorldObjectsMap).erase(guid); +} + +void ObjectVisibilityContainer::DirectInsertVisiblePlayerReference(Player* player) +{ + _visiblePlayersMap.insert(std::make_pair(player->GetGUID(), player)); +} + +void ObjectVisibilityContainer::DirectRemoveVisiblePlayerReference(ObjectGuid guid) +{ + _visiblePlayersMap.erase(guid); +} diff --git a/src/server/game/Entities/Object/ObjectVisibilityContainer.h b/src/server/game/Entities/Object/ObjectVisibilityContainer.h new file mode 100644 index 000000000..07444c635 --- /dev/null +++ b/src/server/game/Entities/Object/ObjectVisibilityContainer.h @@ -0,0 +1,101 @@ +/* + * 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 _OBJECTVISIBILITYCONTAINER_H +#define _OBJECTVISIBILITYCONTAINER_H + +#include "Common.h" +#include "ObjectGuid.h" +#include +#include + +class Player; +class WorldObject; + +typedef std::unordered_map VisibleWorldObjectsMap; +typedef std::unordered_map VisiblePlayersMap; + +// Class that manages the visibility containers of a worldobject +class ObjectVisibilityContainer +{ +public: + ObjectVisibilityContainer(WorldObject* selfObject); + ~ObjectVisibilityContainer(); + + // Creates the _visibleWorldObjectsMap map if we are a player + void InitForPlayer(); + + // Cleans up all visibility references from other worldobjects, + // this is used before a worldobject is deleted to prevent any dangling references + void CleanVisibilityReferences(); + + void LinkWorldObjectVisibility(WorldObject* worldObject); + void UnlinkWorldObjectVisibility(WorldObject* worldObject); + + // These helpers aren't ideal, but needed in a few spots for cleaning up references + VisibleWorldObjectsMap::iterator UnlinkVisibilityFromPlayer(WorldObject* worldObject, VisibleWorldObjectsMap::iterator itr); + VisiblePlayersMap::iterator UnlinkVisibilityFromWorldObject(Player* player, VisiblePlayersMap::iterator itr); + + // Returns a list of all players who can see us + VisiblePlayersMap& GetVisiblePlayersMap() { return _visiblePlayersMap; } + VisiblePlayersMap const& GetVisiblePlayersMap() const { return _visiblePlayersMap; } + + // Returns a list of all worldobjects who we can see + // Warning: This is for player objects only, all other objects will return a nullptr + VisibleWorldObjectsMap* GetVisibleWorldObjectsMap() + { + if (!_visibleWorldObjectsMap) + return nullptr; + + return _visibleWorldObjectsMap.get(); + } + + VisibleWorldObjectsMap const* GetVisibleWorldObjectsMap() const + { + if (!_visibleWorldObjectsMap) + return nullptr; + + return _visibleWorldObjectsMap.get(); + } + +private: + + // Directly removes visibility reference. This is to be ONLY used as + // a more efficient method for cleaning up visibility references. + // Warning: Improper use will leave dangling references and result in crashes. + void DirectRemoveVisibilityReference(ObjectGuid guid); + + // Directly inserts player visibility reference. + // Warning: Improper use will leave dangling references and result in crashes. + void DirectInsertVisiblePlayerReference(Player* player); + + // Directly removes player visibility reference. + // Warning: Improper use will leave dangling references and result in crashes. + void DirectRemoveVisiblePlayerReference(ObjectGuid guid); + + WorldObject* _selfObject; + + // List of all worldobjects that are visible to us (including other players) + // Only players contain this map, thus we will only allocate it as needed. + std::unique_ptr _visibleWorldObjectsMap; + + // List of players who are currently able to see this worldobject. + // All worldobjects will contain this map + VisiblePlayersMap _visiblePlayersMap; +}; + +#endif diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 25c2f7271..b2be1b4b8 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -412,6 +412,9 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this) m_isInstantFlightOn = true; _wasOutdoor = true; + + GetObjectVisibilityContainer().InitForPlayer(); + sScriptMgr->OnConstructPlayer(this); } @@ -5707,20 +5710,7 @@ void Player::SendMessageToSetInRange(WorldPacket const* data, float dist, bool s SendDirectMessage(data); Acore::MessageDistDeliverer notifier(this, data, dist); - Cell::VisitWorldObjects(this, notifier, dist); -} - -void Player::SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool includeMargin, bool ownTeamOnly, bool required3dDist) const -{ - if (self) - SendDirectMessage(data); - - dist += GetObjectSize(); - if (includeMargin) - dist += VISIBILITY_COMPENSATION; // pussywizard: to ensure everyone receives all important packets - - Acore::MessageDistDeliverer notifier(this, data, dist, ownTeamOnly, nullptr, required3dDist); - Cell::VisitWorldObjects(this, notifier, dist); + notifier.Visit(GetObjectVisibilityContainer().GetVisiblePlayersMap()); } void Player::SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const @@ -5728,8 +5718,8 @@ void Player::SendMessageToSet(WorldPacket const* data, Player const* skipped_rcv if (skipped_rcvr != this) SendDirectMessage(data); - Acore::MessageDistDeliverer notifier(this, data, GetVisibilityRange(), false, skipped_rcvr); - Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); + Acore::MessageDistDeliverer notifier(this, data, 0.0f, false, skipped_rcvr); + notifier.Visit(GetObjectVisibilityContainer().GetVisiblePlayersMap()); } void Player::SendDirectMessage(WorldPacket const* data) const @@ -7711,23 +7701,26 @@ bool Player::CheckAmmoCompatibility(ItemTemplate const* ammo_proto) const void Player::SendQuestGiverStatusMultiple() { + if (GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->empty()) + return; + uint32 count = 0; WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4); data << uint32(count); // placeholder - for (GuidUnorderedSet::const_iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) + DoForAllVisibleWorldObjects([this, &data, &count](WorldObject* worldObject) { uint32 questStatus = DIALOG_STATUS_NONE; - if ((*itr).IsAnyTypeCreature()) + if (worldObject->IsCreature()) { // need also pet quests case support - Creature* questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr); + Creature* questgiver = worldObject->ToCreature(); if (!questgiver || questgiver->IsHostileTo(this)) - continue; + return; if (!questgiver->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER)) - continue; + return; questStatus = GetQuestDialogStatus(questgiver); @@ -7735,11 +7728,11 @@ void Player::SendQuestGiverStatusMultiple() data << uint8(questStatus); ++count; } - else if ((*itr).IsGameObject()) + else if (worldObject->IsGameObject()) { - GameObject* questgiver = GetMap()->GetGameObject(*itr); + GameObject* questgiver = worldObject->ToGameObject(); if (!questgiver || questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER) - continue; + return; questStatus = GetQuestDialogStatus(questgiver); @@ -7747,7 +7740,7 @@ void Player::SendQuestGiverStatusMultiple() data << uint8(questStatus); ++count; } - } + }); data.put(0, count); // write real count GetSession()->SendPacket(&data); @@ -9383,7 +9376,12 @@ void Player::Say(std::string_view text, Language language, WorldObject const* /* WorldPacket data; ChatHandler::BuildChatPacket(data, CHAT_MSG_SAY, language, this, this, _text); - SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true, false, false, true); + + SendDirectMessage(&data); + + // Special handling for messages, do not use visibility map for stealthed units + Acore::MessageDistDeliverer notifier(this, &data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), false, nullptr, true); + Cell::VisitWorldObjects(this, notifier, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY)); } void Player::Say(uint32 textId, WorldObject const* target /*= nullptr*/) @@ -9404,7 +9402,12 @@ void Player::Yell(std::string_view text, Language language, WorldObject const* / WorldPacket data; ChatHandler::BuildChatPacket(data, CHAT_MSG_YELL, language, this, this, _text); - SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true, false, false, true); + + SendDirectMessage(&data); + + // Special handling for messages, do not use visibility map for stealthed units + Acore::MessageDistDeliverer notifier(this, &data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), false, nullptr, true); + Cell::VisitWorldObjects(this, notifier, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL)); } void Player::Yell(uint32 textId, WorldObject const* target /*= nullptr*/) @@ -9426,7 +9429,11 @@ void Player::TextEmote(std::string_view text, WorldObject const* /*= nullptr*/, WorldPacket data; ChatHandler::BuildChatPacket(data, CHAT_MSG_EMOTE, LANG_UNIVERSAL, this, this, _text); - SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, false, !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE), true); + SendDirectMessage(&data); + + // Special handling for messages, do not use visibility map for stealthed units + Acore::MessageDistDeliverer notifier(this, &data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE), nullptr, true); + Cell::VisitWorldObjects(this, notifier, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE)); } void Player::TextEmote(uint32 textId, WorldObject const* target /*= nullptr*/, bool /*isBossEmote = false*/) @@ -10487,35 +10494,24 @@ void Player::ContinueTaxiFlight() void Player::SendTaxiNodeStatusMultiple() { - for (auto itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) + DoForAllVisibleWorldObjects([this](WorldObject* worldObject) { - if (!itr->IsCreature()) - { - continue; - } - - Creature* creature = ObjectAccessor::GetCreature(*this, *itr); + Creature* creature = worldObject->ToCreature(); if (!creature || creature->IsHostileTo(this)) - { - continue; - } + return; if (!creature->HasNpcFlag(UNIT_NPC_FLAG_FLIGHTMASTER)) - { - continue; - } + return; uint32 nearestNode = sObjectMgr->GetNearestTaxiNode(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetMapId(), GetTeamId()); if (!nearestNode) - { - continue; - } + return; WorldPacket data(SMSG_TAXINODE_STATUS, 9); - data << *itr; + data << creature->GetGUID(); data << uint8(m_taxi.IsTaximaskNodeKnown(nearestNode) ? 1 : 0); SendDirectMessage(&data); - } + }); } void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) @@ -11393,31 +11389,22 @@ WorldLocation Player::GetStartPosition() const bool Player::HaveAtClient(WorldObject const* u) const { - if (u == this) - { - return true; - } - // Motion Transports are always present in player's client if (GameObject const* gameobject = u->ToGameObject()) { if (gameobject->IsMotionTransport()) - { return true; - } } - return m_clientGUIDs.find(u->GetGUID()) != m_clientGUIDs.end(); + return HaveAtClient(u->GetGUID()); } bool Player::HaveAtClient(ObjectGuid guid) const { if (guid == GetGUID()) - { return true; - } - return m_clientGUIDs.find(guid) != m_clientGUIDs.end(); + return GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->find(guid) != GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->end(); } bool Player::IsNeverVisible() const diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index abf5e1958..adc4f04c0 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2043,7 +2043,6 @@ public: void SendMessageToSet(WorldPacket const* data, bool self) const override; void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) const override; - void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool includeMargin, bool ownTeamOnly, bool required3dDist = false) const; void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const override; void SendTeleportAckPacket(); @@ -2397,7 +2396,6 @@ public: void SetEntryPoint(); // currently visible objects at player client - GuidUnorderedSet m_clientGUIDs; std::vector m_newVisible; // pussywizard [[nodiscard]] bool HaveAtClient(WorldObject const* u) const; diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 5291256a7..5dba993a7 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -1635,35 +1635,35 @@ void Player::UpdateObjectVisibility(bool forced, bool fromUpdate) } template -inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, T* target, +inline void UpdateVisibilityOf_helper(Player* player, T* target, std::vector& /*v*/) { - s64.insert(target->GetGUID()); + player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target); } template <> -inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, GameObject* target, +inline void UpdateVisibilityOf_helper(Player* player, GameObject* target, std::vector& /*v*/) { // @HACK: This is to prevent objects like deeprun tram from disappearing // when player moves far from its spawn point while riding it if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT)) - s64.insert(target->GetGUID()); + player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target); } template <> -inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Creature* target, +inline void UpdateVisibilityOf_helper(Player* player, Creature* target, std::vector& v) { - s64.insert(target->GetGUID()); + player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target); v.push_back(target); } template <> -inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Player* target, +inline void UpdateVisibilityOf_helper(Player* player, Player* target, std::vector& v) { - s64.insert(target->GetGUID()); + player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target); v.push_back(target); } @@ -1692,7 +1692,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, BeforeVisibilityDestroy(target, this); target->BuildOutOfRangeUpdateBlock(&data); - m_clientGUIDs.erase(target->GetGUID()); + GetObjectVisibilityContainer().UnlinkWorldObjectVisibility(target); } } else @@ -1700,7 +1700,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, if (CanSeeOrDetect(target, false, true)) { target->BuildCreateUpdateBlockForPlayer(&data, this); - UpdateVisibilityOf_helper(m_clientGUIDs, target, visibleNow); + UpdateVisibilityOf_helper(this, target, visibleNow); } } } @@ -1726,7 +1726,7 @@ void Player::UpdateVisibilityOf(WorldObject* target) BeforeVisibilityDestroy(target->ToCreature(), this); target->DestroyForPlayer(this); - m_clientGUIDs.erase(target->GetGUID()); + GetObjectVisibilityContainer().UnlinkWorldObjectVisibility(target); } } else @@ -1734,7 +1734,7 @@ void Player::UpdateVisibilityOf(WorldObject* target) if (CanSeeOrDetect(target, false, true)) { target->SendUpdateToPlayer(this); - m_clientGUIDs.insert(target->GetGUID()); + GetObjectVisibilityContainer().LinkWorldObjectVisibility(target); // target aura duration for caster show only if target exist at // caster client send data at target visibility change (adding to @@ -1747,69 +1747,67 @@ void Player::UpdateVisibilityOf(WorldObject* target) void Player::UpdateTriggerVisibility() { - if (m_clientGUIDs.empty()) - return; - if (!IsInWorld()) return; - UpdateData udata; - WorldPacket packet; - for (GuidUnorderedSet::iterator itr = m_clientGUIDs.begin(); - itr != m_clientGUIDs.end(); ++itr) + if (GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->empty()) + return; + + UpdateData udata; + DoForAllVisibleWorldObjects([this, &udata](WorldObject* worldObject) { - if ((*itr).IsCreatureOrVehicle()) + if (worldObject->IsCreature()) { - Creature* creature = GetMap()->GetCreature(*itr); + Creature* creature = worldObject->ToCreature(); // Update fields of triggers, transformed units or unselectable // units (values dependent on GM state) if (!creature || (!creature->IsTrigger() && - !creature->HasTransformAura() && - !creature->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))) - continue; + !creature->HasTransformAura() && + !creature->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))) + return; creature->SetFieldNotifyFlag(UF_FLAG_PUBLIC); creature->BuildValuesUpdateBlockForPlayer(&udata, this); creature->RemoveFieldNotifyFlag(UF_FLAG_PUBLIC); } - else if ((*itr).IsGameObject()) + else if (worldObject->IsGameObject()) { - GameObject* go = GetMap()->GetGameObject(*itr); + GameObject* go = worldObject->ToGameObject(); if (!go) - continue; + return; go->SetFieldNotifyFlag(UF_FLAG_PUBLIC); go->BuildValuesUpdateBlockForPlayer(&udata, this); go->RemoveFieldNotifyFlag(UF_FLAG_PUBLIC); } - } + }); if (!udata.HasData()) return; + WorldPacket packet; udata.BuildPacket(packet); GetSession()->SendPacket(&packet); } void Player::UpdateForQuestWorldObjects() { - if (m_clientGUIDs.empty()) + if (GetObjectVisibilityContainer().GetVisibleWorldObjectsMap()->empty()) return; - UpdateData udata; - WorldPacket packet; - for (GuidUnorderedSet::iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) + UpdateData udata; + DoForAllVisibleWorldObjects([this, &udata](WorldObject* worldObject) { - if ((*itr).IsGameObject()) + if (worldObject->IsGameObject()) { - if (GameObject* obj = ObjectAccessor::GetGameObject(*this, *itr)) + if (GameObject* obj = worldObject->ToGameObject()) obj->BuildValuesUpdateBlockForPlayer(&udata, this); } - else if ((*itr).IsCreatureOrVehicle()) + else if (worldObject->IsCreature()) { - Creature* obj = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr); + Creature* obj = worldObject->ToCreature(); if (!obj) - continue; + return; // check if this unit requires quest specific flags if (obj->HasNpcFlag(UNIT_NPC_FLAG_SPELLCLICK)) @@ -1833,12 +1831,14 @@ void Player::UpdateForQuestWorldObjects() } } else if (obj->HasNpcFlag(UNIT_NPC_FLAG_VENDOR_MASK | UNIT_NPC_FLAG_TRAINER)) - { obj->BuildValuesUpdateBlockForPlayer(&udata, this); - } } - } + }); + if (!udata.HasData()) + return; + + WorldPacket packet; udata.BuildPacket(packet); GetSession()->SendPacket(&packet); } diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 1ab462f70..251cdb936 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -123,7 +123,7 @@ void MotionTransport::CleanupsBeforeDelete(bool finalCleanup /*= true*/) GameObject::CleanupsBeforeDelete(finalCleanup); } -void MotionTransport::BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&) +void MotionTransport::BuildUpdate(UpdateDataMapType& data_map) { Map::PlayerList const& players = GetMap()->GetPlayers(); if (players.IsEmpty()) @@ -812,7 +812,7 @@ void StaticTransport::CleanupsBeforeDelete(bool finalCleanup /*= true*/) GameObject::CleanupsBeforeDelete(finalCleanup); } -void StaticTransport::BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&) +void StaticTransport::BuildUpdate(UpdateDataMapType& data_map) { Map::PlayerList const& players = GetMap()->GetPlayers(); if (players.IsEmpty()) diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 2387b3b98..005e9d789 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -54,7 +54,7 @@ public: bool CreateMoTrans(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress); void CleanupsBeforeDelete(bool finalCleanup = true) override; - void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&) override; + void BuildUpdate(UpdateDataMapType& data_map) override; void Update(uint32 diff) override; void DelayedUpdate(uint32 diff); @@ -117,7 +117,7 @@ public: bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0) override; void CleanupsBeforeDelete(bool finalCleanup = true) override; - void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet&) override; + void BuildUpdate(UpdateDataMapType& data_map) override; void Update(uint32 diff) override; void RelocateToProgress(uint32 progress); diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 67259d8ab..563634929 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -32,7 +32,6 @@ void VisibleNotifier::Visit(GameObjectMapType& m) if (i_largeOnly != go->IsVisibilityOverridden()) continue; - vis_guids.erase(go->GetGUID()); i_player.UpdateVisibilityOf(go, i_data, i_visibleNow); } } @@ -42,56 +41,66 @@ void VisibleNotifier::SendToSelf() // at this moment i_clientGUIDs have guids that not iterate at grid level checks // but exist one case when this possible and object not out of range: transports 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()) + switch ((*itr)->GetTypeId()) { - vis_guids.erase((*itr)->GetGUID()); + case TYPEID_GAMEOBJECT: + i_player.UpdateVisibilityOf((*itr)->ToGameObject(), i_data, i_visibleNow); + break; + case TYPEID_PLAYER: + i_player.UpdateVisibilityOf((*itr)->ToPlayer(), i_data, i_visibleNow); + (*itr)->ToPlayer()->UpdateVisibilityOf(&i_player); + break; + case TYPEID_UNIT: + i_player.UpdateVisibilityOf((*itr)->ToCreature(), i_data, i_visibleNow); + break; + default: + break; + } + } + } - switch ((*itr)->GetTypeId()) + VisibleWorldObjectsMap* visibleWorldObjects = i_player.GetObjectVisibilityContainer().GetVisibleWorldObjectsMap(); + for (VisibleWorldObjectsMap::iterator itr = visibleWorldObjects->begin(); itr != visibleWorldObjects->end();) + { + WorldObject* obj = itr->second; + if (i_largeOnly != obj->IsVisibilityOverridden()) + { + ++itr; + continue; + } + + // pussywizard: static transports are removed only in RemovePlayerFromMap and here if can no longer detect (eg. phase changed) + if (itr->first.IsTransport()) + { + if (GameObject* staticTrans = obj->ToGameObject()) + { + if (i_player.CanSeeOrDetect(staticTrans, false, true)) { - case TYPEID_GAMEOBJECT: - i_player.UpdateVisibilityOf((*itr)->ToGameObject(), i_data, i_visibleNow); - break; - case TYPEID_PLAYER: - i_player.UpdateVisibilityOf((*itr)->ToPlayer(), i_data, i_visibleNow); - (*itr)->ToPlayer()->UpdateVisibilityOf(&i_player); - break; - case TYPEID_UNIT: - i_player.UpdateVisibilityOf((*itr)->ToCreature(), i_data, i_visibleNow); - break; - default: - break; + ++itr; + continue; } } } - for (GuidUnorderedSet::const_iterator it = vis_guids.begin(); it != vis_guids.end(); ++it) - { - if (WorldObject* obj = ObjectAccessor::GetWorldObject(i_player, *it)) + if (i_player.m_seer->IsWithinDist(obj, i_player.GetSightRange(obj), true)) { - if (i_largeOnly != obj->IsVisibilityOverridden()) - continue; + ++itr; + 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)) - if (i_player.CanSeeOrDetect(staticTrans, false, true)) - continue; + i_data.AddOutOfRangeGUID(obj->GetGUID()); - i_player.m_clientGUIDs.erase(*it); - i_data.AddOutOfRangeGUID(*it); + if (Player* objPlayer = obj->ToPlayer()) + objPlayer->UpdateVisibilityOf(&i_player); - if ((*it).IsPlayer()) - { - Player* player = ObjectAccessor::FindPlayer(*it); - if (player && player->IsInMap(&i_player)) - player->UpdateVisibilityOf(&i_player); - } + // Clean up references + itr = i_player.GetObjectVisibilityContainer().UnlinkVisibilityFromPlayer(obj, itr); } if (!i_data.HasData()) @@ -170,7 +179,6 @@ void PlayerRelocationNotifier::Visit(PlayerMapType& m) for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter) { Player* player = iter->GetSource(); - vis_guids.erase(player->GetGUID()); i_player.UpdateVisibilityOf(player, i_data, i_visibleNow); player->UpdateVisibilityOf(&i_player); // this notifier with different Visit(PlayerMapType&) than VisibleNotifier is needed to update visibility of self for other players when we move (eg. stealth detection changes) } @@ -208,6 +216,23 @@ void AIRelocationNotifier::Visit(CreatureMapType& m) } } +// Uses visibility map +void MessageDistDeliverer::Visit(VisiblePlayersMap const& m) +{ + for (auto const& kvPair : m) + { + Player const* target = kvPair.second; + if (i_distSq != 0.0f && target->m_seer->GetExactDist2dSq(i_source) > i_distSq) + continue; + + // @todo: Might not need this check anymore + if (skipped_receiver == target) + continue; + + target->SendDirectMessage(i_message); + } +} + void MessageDistDeliverer::Visit(PlayerMapType& m) { for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter) diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 27ed0d462..040c54244 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -43,14 +43,13 @@ namespace Acore struct VisibleNotifier { Player& i_player; - 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) + i_player(player), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly), i_largeOnly(largeOnly) { i_visibleNow.clear(); } @@ -111,6 +110,7 @@ namespace Acore , skipped_receiver(skipped), required3dDist(req3dDist) { } + void Visit(VisiblePlayersMap const& m); void Visit(PlayerMapType& m); void Visit(CreatureMapType& m); void Visit(DynamicObjectMapType& m); diff --git a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h index cb1c489ae..592e584ea 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h +++ b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h @@ -37,7 +37,6 @@ inline void Acore::VisibleNotifier::Visit(GridRefMgr& m) 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/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 511a5b5d2..f6ee1479d 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1180,7 +1180,6 @@ void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar) pCurrChar->GetMap()->SendInitTransports(pCurrChar); pCurrChar->GetMap()->SendInitSelf(pCurrChar); pCurrChar->GetMap()->SendZoneDynamicInfo(pCurrChar); - pCurrChar->m_clientGUIDs.clear(); pCurrChar->UpdateObjectVisibility(false); pCurrChar->CleanupChannels(); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 4e731fc30..e82fbcaed 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -289,7 +289,6 @@ bool Map::AddPlayerToMap(Player* player) SendInitSelf(player); SendZoneDynamicInfo(player); - player->m_clientGUIDs.clear(); player->UpdateObjectVisibility(false); if (player->IsAlive()) @@ -676,13 +675,9 @@ void Map::RemovePlayerFromMap(Player* player, bool remove) player->UpdateZone(MAP_INVALID_ZONE, 0); player->getHostileRefMgr().deleteReferences(true); // pussywizard: multithreading crashfix - bool inWorld = player->IsInWorld(); player->RemoveFromWorld(); SendRemoveTransports(player); - if (!inWorld) // pussywizard: if was in world, RemoveFromWorld() called DestroyForNearbyPlayers() - player->DestroyForNearbyPlayers(); // pussywizard: previous player->UpdateObjectVisibility(true) - if (player->IsInGrid()) player->RemoveFromGrid(); else @@ -702,15 +697,11 @@ void Map::AfterPlayerUnlinkFromMap() template void Map::RemoveFromMap(T* obj, bool remove) { - bool inWorld = obj->IsInWorld() && obj->GetTypeId() >= TYPEID_UNIT && obj->GetTypeId() <= TYPEID_GAMEOBJECT; obj->RemoveFromWorld(); if (obj->isActiveObject()) RemoveFromActive(obj); - if (!inWorld) // pussywizard: if was in world, RemoveFromWorld() called DestroyForNearbyPlayers() - obj->DestroyForNearbyPlayers(); // pussywizard: previous player->UpdateObjectVisibility() - obj->RemoveFromGrid(); obj->ResetMap(); @@ -1653,6 +1644,9 @@ void Map::SendInitTransports(Player* player) if (*itr != player->GetTransport()) (*itr)->BuildCreateUpdateBlockForPlayer(&transData, player); + if (!transData.HasData()) + return; + WorldPacket packet; transData.BuildPacket(packet); player->GetSession()->SendPacket(&packet); @@ -1667,7 +1661,7 @@ void Map::SendRemoveTransports(Player* player) (*itr)->BuildOutOfRangeUpdateBlock(&transData); // pussywizard: remove static transports from client - for (GuidUnorderedSet::const_iterator it = player->m_clientGUIDs.begin(); it != player->m_clientGUIDs.end(); ) + /*for (GuidUnorderedSet::const_iterator it = player->m_clientGUIDs.begin(); it != player->m_clientGUIDs.end(); ) { if ((*it).IsTransport()) { @@ -1676,7 +1670,10 @@ void Map::SendRemoveTransports(Player* player) } else ++it; - } + }*/ + + if (!transData.HasData()) + return; WorldPacket packet; transData.BuildPacket(packet); @@ -1686,7 +1683,6 @@ void Map::SendRemoveTransports(Player* player) void Map::SendObjectUpdates() { UpdateDataMapType update_players; - UpdatePlayerSet player_set; while (!_updateObjects.empty()) { @@ -1694,7 +1690,7 @@ void Map::SendObjectUpdates() ASSERT(obj->IsInWorld()); _updateObjects.erase(_updateObjects.begin()); - obj->BuildUpdate(update_players, player_set); + obj->BuildUpdate(update_players); } WorldPacket packet; // here we allocate a std::vector with a size of 0x10000 @@ -2686,7 +2682,7 @@ void Map::RemoveCorpse(Corpse* corpse) ASSERT(corpse); GridCoord const gridCoord = Acore::ComputeGridCoord(corpse->GetPositionX(), corpse->GetPositionY()); - corpse->DestroyForNearbyPlayers(); + corpse->DestroyForVisiblePlayers(); if (corpse->IsInGrid()) RemoveFromMap(corpse, false); else diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp index 7743fda48..7dc9c0c59 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp @@ -104,7 +104,7 @@ public: c->UpdatePosition(630.88f, 131.39f, 140.8f, 3.02f, true); c->StopMovingOnCurrentPos(); - c->DestroyForNearbyPlayers(); + c->DestroyForVisiblePlayers(); } } } @@ -792,7 +792,7 @@ public: { icehowl->UpdatePosition(513.19f, 139.48f, 395.22f, 3 * M_PI / 2, true); icehowl->StopMovingOnCurrentPos(); - icehowl->DestroyForNearbyPlayers(); + icehowl->DestroyForVisiblePlayers(); } } break; @@ -959,7 +959,7 @@ public: { jaraxxus->UpdatePosition(613.83f, 139.5f, 395.22f, 3 * M_PI / 2, true); jaraxxus->StopMovingOnCurrentPos(); - jaraxxus->DestroyForNearbyPlayers(); + jaraxxus->DestroyForVisiblePlayers(); } if (Creature* c = instance->GetCreature(NPC_TirionGUID)) diff --git a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp index 06a1823ed..6793cc050 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp @@ -252,7 +252,7 @@ public: me->SummonCreature(summons[0][i], cords[0][0] + ((i % 2) ? 4.0f : -4.0f), cords[0][1] + (i < 2 ? 4.0f : -4.0f), cords[0][2], 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000); uint8 phase = GetPhaseByCurrentPosition(); me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]); - me->DestroyForNearbyPlayers(); + me->DestroyForVisiblePlayers(); LeaveCombat(); me->CastSpell(me, SPELL_EVOCATION, true); releaseLockTimer = 1; @@ -264,7 +264,7 @@ public: me->SummonCreature(summons[1][i], cords[1][0] + ((i % 2) ? 4.0f : -4.0f), cords[1][1] + (i < 2 ? 4.0f : -4.0f), cords[1][2], 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000); uint8 phase = GetPhaseByCurrentPosition(); me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]); - me->DestroyForNearbyPlayers(); + me->DestroyForVisiblePlayers(); LeaveCombat(); me->CastSpell(me, SPELL_EVOCATION, true); releaseLockTimer = 1; @@ -276,7 +276,7 @@ public: me->SummonCreature(summons[2][i], cords[2][0] + ((i % 2) ? 4.0f : -4.0f), cords[2][1] + (i < 2 ? 4.0f : -4.0f), cords[2][2], 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000); uint8 phase = GetPhaseByCurrentPosition(); me->SetHomePosition(cords[phase + 1][0], cords[phase + 1][1], cords[phase + 1][2], cords[phase + 1][3]); - me->DestroyForNearbyPlayers(); + me->DestroyForVisiblePlayers(); LeaveCombat(); me->CastSpell(me, SPELL_EVOCATION, true); releaseLockTimer = 1; diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp index 6d80f9216..868f8000f 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp @@ -366,7 +366,7 @@ struct boss_ingvar_the_plunderer : public ScriptedAI case EVENT_AXE_PICKUP: if (Creature* c = ObjectAccessor::GetCreature(*me, ThrowGUID)) { - c->DestroyForNearbyPlayers(); + c->DestroyForVisiblePlayers(); c->DespawnOrUnsummon(); summons.DespawnAll(); } diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/instance_utgarde_keep.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/instance_utgarde_keep.cpp index 7f9293a66..60ecd789c 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/instance_utgarde_keep.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/instance_utgarde_keep.cpp @@ -229,7 +229,7 @@ public: c->loot.FillLoot(lootid, LootTemplates_Creature, c->GetLootRecipient(), false, false, c->GetLootMode(), c); if (c->GetLootMode()) c->loot.generateMoneyLoot(c->GetCreatureTemplate()->mingold, c->GetCreatureTemplate()->maxgold); - c->DestroyForNearbyPlayers(); + c->DestroyForVisiblePlayers(); c->SetVisible(true); } break; @@ -244,7 +244,7 @@ public: c->loot.FillLoot(lootid, LootTemplates_Creature, c->GetLootRecipient(), false, false, c->GetLootMode(), c); if (c->GetLootMode()) c->loot.generateMoneyLoot(c->GetCreatureTemplate()->mingold, c->GetCreatureTemplate()->maxgold); - c->DestroyForNearbyPlayers(); + c->DestroyForVisiblePlayers(); c->SetVisible(true); } break; diff --git a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp index 1475b9a3e..c7c9a01ad 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp @@ -159,7 +159,7 @@ public: { me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), h, me->GetOrientation(), true); // move to ground me->StopMovingOnCurrentPos(); - me->DestroyForNearbyPlayers(); + me->DestroyForVisiblePlayers(); } } diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp index 0e4bb5552..8c22b8d04 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp @@ -110,10 +110,11 @@ public: { if (Player* target = ObjectAccessor::GetPlayer(_owner, _targetGUID)) { - target->m_clientGUIDs.insert(_owner.GetGUID()); + // @todo: wtf? this is wrong but I cba looking into it. + target->GetObjectVisibilityContainer().LinkWorldObjectVisibility(&_owner); _owner.CastSpell(target, SPELL_ENVENOM, true); target->RemoveAurasDueToSpell(SPELL_DEADLY_POISON); - target->m_clientGUIDs.erase(_owner.GetGUID()); + target->GetObjectVisibilityContainer().UnlinkWorldObjectVisibility(&_owner); } return true; } diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index cabadc942..2ee13378d 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -80,7 +80,7 @@ public: if (_timer > 5000) { me->CastSpell(nullptr, 9056); - me->DestroyForNearbyPlayers(); + me->DestroyForVisiblePlayers(); _timer = 0; } }